diff options
author | gunsch <gunsch@chromium.org> | 2014-09-11 15:03:41 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2014-09-11 22:25:48 +0000 |
commit | 407189f61ab162096ea98c40b2ecd85875bd6049 (patch) | |
tree | 30b112aec44856c8f10e6e52a820cf55dcb2affd /chromecast | |
parent | 149b92db00bc1deab22599e7d3da3fc8be2c0d8b (diff) | |
download | chromium_src-407189f61ab162096ea98c40b2ecd85875bd6049.zip chromium_src-407189f61ab162096ea98c40b2ecd85875bd6049.tar.gz chromium_src-407189f61ab162096ea98c40b2ecd85875bd6049.tar.bz2 |
Chromecast: initial checkin of Android-based cast shell.
R=byungchul@chromium.org,lcwu@chromium.org,yfriedman@chromium.org
BUG=400876
Review URL: https://codereview.chromium.org/490603002
Cr-Commit-Position: refs/heads/master@{#294476}
Diffstat (limited to 'chromecast')
42 files changed, 1864 insertions, 28 deletions
diff --git a/chromecast/DEPS b/chromecast/DEPS index 438dbc0..a10b957 100644 --- a/chromecast/DEPS +++ b/chromecast/DEPS @@ -5,6 +5,7 @@ include_rules = [ "+crypto", "+grit/chromecast_settings.h", "+grit/shell_resources.h", + "+jni", "+net", "+ui", ] diff --git a/chromecast/android/cast_jni_registrar.cc b/chromecast/android/cast_jni_registrar.cc new file mode 100644 index 0000000..db79061 --- /dev/null +++ b/chromecast/android/cast_jni_registrar.cc @@ -0,0 +1,30 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromecast/android/cast_jni_registrar.h" + +#include "base/android/jni_android.h" +#include "base/android/jni_registrar.h" +#include "chromecast/android/chromecast_config_android.h" +#include "chromecast/shell/browser/android/cast_window_android.h" +#include "chromecast/shell/browser/android/cast_window_manager.h" + +namespace chromecast { +namespace android { + +namespace { + +static base::android::RegistrationMethod kMethods[] = { + { "CastWindowAndroid", shell::CastWindowAndroid::RegisterJni }, + { "CastWindowManager", shell::RegisterCastWindowManager }, +}; + +} // namespace + +bool RegisterJni(JNIEnv* env) { + return RegisterNativeMethods(env, kMethods, arraysize(kMethods)); +} + +} // namespace android +} // namespace chromecast diff --git a/chromecast/android/cast_jni_registrar.h b/chromecast/android/cast_jni_registrar.h new file mode 100644 index 0000000..b07316b --- /dev/null +++ b/chromecast/android/cast_jni_registrar.h @@ -0,0 +1,19 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMECAST_ANDROID_CAST_JNI_REGISTRAR_H_ +#define CHROMECAST_ANDROID_CAST_JNI_REGISTRAR_H_ + +#include <jni.h> + +namespace chromecast { +namespace android { + +// Register all JNI bindings necessary for the Android cast shell. +bool RegisterJni(JNIEnv* env); + +} // namespace android +} // namespace chromecast + +#endif // CHROMECAST_ANDROID_CAST_JNI_REGISTRAR_H_ diff --git a/chromecast/android/chromecast_config_android.cc b/chromecast/android/chromecast_config_android.cc new file mode 100644 index 0000000..d698b70 --- /dev/null +++ b/chromecast/android/chromecast_config_android.cc @@ -0,0 +1,33 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromecast/android/chromecast_config_android.h" + +namespace chromecast { +namespace android { + +namespace { +base::LazyInstance<ChromecastConfigAndroid> g_instance = + LAZY_INSTANCE_INITIALIZER; +} // namespace + +// static +ChromecastConfigAndroid* ChromecastConfigAndroid::GetInstance() { + return g_instance.Pointer(); +} + +ChromecastConfigAndroid::ChromecastConfigAndroid() { +} + +ChromecastConfigAndroid::~ChromecastConfigAndroid() { +} + +// Registers a handler to be notified when SendUsageStats is changed. +void ChromecastConfigAndroid::SetSendUsageStatsChangedCallback( + const base::Callback<void(bool)>& callback) { + send_usage_stats_changed_callback_ = callback; +} + +} // namespace android +} // namespace chromecast diff --git a/chromecast/android/chromecast_config_android.h b/chromecast/android/chromecast_config_android.h new file mode 100644 index 0000000..627259e --- /dev/null +++ b/chromecast/android/chromecast_config_android.h @@ -0,0 +1,43 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMECAST_ANDROID_CHROMECAST_CONFIG_ANDROID_H_ +#define CHROMECAST_ANDROID_CHROMECAST_CONFIG_ANDROID_H_ + +#include <jni.h> + +#include "base/callback.h" +#include "base/lazy_instance.h" +#include "base/macros.h" + +namespace chromecast { +namespace android { + +class ChromecastConfigAndroid { + public: + static ChromecastConfigAndroid* GetInstance(); + + // Registers a handler to be notified when SendUsageStats is changed. + void SetSendUsageStatsChangedCallback( + const base::Callback<void(bool)>& callback); + + const base::Callback<void(bool)>& send_usage_stats_changed_callback() const { + return send_usage_stats_changed_callback_; + } + + private: + friend struct base::DefaultLazyInstanceTraits<ChromecastConfigAndroid>; + + ChromecastConfigAndroid(); + ~ChromecastConfigAndroid(); + + base::Callback<void(bool)> send_usage_stats_changed_callback_; + + DISALLOW_COPY_AND_ASSIGN(ChromecastConfigAndroid); +}; + +} // namespace android +} // namespace chromecast + +#endif // CHROMECAST_ANDROID_CHROMECAST_CONFIG_ANDROID_H_ diff --git a/chromecast/android/platform_jni_loader.h b/chromecast/android/platform_jni_loader.h new file mode 100644 index 0000000..7630746 --- /dev/null +++ b/chromecast/android/platform_jni_loader.h @@ -0,0 +1,18 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMECAST_ANDROID_PLATFORM_JNI_LOADER_H_ +#define CHROMECAST_ANDROID_PLATFORM_JNI_LOADER_H_ + +#include <jni.h> + +namespace chromecast { +namespace android { + +bool PlatformRegisterJni(JNIEnv* env); + +} // namespace android +} // namespace chromecast + +#endif // CHROMECAST_ANDROID_PLATFORM_JNI_LOADER_H_ diff --git a/chromecast/android/platform_jni_loader_stub.cc b/chromecast/android/platform_jni_loader_stub.cc new file mode 100644 index 0000000..0f8d814 --- /dev/null +++ b/chromecast/android/platform_jni_loader_stub.cc @@ -0,0 +1,16 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromecast/android/platform_jni_loader.h" + +namespace chromecast { +namespace android { + +bool PlatformRegisterJni(JNIEnv* env) { + // Intentional no-op for public build. + return true; +} + +} // namespace android +} // namespace chromecast diff --git a/chromecast/android/src/dummy b/chromecast/android/src/dummy new file mode 100644 index 0000000..c1069e0 --- /dev/null +++ b/chromecast/android/src/dummy @@ -0,0 +1,2 @@ +Note(gunsch): This file is for the cast_shell_apk target in chromecast.gyp. +See the notes above that target's 'java_in_dir' variable in chromecast.gyp. diff --git a/chromecast/chromecast.gyp b/chromecast/chromecast.gyp index 55561df..ee35203 100644 --- a/chromecast/chromecast.gyp +++ b/chromecast/chromecast.gyp @@ -87,6 +87,16 @@ ], }, # end of target 'cast_metrics_unittests' { + 'target_name': 'cast_net', + 'type': '<(component)', + 'sources': [ + 'net/network_change_notifier_cast.cc', + 'net/network_change_notifier_cast.h', + 'net/network_change_notifier_factory_cast.cc', + 'net/network_change_notifier_factory_cast.h', + ], + }, + { 'target_name': 'cast_service', 'type': '<(component)', 'dependencies': [ @@ -106,9 +116,18 @@ '../base/base.gyp:base', '../content/content.gyp:content', ], - 'sources': [ - 'service/cast_service_simple.cc', - 'service/cast_service_simple.h', + 'conditions': [ + ['OS=="android"', { + 'sources': [ + 'service/cast_service_android.cc', + 'service/cast_service_android.h', + ], + }, { + 'sources': [ + 'service/cast_service_simple.cc', + 'service/cast_service_simple.h', + ], + }], ], }], ], @@ -167,9 +186,11 @@ }, ], }, + # This target contains all content-embedder implementation that is + # non-platform-specific. { - 'target_name': 'cast_shell', - 'type': 'executable', + 'target_name': 'cast_shell_common', + 'type': '<(component)', 'dependencies': [ 'cast_common', 'cast_metrics', @@ -179,19 +200,12 @@ 'cast_version_header', 'chromecast_locales.gyp:chromecast_locales_pak', 'chromecast_locales.gyp:chromecast_settings', - 'media/media.gyp:cast_media', '../components/components.gyp:component_metrics_proto', '../content/content.gyp:content', '../content/content.gyp:content_app_browser', '../skia/skia.gyp:skia', - '../ui/aura/aura.gyp:aura_test_support', ], 'sources': [ - 'net/network_change_notifier_cast.cc', - 'net/network_change_notifier_cast.h', - 'net/network_change_notifier_factory_cast.cc', - 'net/network_change_notifier_factory_cast.h', - 'shell/app/cast_main.cc', 'shell/app/cast_main_delegate.cc', 'shell/app/cast_main_delegate.h', 'shell/browser/cast_browser_context.cc', @@ -221,13 +235,9 @@ 'conditions': [ ['chromecast_branding=="Chrome"', { 'dependencies': [ - 'internal/chromecast_internal.gyp:cast_gfx_internal', 'internal/chromecast_internal.gyp:cast_shell_internal', ], }, { - 'dependencies': [ - '../ui/ozone/ozone.gyp:eglplatform_shim_x11', - ], 'sources': [ 'shell/browser/devtools/remote_debugging_server_simple.cc', 'shell/browser/webui/webui_cast_simple.cc', @@ -275,4 +285,141 @@ ], }, ], # end of targets + + # Targets for Android receiver. + 'conditions': [ + ['OS=="android"', { + 'targets': [ + { + 'target_name': 'libcast_shell_android', + 'type': 'shared_library', + 'dependencies': [ + 'cast_common', + 'cast_jni_headers', + 'cast_shell_common', + 'cast_shell_pak', + 'cast_version_header', + '../base/base.gyp:base', + '../content/content.gyp:content_app_browser', + '../content/content.gyp:content', + '../skia/skia.gyp:skia', + '../ui/gfx/gfx.gyp:gfx', + '../ui/gl/gl.gyp:gl', + ], + 'sources': [ + 'android/cast_jni_registrar.cc', + 'android/cast_jni_registrar.h', + 'android/chromecast_config_android.cc', + 'android/chromecast_config_android.h', + 'android/platform_jni_loader.h', + 'shell/app/android/cast_jni_loader.cc', + 'shell/browser/android/cast_window_manager.cc', + 'shell/browser/android/cast_window_manager.h', + 'shell/browser/android/cast_window_android.cc', + 'shell/browser/android/cast_window_android.h', + ], + 'conditions': [ + ['chromecast_branding=="Chrome"', { + 'dependencies': [ + '<(cast_internal_gyp):cast_shell_android_internal' + ], + }, { + 'sources': [ + 'android/platform_jni_loader_stub.cc', + ], + }] + ], + }, # end of target 'libcast_shell_android' + { + 'target_name': 'cast_shell_java', + 'type': 'none', + 'dependencies': [ + '../base/base.gyp:base_java', + '../content/content.gyp:content_java', + '../media/media.gyp:media_java', + '../net/net.gyp:net_java', + '../third_party/android_tools/android_tools.gyp:android_support_v13_javalib', + '../ui/android/ui_android.gyp:ui_java', + ], + 'variables': { + 'has_java_resources': 1, + 'java_in_dir': 'shell/android/apk', + 'resource_dir': 'shell/android/apk/res', + 'R_package': 'org.chromium.chromecast.shell', + }, + 'includes': ['../build/java.gypi'], + }, # end of target 'cast_shell_java' + { + 'target_name': 'cast_shell_apk', + 'type': 'none', + 'dependencies': [ + 'cast_shell_java', + 'libcast_shell_android', + ], + 'variables': { + 'apk_name': 'CastShell', + 'manifest_package_name': 'org.chromium.chromecast.shell', + # Note(gunsch): there are no Java files in the android/ directory. + # Unfortunately, the java_apk.gypi target rigidly insists on having + # a java_in_dir directory, but complains about duplicate classes + # from the common cast_shell_java target (shared with internal APK) + # if the actual Java path is used. + # This will hopefully be removable after the great GN migration. + 'java_in_dir': 'android', + 'android_manifest_path': 'shell/android/apk/AndroidManifest.xml', + 'package_name': 'org.chromium.chromecast.shell', + 'native_lib_target': 'libcast_shell_android', + 'asset_location': '<(PRODUCT_DIR)/assets', + 'additional_input_paths': ['<(PRODUCT_DIR)/assets/cast_shell.pak'], + }, + 'includes': [ '../build/java_apk.gypi' ], + }, + { + 'target_name': 'cast_jni_headers', + 'type': 'none', + 'sources': [ + 'shell/android/apk/src/org/chromium/chromecast/shell/CastWindowAndroid.java', + 'shell/android/apk/src/org/chromium/chromecast/shell/CastWindowManager.java', + ], + 'direct_dependent_settings': { + 'include_dirs': [ + '<(SHARED_INTERMEDIATE_DIR)/chromecast', + ], + }, + 'variables': { + 'jni_gen_package': 'chromecast', + }, + 'includes': [ '../build/jni_generator.gypi' ], + }, + ], # end of targets + }, { # OS != "android" + 'targets': [ + # This target includes all dependencies that cannot be built on Android. + { + 'target_name': 'cast_shell', + 'type': 'executable', + 'dependencies': [ + 'cast_net', + 'cast_shell_common', + 'media/media.gyp:cast_media', + '../ui/aura/aura.gyp:aura_test_support', + ], + 'sources': [ + 'shell/app/cast_main.cc', + ], + 'conditions': [ + ['chromecast_branding=="Chrome"', { + 'dependencies': [ + 'internal/chromecast_internal.gyp:cast_gfx_internal', + ], + }, { + 'dependencies': [ + '../ui/ozone/ozone.gyp:eglplatform_shim_x11', + ], + }], + ], + }, + ], # end of targets + }], + ], # end of conditions } diff --git a/chromecast/common/cast_paths.cc b/chromecast/common/cast_paths.cc index fb3cdf1..d5d471c 100644 --- a/chromecast/common/cast_paths.cc +++ b/chromecast/common/cast_paths.cc @@ -7,6 +7,7 @@ #include "base/base_paths.h" #include "base/files/file_path.h" #include "base/files/file_util.h" +#include "base/logging.h" #include "base/path_service.h" #include "build/build_config.h" @@ -27,10 +28,18 @@ bool PathProvider(int key, base::FilePath* result) { #endif return true; } +#if defined(OS_ANDROID) + case FILE_CAST_ANDROID_LOG: { + base::FilePath base_dir; + CHECK(PathService::Get(base::DIR_ANDROID_APP_DATA, &base_dir)); + *result = base_dir.AppendASCII("cast_shell.log"); + return true; + } +#endif // defined(OS_ANDROID) case FILE_CAST_CONFIG: { base::FilePath data_dir; #if defined(OS_ANDROID) - CHECK(PathService::Get(base::DIR_ANDROID_APP_DATA, &data_dir); + CHECK(PathService::Get(base::DIR_ANDROID_APP_DATA, &data_dir)); *result = data_dir.Append("cast_shell.conf"); #else CHECK(PathService::Get(DIR_CAST_HOME, &data_dir)); diff --git a/chromecast/common/cast_paths.h b/chromecast/common/cast_paths.h index f228cd1..5ab156c2 100644 --- a/chromecast/common/cast_paths.h +++ b/chromecast/common/cast_paths.h @@ -5,6 +5,8 @@ #ifndef CHROMECAST_COMMON_CAST_PATHS_H_ #define CHROMECAST_COMMON_CAST_PATHS_H_ +#include "build/build_config.h" + // This file declares path keys for the chromecast module. These can be used // with the PathService to access various special directories and files. @@ -16,6 +18,9 @@ enum { DIR_CAST_HOME, // Return a modified $HOME which works for both // development use and the actual device. +#if defined(OS_ANDROID) + FILE_CAST_ANDROID_LOG, // Log file location for Android. +#endif // defined(OS_ANDROID) FILE_CAST_CONFIG, // Config/preferences file path. FILE_CAST_PAK, // cast_shell.pak file path. PATH_END diff --git a/chromecast/common/global_descriptors.h b/chromecast/common/global_descriptors.h new file mode 100644 index 0000000..cbbbb2c --- /dev/null +++ b/chromecast/common/global_descriptors.h @@ -0,0 +1,21 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMECAST_COMMON_GLOBAL_DESCRIPTORS_H_ +#define CHROMECAST_COMMON_GLOBAL_DESCRIPTORS_H_ + +#include "content/public/common/content_descriptors.h" + +// This is a list of global descriptor keys to be used with the +// base::GlobalDescriptors object (see base/posix/global_descriptors.h) +enum { + // TODO(gunsch): Remove once there's a real value here. Otherwise, non-Android + // build compile fails due to empty enum. + kDummyValue = kContentIPCDescriptorMax + 1, +#if defined(OS_ANDROID) + kAndroidPakDescriptor, +#endif // defined(OS_ANDROID) +}; + +#endif // CHROMECAST_COMMON_GLOBAL_DESCRIPTORS_H_ diff --git a/chromecast/service/cast_service_android.cc b/chromecast/service/cast_service_android.cc new file mode 100644 index 0000000..afaab64 --- /dev/null +++ b/chromecast/service/cast_service_android.cc @@ -0,0 +1,37 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromecast/service/cast_service_android.h" + +#include "base/callback.h" +#include "chromecast/android/chromecast_config_android.h" +#include "chromecast/shell/browser/android/cast_window_manager.h" + +namespace chromecast { + +// static +CastService* CastService::Create(content::BrowserContext* browser_context) { + return new CastServiceAndroid(browser_context); +} + +CastServiceAndroid::CastServiceAndroid(content::BrowserContext* browser_context) + : CastService(browser_context) { + shell::SetBrowserContextAndroid(browser_context); +} + +CastServiceAndroid::~CastServiceAndroid() { +} + +void CastServiceAndroid::Initialize() { + // TODO(gunsch): Wire this the SendUsageStatsChanged callback once + // CastService::Delegate is added. +} + +void CastServiceAndroid::StartInternal() { +} + +void CastServiceAndroid::StopInternal() { +} + +} // namespace chromecast diff --git a/chromecast/service/cast_service_android.h b/chromecast/service/cast_service_android.h new file mode 100644 index 0000000..234b012 --- /dev/null +++ b/chromecast/service/cast_service_android.h @@ -0,0 +1,30 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMECAST_SERVICE_CAST_SERVICE_ANDROID_H_ +#define CHROMECAST_SERVICE_CAST_SERVICE_ANDROID_H_ + +#include "base/macros.h" +#include "chromecast/service/cast_service.h" + +namespace chromecast { + +class CastServiceAndroid : public CastService { + public: + explicit CastServiceAndroid(content::BrowserContext* browser_context); + virtual ~CastServiceAndroid(); + + protected: + // CastService implementation. + virtual void Initialize() OVERRIDE; + virtual void StartInternal() OVERRIDE; + virtual void StopInternal() OVERRIDE; + + private: + DISALLOW_COPY_AND_ASSIGN(CastServiceAndroid); +}; + +} // namespace chromecast + +#endif // CHROMECAST_SERVICE_CAST_SERVICE_ANDROID_H_ diff --git a/chromecast/shell/android/DEPS b/chromecast/shell/android/DEPS new file mode 100644 index 0000000..69269bc --- /dev/null +++ b/chromecast/shell/android/DEPS @@ -0,0 +1,3 @@ +include_rules = [ + "+content/public/android", +] diff --git a/chromecast/shell/android/apk/AndroidManifest.xml b/chromecast/shell/android/apk/AndroidManifest.xml new file mode 100644 index 0000000..63ad17c --- /dev/null +++ b/chromecast/shell/android/apk/AndroidManifest.xml @@ -0,0 +1,142 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- Copyright 2014 The Chromium Authors. All rights reserved. + Use of this source code is governed by a BSD-style license that can be + found in the LICENSE file. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="org.chromium.chromecast.shell"> + + <permission android:name="org.chromium.chromecast.shell.permission.SANDBOX" + android:protectionLevel="signature" /> + + <application android:name="org.chromium.chromecast.shell.CastApplication" + android:icon="@mipmap/app_icon" + android:label="@string/app_name"> + <activity android:name="org.chromium.chromecast.shell.CastShellActivity" + android:theme="@style/CastShellTheme" + android:exported="true" + android:hardwareAccelerated="true" + android:taskAffinity=".CastShellActivity" + android:configChanges="orientation|keyboardHidden|keyboard|screenSize" + android:excludeFromRecents="true" + android:noHistory="true" + android:permission="android.permission.INTERNET"> + <intent-filter> + <action android:name="android.intent.action.MAIN"/> + <category android:name="android.intent.category.LAUNCHER"/> + </intent-filter> + </activity> + + <!-- The following service entries exist in order to allow us to + start more than one sandboxed process. --> + <service android:name="org.chromium.content.app.SandboxedProcessService0" + android:process=":sandboxed_process0" + android:permission="org.chromium.chromecast.shell.permission.SANDBOX" + android:isolatedProcess="true" + android:exported="false" /> + <service android:name="org.chromium.content.app.SandboxedProcessService1" + android:process=":sandboxed_process1" + android:permission="org.chromium.chromecast.shell.permission.SANDBOX" + android:isolatedProcess="true" + android:exported="false" /> + <service android:name="org.chromium.content.app.SandboxedProcessService2" + android:process=":sandboxed_process2" + android:permission="org.chromium.chromecast.shell.permission.SANDBOX" + android:isolatedProcess="true" + android:exported="false" /> + <service android:name="org.chromium.content.app.SandboxedProcessService3" + android:process=":sandboxed_process3" + android:permission="org.chromium.chromecast.shell.permission.SANDBOX" + android:isolatedProcess="true" + android:exported="false" /> + <service android:name="org.chromium.content.app.SandboxedProcessService4" + android:process=":sandboxed_process4" + android:permission="org.chromium.chromecast.shell.permission.SANDBOX" + android:isolatedProcess="true" + android:exported="false" /> + <service android:name="org.chromium.content.app.SandboxedProcessService5" + android:process=":sandboxed_process5" + android:permission="org.chromium.chromecast.shell.permission.SANDBOX" + android:isolatedProcess="true" + android:exported="false" /> + <service android:name="org.chromium.content.app.SandboxedProcessService6" + android:process=":sandboxed_process6" + android:permission="org.chromium.chromecast.shell.permission.SANDBOX" + android:isolatedProcess="true" + android:exported="false" /> + <service android:name="org.chromium.content.app.SandboxedProcessService7" + android:process=":sandboxed_process7" + android:permission="org.chromium.chromecast.shell.permission.SANDBOX" + android:isolatedProcess="true" + android:exported="false" /> + <service android:name="org.chromium.content.app.SandboxedProcessService8" + android:process=":sandboxed_process8" + android:permission="org.chromium.chromecast.shell.permission.SANDBOX" + android:isolatedProcess="true" + android:exported="false" /> + <service android:name="org.chromium.content.app.SandboxedProcessService9" + android:process=":sandboxed_process9" + android:permission="org.chromium.chromecast.shell.permission.SANDBOX" + android:isolatedProcess="true" + android:exported="false" /> + <service android:name="org.chromium.content.app.SandboxedProcessService10" + android:process=":sandboxed_process10" + android:permission="org.chromium.chromecast.shell.permission.SANDBOX" + android:isolatedProcess="true" + android:exported="false" /> + <service android:name="org.chromium.content.app.SandboxedProcessService11" + android:process=":sandboxed_process11" + android:permission="org.chromium.chromecast.shell.permission.SANDBOX" + android:isolatedProcess="true" + android:exported="false" /> + <service android:name="org.chromium.content.app.SandboxedProcessService12" + android:process=":sandboxed_process12" + android:permission="org.chromium.chromecast.shell.permission.SANDBOX" + android:isolatedProcess="true" + android:exported="false" /> + <service android:name="org.chromium.content.app.SandboxedProcessService13" + android:process=":sandboxed_process13" + android:permission="org.chromium.content_shell.permission.SANDBOX" + android:isolatedProcess="true" + android:exported="false" /> + <service android:name="org.chromium.content.app.SandboxedProcessService14" + android:process=":sandboxed_process14" + android:permission="org.chromium.content_shell.permission.SANDBOX" + android:isolatedProcess="true" + android:exported="false" /> + <service android:name="org.chromium.content.app.SandboxedProcessService15" + android:process=":sandboxed_process15" + android:permission="org.chromium.content_shell.permission.SANDBOX" + android:isolatedProcess="true" + android:exported="false" /> + <service android:name="org.chromium.content.app.SandboxedProcessService16" + android:process=":sandboxed_process16" + android:permission="org.chromium.content_shell.permission.SANDBOX" + android:isolatedProcess="true" + android:exported="false" /> + <service android:name="org.chromium.content.app.SandboxedProcessService17" + android:process=":sandboxed_process17" + android:permission="org.chromium.content_shell.permission.SANDBOX" + android:isolatedProcess="true" + android:exported="false" /> + <service android:name="org.chromium.content.app.SandboxedProcessService18" + android:process=":sandboxed_process18" + android:permission="org.chromium.content_shell.permission.SANDBOX" + android:isolatedProcess="true" + android:exported="false" /> + <service android:name="org.chromium.content.app.SandboxedProcessService19" + android:process=":sandboxed_process19" + android:permission="org.chromium.content_shell.permission.SANDBOX" + android:isolatedProcess="true" + android:exported="false" /> + </application> + + <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="19" /> + <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> + <uses-permission android:name="android.permission.INTERNET"/> + <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/> + <uses-permission android:name="android.permission.WAKE_LOCK"/> + +</manifest> diff --git a/chromecast/shell/android/apk/res/layout/cast_shell_activity.xml b/chromecast/shell/android/apk/res/layout/cast_shell_activity.xml new file mode 100644 index 0000000..428b1a5 --- /dev/null +++ b/chromecast/shell/android/apk/res/layout/cast_shell_activity.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- Copyright 2014 The Chromium Authors. All rights reserved. + Use of this source code is governed by a BSD-style license that can be + found in the LICENSE file. + --> + +<merge xmlns:android="http://schemas.android.com/apk/res/android"> + <org.chromium.chromecast.shell.CastWindowManager + android:id="@+id/shell_container" + android:layout_width="match_parent" + android:layout_height="match_parent" /> +</merge> diff --git a/chromecast/shell/android/apk/res/layout/cast_window_view.xml b/chromecast/shell/android/apk/res/layout/cast_window_view.xml new file mode 100644 index 0000000..ce57c9e --- /dev/null +++ b/chromecast/shell/android/apk/res/layout/cast_window_view.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- Copyright 2014 The Chromium Authors. All rights reserved. + Use of this source code is governed by a BSD-style license that can be + found in the LICENSE file. + --> + +<org.chromium.chromecast.shell.CastWindowAndroid + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/container" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical" > + <FrameLayout android:id="@+id/contentview_holder" + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1" /> +</org.chromium.chromecast.shell.CastWindowAndroid> diff --git a/chromecast/shell/android/apk/res/mipmap-hdpi/app_icon.png b/chromecast/shell/android/apk/res/mipmap-hdpi/app_icon.png Binary files differnew file mode 100644 index 0000000..72d8318 --- /dev/null +++ b/chromecast/shell/android/apk/res/mipmap-hdpi/app_icon.png diff --git a/chromecast/shell/android/apk/res/mipmap-mdpi/app_icon.png b/chromecast/shell/android/apk/res/mipmap-mdpi/app_icon.png Binary files differnew file mode 100644 index 0000000..4076d5d --- /dev/null +++ b/chromecast/shell/android/apk/res/mipmap-mdpi/app_icon.png diff --git a/chromecast/shell/android/apk/res/mipmap-xhdpi/app_icon.png b/chromecast/shell/android/apk/res/mipmap-xhdpi/app_icon.png Binary files differnew file mode 100644 index 0000000..c7f092c --- /dev/null +++ b/chromecast/shell/android/apk/res/mipmap-xhdpi/app_icon.png diff --git a/chromecast/shell/android/apk/res/mipmap-xxhdpi/app_icon.png b/chromecast/shell/android/apk/res/mipmap-xxhdpi/app_icon.png Binary files differnew file mode 100644 index 0000000..9addc3b --- /dev/null +++ b/chromecast/shell/android/apk/res/mipmap-xxhdpi/app_icon.png diff --git a/chromecast/shell/android/apk/res/values-v17/styles.xml b/chromecast/shell/android/apk/res/values-v17/styles.xml new file mode 100644 index 0000000..80eaf9b --- /dev/null +++ b/chromecast/shell/android/apk/res/values-v17/styles.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- Copyright 2014 The Chromium Authors. All rights reserved. + Use of this source code is governed by a BSD-style license that can be + found in the LICENSE file. +--> + +<resources> + <style name="CastShellTheme" parent="@android:style/Theme.Holo.Light.NoActionBar"> + <item name="android:windowBackground">@android:color/black</item> + </style> +</resources> diff --git a/chromecast/shell/android/apk/res/values/strings.xml b/chromecast/shell/android/apk/res/values/strings.xml new file mode 100644 index 0000000..9ca510a6 --- /dev/null +++ b/chromecast/shell/android/apk/res/values/strings.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- Copyright 2014 The Chromium Authors. All rights reserved. + Use of this source code is governed by a BSD-style license that can be + found in the LICENSE file. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android"> + <string name="app_name">Chromecast Android Shell</string> + <string name="browser_process_initialization_failed">Initialization failed.</string> +</resources> diff --git a/chromecast/shell/android/apk/src/org/chromium/chromecast/shell/CastApplication.java b/chromecast/shell/android/apk/src/org/chromium/chromecast/shell/CastApplication.java new file mode 100644 index 0000000..1360dab --- /dev/null +++ b/chromecast/shell/android/apk/src/org/chromium/chromecast/shell/CastApplication.java @@ -0,0 +1,35 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chromecast.shell; + +import org.chromium.base.PathUtils; +import org.chromium.content.app.ContentApplication; +import org.chromium.content.browser.ResourceExtractor; + +/** + * Entry point for the Android cast shell application. Handles initialization of information that + * needs to be shared across the main activity and the child services created. + * + * Note that this gets run for each process, including sandboxed child render processes. Child + * processes don't need most of the full "setup" performed in CastBrowserHelper.java, but they do + * require a few basic pieces (found here). + */ +public class CastApplication extends ContentApplication { + + private static final String[] MANDATORY_PAK_FILES = new String[] {"cast_shell.pak"}; + private static final String PRIVATE_DATA_DIRECTORY_SUFFIX = "cast_shell"; + + @Override + public void onCreate() { + super.onCreate(); + initializeApplicationParameters(); + } + + public static void initializeApplicationParameters() { + ResourceExtractor.setMandatoryPaksToExtract(MANDATORY_PAK_FILES); + PathUtils.setPrivateDataDirectorySuffix(PRIVATE_DATA_DIRECTORY_SUFFIX); + } + +} diff --git a/chromecast/shell/android/apk/src/org/chromium/chromecast/shell/CastBrowserHelper.java b/chromecast/shell/android/apk/src/org/chromium/chromecast/shell/CastBrowserHelper.java new file mode 100644 index 0000000..d3ea8ea --- /dev/null +++ b/chromecast/shell/android/apk/src/org/chromium/chromecast/shell/CastBrowserHelper.java @@ -0,0 +1,100 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chromecast.shell; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.os.Build; +import android.os.Debug; +import android.util.Log; + +import org.chromium.base.BaseSwitches; +import org.chromium.base.CommandLine; +import org.chromium.base.library_loader.LibraryLoader; +import org.chromium.base.library_loader.ProcessInitException; +import org.chromium.content.browser.BrowserStartupController; +import org.chromium.content.browser.DeviceUtils; +import org.chromium.content.common.ContentSwitches; + +/** + * Static, one-time initialization for the browser process. + */ +public class CastBrowserHelper { + private static final String TAG = "CastBrowserHelper"; + + public static final String COMMAND_LINE_FILE = "/data/local/tmp/castshell-command-line"; + public static final String COMMAND_LINE_ARGS_KEY = "commandLineArgs"; + + private static boolean sIsBrowserInitialized = false; + + /** + * Starts the browser process synchronously, returning success or failure. If the browser has + * already started, immediately returns true without performing any more initialization. + * This may only be called on the UI thread. + * + * @return whether or not the process started successfully + */ + public static boolean initializeBrowser(Context context) { + if (sIsBrowserInitialized) return true; + + Log.d(TAG, "Performing one-time browser initialization"); + + // Initializing the command line must occur before loading the library. + if (!CommandLine.isInitialized()) { + if (allowCommandLineImport()) { + Log.d(TAG, "Initializing command line from " + COMMAND_LINE_FILE); + CommandLine.initFromFile(COMMAND_LINE_FILE); + } else { + CommandLine.init(null); + } + + if (context instanceof Activity) { + Intent launchingIntent = ((Activity) context).getIntent(); + String[] commandLineParams = getCommandLineParamsFromIntent(launchingIntent); + if (commandLineParams != null) { + CommandLine.getInstance().appendSwitchesAndArguments(commandLineParams); + } + } + } + + CommandLine.getInstance().appendSwitchWithValue( + ContentSwitches.FORCE_DEVICE_SCALE_FACTOR, "1"); + + waitForDebuggerIfNeeded(); + + DeviceUtils.addDeviceSpecificUserAgentSwitch(context); + + try { + LibraryLoader.ensureInitialized(); + + Log.d(TAG, "Loading BrowserStartupController..."); + BrowserStartupController.get(context).startBrowserProcessesSync(false); + + sIsBrowserInitialized = true; + return true; + } catch (ProcessInitException e) { + Log.e(TAG, "Unable to launch browser process.", e); + return false; + } + } + + private static boolean allowCommandLineImport() { + return !Build.TYPE.equals("user"); + } + + private static String[] getCommandLineParamsFromIntent(Intent intent) { + return intent != null ? intent.getStringArrayExtra(COMMAND_LINE_ARGS_KEY) : null; + } + + private static void waitForDebuggerIfNeeded() { + if (!CommandLine.getInstance().hasSwitch(BaseSwitches.WAIT_FOR_JAVA_DEBUGGER)) { + return; + } + Log.e(TAG, "Waiting for Java debugger to connect..."); + Debug.waitForDebugger(); + Log.e(TAG, "Java debugger connected. Resuming execution."); + } +} diff --git a/chromecast/shell/android/apk/src/org/chromium/chromecast/shell/CastShellActivity.java b/chromecast/shell/android/apk/src/org/chromium/chromecast/shell/CastShellActivity.java new file mode 100644 index 0000000..e88bad3 --- /dev/null +++ b/chromecast/shell/android/apk/src/org/chromium/chromecast/shell/CastShellActivity.java @@ -0,0 +1,291 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chromecast.shell; + +import android.app.Activity; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.media.AudioManager; +import android.net.Uri; +import android.os.Bundle; +import android.support.v4.content.LocalBroadcastManager; +import android.util.Log; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.view.WindowManager; +import android.widget.Toast; + +import org.chromium.base.CommandLine; +import org.chromium.content.browser.ActivityContentVideoViewClient; +import org.chromium.content.browser.ContentVideoViewClient; +import org.chromium.content.browser.ContentViewClient; +import org.chromium.content.browser.ContentViewCore; +import org.chromium.ui.base.WindowAndroid; + +/** + * Activity for managing the Cast shell. + */ +public class CastShellActivity extends Activity { + private static final String TAG = "CastShellActivity"; + + private static final String ACTIVE_SHELL_URL_KEY = "activeUrl"; + private static final int DEFAULT_HEIGHT_PIXELS = 720; + public static final String ACTION_EXTRA_RESOLUTION_HEIGHT = + "org.chromium.chromecast.shell.intent.extra.RESOLUTION_HEIGHT"; + + private CastWindowManager mCastWindowManager; + private AudioManager mAudioManager; + private BroadcastReceiver mBroadcastReceiver; + + // Native window instance. + // TODO(byungchul, gunsch): CastShellActivity, CastWindowAndroid, and native CastWindowAndroid + // have a one-to-one relationship. Consider instantiating CastWindow here and CastWindow having + // this native shell instance. + private long mNativeCastWindow; + + /** + * Returns whether or not CastShellActivity should launch the browser startup sequence. + * Intended to be overridden. + */ + protected boolean shouldLaunchBrowser() { + return true; + } + + @Override + protected void onCreate(final Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + exitIfUrlMissing(); + + if (shouldLaunchBrowser()) { + if (!CastBrowserHelper.initializeBrowser(getApplicationContext())) { + Toast.makeText(this, + R.string.browser_process_initialization_failed, + Toast.LENGTH_SHORT).show(); + finish(); + } + } + + // Whenever our app is visible, volume controls should modify the music stream. + // For more information read: + // http://developer.android.com/training/managing-audio/volume-playback.html + setVolumeControlStream(AudioManager.STREAM_MUSIC); + + // Set flags to both exit sleep mode when this activity starts and + // avoid entering sleep mode while playing media. We cannot distinguish + // between video and audio so this applies to both. + getWindow().addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON); + getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + + mAudioManager = (AudioManager)getSystemService(Context.AUDIO_SERVICE); + + setContentView(R.layout.cast_shell_activity); + mCastWindowManager = (CastWindowManager) findViewById(R.id.shell_container); + mCastWindowManager.setDelegate(new CastWindowManager.Delegate() { + @Override + public void onCreated() { + } + + @Override + public void onClosed() { + mNativeCastWindow = 0; + mCastWindowManager.setDelegate(null); + finish(); + } + }); + setResolution(); + mCastWindowManager.setWindow(new WindowAndroid(this)); + + registerBroadcastReceiver(); + + String url = getIntent().getDataString(); + Log.d(TAG, "onCreate startupUrl: " + url); + mNativeCastWindow = mCastWindowManager.launchCastWindow(url); + + getActiveContentViewCore().setContentViewClient(new ContentViewClient() { + @Override + public ContentVideoViewClient getContentVideoViewClient() { + return new ActivityContentVideoViewClient(CastShellActivity.this); + } + }); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + + unregisterBroadcastReceiver(); + + if (mNativeCastWindow != 0) { + mCastWindowManager.stopCastWindow(mNativeCastWindow); + mNativeCastWindow = 0; + } + } + + @Override + protected void onNewIntent(Intent intent) { + // Only handle direct intents (e.g. "fling") if this activity is also managing + // the browser process. + if (!shouldLaunchBrowser()) return; + + String url = intent.getDataString(); + Log.d(TAG, "onNewIntent: " + url); + + // Reset broadcast intent uri and receiver. + setIntent(intent); + exitIfUrlMissing(); + getActiveCastWindow().loadUrl(url); + } + + @Override + protected void onResume() { + super.onResume(); + + // Inform ContentView that this activity is being shown. + ContentViewCore view = getActiveContentViewCore(); + if (view != null) view.onShow(); + + // Request audio focus so any other audio playback doesn't continue in the background. + if (mAudioManager.requestAudioFocus( + null, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN) + != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { + Log.e(TAG, "Failed to obtain audio focus"); + } + } + + @Override + protected void onPause() { + // As soon as the cast app is no longer in the foreground, we ought to immediately tear + // everything down. Apps should not continue running and playing sound in the background. + super.onPause(); + + // Release the audio focus. Note that releasing audio focus does not stop audio playback, + // it just notifies the framework that this activity has stopped playing audio. + if (mAudioManager.abandonAudioFocus(null) != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { + Log.e(TAG, "Failed to abandon audio focus"); + } + + ContentViewCore view = getActiveContentViewCore(); + if (view != null) view.onHide(); + + finishGracefully(); + } + + protected void finishGracefully() { + if (mNativeCastWindow != 0) { + mCastWindowManager.stopCastWindow(mNativeCastWindow); + mNativeCastWindow = 0; + } + } + + private void registerBroadcastReceiver() { + if (mBroadcastReceiver == null) { + mBroadcastReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + Log.d(TAG, "Received intent: action=" + intent.getAction()); + if (CastWindowAndroid.ACTION_ENABLE_DEV_TOOLS.equals(intent.getAction())) { + mCastWindowManager.nativeEnableDevTools(true); + } else if (CastWindowAndroid.ACTION_DISABLE_DEV_TOOLS.equals( + intent.getAction())) { + mCastWindowManager.nativeEnableDevTools(false); + } + } + }; + } + + IntentFilter devtoolsBroadcastIntentFilter = new IntentFilter(); + devtoolsBroadcastIntentFilter.addAction(CastWindowAndroid.ACTION_ENABLE_DEV_TOOLS); + devtoolsBroadcastIntentFilter.addAction(CastWindowAndroid.ACTION_DISABLE_DEV_TOOLS); + LocalBroadcastManager.getInstance(this) + .registerReceiver(mBroadcastReceiver, devtoolsBroadcastIntentFilter); + } + + private void unregisterBroadcastReceiver() { + LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(this); + broadcastManager.unregisterReceiver(mBroadcastReceiver); + } + + private void setResolution() { + int requestedHeight = getIntent().getIntExtra( + ACTION_EXTRA_RESOLUTION_HEIGHT, DEFAULT_HEIGHT_PIXELS); + int displayHeight = getResources().getDisplayMetrics().heightPixels; + // Clamp within [DEFAULT_HEIGHT_PIXELS, displayHeight] + int desiredHeight = + Math.min(displayHeight, Math.max(DEFAULT_HEIGHT_PIXELS, requestedHeight)); + double deviceScaleFactor = ((double)displayHeight) / desiredHeight; + Log.d(TAG, "Using scale factor " + deviceScaleFactor + " to set height " + desiredHeight); + CommandLine.getInstance().appendSwitchWithValue("force-device-scale-factor", + String.valueOf(deviceScaleFactor)); + } + + private void exitIfUrlMissing() { + Intent intent = getIntent(); + if (intent != null && intent.getData() != null && !intent.getData().equals(Uri.EMPTY)) { + return; + } + // Log an exception so that the exit cause is obvious when reading the logs. + Log.e(TAG, "Activity will not start", + new IllegalArgumentException("Intent did not contain a valid url")); + System.exit(-1); + } + + /** + * @return The currently visible {@link CastWindowAndroid} or null if one is not showing. + */ + public CastWindowAndroid getActiveCastWindow() { + return mCastWindowManager.getActiveCastWindow(); + } + + /** + * @return The {@link ContentViewCore} owned by the currently visible {@link CastWindowAndroid}, + * or null if one is not showing. + */ + public ContentViewCore getActiveContentViewCore() { + CastWindowAndroid shell = getActiveCastWindow(); + return shell != null ? shell.getContentViewCore() : null; + } + + @Override + public boolean onKeyUp(int keyCode, KeyEvent event) { + if (keyCode != KeyEvent.KEYCODE_BACK) { + return super.onKeyUp(keyCode, event); + } + + // Just finish this activity to go back to the previous activity or launcher. + finishGracefully(); + return true; + } + + @Override + public boolean dispatchKeyEvent(KeyEvent event) { + int keyCode = event.getKeyCode(); + if (keyCode == KeyEvent.KEYCODE_BACK) { + return super.dispatchKeyEvent(event); + } + return false; + } + + @Override + public boolean dispatchGenericMotionEvent(MotionEvent ev) { + return false; + } + + @Override + public boolean dispatchKeyShortcutEvent(KeyEvent event) { + return false; + } + + @Override + public boolean dispatchTouchEvent(MotionEvent ev) { + return false; + } + + @Override + public boolean dispatchTrackballEvent(MotionEvent ev) { + return false; + } +} diff --git a/chromecast/shell/android/apk/src/org/chromium/chromecast/shell/CastWindowAndroid.java b/chromecast/shell/android/apk/src/org/chromium/chromecast/shell/CastWindowAndroid.java new file mode 100644 index 0000000..057f812 --- /dev/null +++ b/chromecast/shell/android/apk/src/org/chromium/chromecast/shell/CastWindowAndroid.java @@ -0,0 +1,155 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chromecast.shell; + +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.support.v4.content.LocalBroadcastManager; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.util.Log; +import android.view.ViewGroup; +import android.widget.FrameLayout; +import android.widget.LinearLayout; + +import org.chromium.base.CalledByNative; +import org.chromium.base.JNINamespace; +import org.chromium.content.browser.ContentView; +import org.chromium.content.browser.ContentViewCore; +import org.chromium.content.browser.ContentViewRenderView; +import org.chromium.content.browser.WebContentsObserverAndroid; +import org.chromium.content_public.browser.LoadUrlParams; +import org.chromium.ui.base.WindowAndroid; + +/** + * Container for the various UI components that make up a shell window. + */ +@JNINamespace("chromecast::shell") +public class CastWindowAndroid extends LinearLayout { + public static final String TAG = "CastWindowAndroid"; + + public static final String ACTION_PAGE_LOADED = "castPageLoaded"; + public static final String ACTION_ENABLE_DEV_TOOLS = "castEnableDevTools"; + public static final String ACTION_DISABLE_DEV_TOOLS = "castDisableDevTools"; + + private ContentViewCore mContentViewCore; + private ContentViewRenderView mContentViewRenderView; + private WebContentsObserverAndroid mWebContentsObserver; + private WindowAndroid mWindow; + + /** + * Constructor for inflating via XML. + */ + public CastWindowAndroid(Context context, AttributeSet attrs) { + super(context, attrs); + } + + /** + * Set the SurfaceView being renderered to as soon as it is available. + */ + public void setContentViewRenderView(ContentViewRenderView contentViewRenderView) { + FrameLayout contentViewHolder = (FrameLayout) findViewById(R.id.contentview_holder); + if (contentViewRenderView == null) { + if (mContentViewRenderView != null) { + contentViewHolder.removeView(mContentViewRenderView); + } + } else { + contentViewHolder.addView(contentViewRenderView, + new FrameLayout.LayoutParams( + FrameLayout.LayoutParams.MATCH_PARENT, + FrameLayout.LayoutParams.MATCH_PARENT)); + } + mContentViewRenderView = contentViewRenderView; + } + + /** + * @param window The owning window for this shell. + */ + public void setWindow(WindowAndroid window) { + mWindow = window; + } + + /** + * Loads an URL. This will perform minimal amounts of sanitizing of the URL to attempt to + * make it valid. + * + * @param url The URL to be loaded by the shell. + */ + public void loadUrl(String url) { + if (url == null) return; + + if (TextUtils.equals(url, mContentViewCore.getUrl())) { + mContentViewCore.reload(true); + } else { + mContentViewCore.loadUrl(new LoadUrlParams(normalizeUrl(url))); + } + + // TODO(aurimas): Remove this when crbug.com/174541 is fixed. + mContentViewCore.getContainerView().clearFocus(); + mContentViewCore.getContainerView().requestFocus(); + } + + /** + * Given a URI String, performs minimal normalization to attempt to build a usable URL from it. + * @param uriString The passed-in path to be normalized. + * @return The normalized URL, as a string. + */ + private static String normalizeUrl(String uriString) { + if (uriString == null) return uriString; + Uri uri = Uri.parse(uriString); + if (uri.getScheme() == null) { + uri = Uri.parse("http://" + uriString); + } + return uri.toString(); + } + + /** + * Initializes the ContentView based on the native tab contents pointer passed in. + * @param nativeWebContents The pointer to the native tab contents object. + */ + @SuppressWarnings("unused") + @CalledByNative + private void initFromNativeWebContents(long nativeWebContents) { + Context context = getContext(); + mContentViewCore = new ContentViewCore(context); + ContentView view = ContentView.newInstance(context, mContentViewCore); + mContentViewCore.initialize(view, view, nativeWebContents, mWindow); + + if (getParent() != null) mContentViewCore.onShow(); + ((FrameLayout) findViewById(R.id.contentview_holder)).addView(view, + new FrameLayout.LayoutParams( + FrameLayout.LayoutParams.MATCH_PARENT, + FrameLayout.LayoutParams.MATCH_PARENT)); + view.requestFocus(); + mContentViewRenderView.setCurrentContentViewCore(mContentViewCore); + + mWebContentsObserver = new WebContentsObserverAndroid(mContentViewCore.getWebContents()) { + @Override + public void didStopLoading(String url) { + Uri intentUri = Uri.parse(mContentViewCore + .getOriginalUrlForActiveNavigationEntry()); + Log.v(TAG, "Broadcast ACTION_PAGE_LOADED: scheme=" + intentUri.getScheme() + + ", host=" + intentUri.getHost()); + LocalBroadcastManager.getInstance(getContext()).sendBroadcast( + new Intent(ACTION_PAGE_LOADED, intentUri)); + } + }; + } + + /** + * @return The {@link ViewGroup} currently shown by this Shell. + */ + public ViewGroup getContentView() { + return mContentViewCore.getContainerView(); + } + + /** + * @return The {@link ContentViewCore} currently managing the view shown by this Shell. + */ + public ContentViewCore getContentViewCore() { + return mContentViewCore; + } +} diff --git a/chromecast/shell/android/apk/src/org/chromium/chromecast/shell/CastWindowManager.java b/chromecast/shell/android/apk/src/org/chromium/chromecast/shell/CastWindowManager.java new file mode 100644 index 0000000..a8aca37 --- /dev/null +++ b/chromecast/shell/android/apk/src/org/chromium/chromecast/shell/CastWindowManager.java @@ -0,0 +1,155 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chromecast.shell; + +import android.content.Context; +import android.graphics.Color; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.widget.FrameLayout; + +import org.chromium.base.CalledByNative; +import org.chromium.base.JNINamespace; +import org.chromium.content.browser.ContentViewCore; +import org.chromium.content.browser.ContentViewRenderView; +import org.chromium.ui.base.WindowAndroid; + +/** + * Container and generator of CastWindow instances. + */ +@JNINamespace("chromecast::shell") +public class CastWindowManager extends FrameLayout { + private static final String TAG = "CastWindowManager"; + + private WindowAndroid mWindow; + private CastWindowAndroid mActiveCastWindow; + + // The target for all content rendering. + private ContentViewRenderView mContentViewRenderView; + + /** + * Delegate to deliver events from the native window. + */ + public interface Delegate { + public void onCreated(); + public void onClosed(); + } + private Delegate mDelegate; + + /** + * Constructor for inflating via XML. + */ + public CastWindowManager(Context context, AttributeSet attrs) { + super(context, attrs); + nativeInit(this); + } + + /** + * @param delegate Delegate to handle events. + */ + public void setDelegate(Delegate delegate) { + mDelegate = delegate; + } + + /** + * @param window Represents the activity window. + */ + public void setWindow(WindowAndroid window) { + assert window != null; + mWindow = window; + mContentViewRenderView = new ContentViewRenderView(getContext()) { + @Override + protected void onReadyToRender() { + setOverlayVideoMode(true); + } + }; + mContentViewRenderView.onNativeLibraryLoaded(window); + // Setting the background color to black avoids rendering a white splash screen + // before the players are loaded. See crbug/307113 for details. + mContentViewRenderView.setSurfaceViewBackgroundColor(Color.BLACK); + } + + /** + * @return The window used to generate all shells. + */ + public WindowAndroid getWindow() { + return mWindow; + } + + /** + * @return The currently visible shell view or null if one is not showing. + */ + public CastWindowAndroid getActiveCastWindow() { + return mActiveCastWindow; + } + + /** + * Creates a new shell pointing to the specified URL. + * @param url The URL the shell should load upon creation. + * @return Pointer of native cast shell instance. + */ + public long launchCastWindow(String url) { + return nativeLaunchCastWindow(url); + } + + /** + * Stops a native cast shell instance created by {@link #launchCastWindow(String)}. + * @param nativeCastWindow Pointer of native cast shell instance returned + * by {@link #launchCastWindow(String)}. + * @see #launchCastWindow(String) + */ + public void stopCastWindow(long nativeCastWindow) { + nativeStopCastWindow(nativeCastWindow); + } + + @SuppressWarnings("unused") + @CalledByNative + private Object createCastWindow() { + assert mContentViewRenderView != null; + LayoutInflater inflater = + (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); + CastWindowAndroid shellView = + (CastWindowAndroid) inflater.inflate(R.layout.cast_window_view, null); + shellView.setWindow(mWindow); + + if (mActiveCastWindow != null) closeCastWindow(mActiveCastWindow); + + shellView.setContentViewRenderView(mContentViewRenderView); + addView(shellView, new FrameLayout.LayoutParams( + FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)); + mActiveCastWindow = shellView; + ContentViewCore contentViewCore = mActiveCastWindow.getContentViewCore(); + if (contentViewCore != null) { + mContentViewRenderView.setCurrentContentViewCore(contentViewCore); + contentViewCore.onShow(); + } + + if (mDelegate != null) { + mDelegate.onCreated(); + } + + return shellView; + } + + @SuppressWarnings("unused") + @CalledByNative + private void closeCastWindow(CastWindowAndroid shellView) { + if (shellView == mActiveCastWindow) mActiveCastWindow = null; + ContentViewCore contentViewCore = shellView.getContentViewCore(); + if (contentViewCore != null) contentViewCore.onHide(); + shellView.setContentViewRenderView(null); + shellView.setWindow(null); + removeView(shellView); + + if (mDelegate != null) { + mDelegate.onClosed(); + } + } + + private static native void nativeInit(Object shellManagerInstance); + private static native long nativeLaunchCastWindow(String url); + private static native void nativeStopCastWindow(long pointerOfNativeCastWindow); + public static native void nativeEnableDevTools(boolean enable); +} diff --git a/chromecast/shell/app/DEPS b/chromecast/shell/app/DEPS index 7c1793e..efc610d 100644 --- a/chromecast/shell/app/DEPS +++ b/chromecast/shell/app/DEPS @@ -1,3 +1,4 @@ include_rules = [ "+content/public/app", + "+content/public/browser", ] diff --git a/chromecast/shell/app/android/cast_jni_loader.cc b/chromecast/shell/app/android/cast_jni_loader.cc new file mode 100644 index 0000000..4cbf8f8 --- /dev/null +++ b/chromecast/shell/app/android/cast_jni_loader.cc @@ -0,0 +1,36 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/android/jni_android.h" +#include "base/android/jni_registrar.h" +#include "base/android/library_loader/library_loader_hooks.h" +#include "base/basictypes.h" +#include "base/debug/debugger.h" +#include "base/logging.h" +#include "chromecast/android/cast_jni_registrar.h" +#include "chromecast/android/platform_jni_loader.h" +#include "chromecast/shell/app/cast_main_delegate.h" +#include "content/public/app/android_library_loader_hooks.h" +#include "content/public/app/content_main.h" +#include "content/public/browser/android/compositor.h" + +// This is called by the VM when the shared library is first loaded. +JNI_EXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) { + base::android::SetLibraryLoadedHook(&content::LibraryLoaded); + base::android::InitVM(vm); + JNIEnv* env = base::android::AttachCurrentThread(); + + if (!base::android::RegisterLibraryLoaderEntryHook(env)) return -1; + + // To be called only from the UI thread. If loading the library is done on + // a separate thread, this should be moved elsewhere. + if (!chromecast::android::RegisterJni(env)) return -1; + // Allow platform-specific implementations to perform more JNI registration. + if (!chromecast::android::PlatformRegisterJni(env)) return -1; + + content::Compositor::Initialize(); + content::SetContentMainDelegate(new chromecast::shell::CastMainDelegate); + + return JNI_VERSION_1_4; +} diff --git a/chromecast/shell/app/cast_main_delegate.cc b/chromecast/shell/app/cast_main_delegate.cc index 1fb2ad7..f713f5b 100644 --- a/chromecast/shell/app/cast_main_delegate.cc +++ b/chromecast/shell/app/cast_main_delegate.cc @@ -4,12 +4,16 @@ #include "chromecast/shell/app/cast_main_delegate.h" +#include "base/cpu.h" #include "base/logging.h" #include "base/path_service.h" +#include "base/posix/global_descriptors.h" #include "chromecast/common/cast_paths.h" #include "chromecast/common/cast_resource_delegate.h" +#include "chromecast/common/global_descriptors.h" #include "chromecast/shell/browser/cast_content_browser_client.h" #include "chromecast/shell/renderer/cast_content_renderer_client.h" +#include "content/public/browser/browser_main_runner.h" #include "content/public/common/content_switches.h" #include "ui/base/resource/resource_bundle.h" @@ -23,26 +27,74 @@ CastMainDelegate::~CastMainDelegate() { } bool CastMainDelegate::BasicStartupComplete(int* exit_code) { + RegisterPathProvider(); + logging::LoggingSettings settings; +#if defined(OS_ANDROID) + base::FilePath log_file; + PathService::Get(FILE_CAST_ANDROID_LOG, &log_file); + settings.logging_dest = logging::LOG_TO_ALL; + settings.log_file = log_file.value().c_str(); + settings.delete_old = logging::DELETE_OLD_LOG_FILE; +#else settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG; +#endif // defined(OS_ANDROID) logging::InitLogging(settings); // Time, process, and thread ID are available through logcat. logging::SetLogItems(true, true, false, false); - RegisterPathProvider(); - content::SetContentClient(&content_client_); return false; } void CastMainDelegate::PreSandboxStartup() { +#if defined(ARCH_CPU_ARM_FAMILY) && (defined(OS_ANDROID) || defined(OS_LINUX)) + // Create an instance of the CPU class to parse /proc/cpuinfo and cache the + // results. This data needs to be cached when file-reading is still allowed, + // since base::CPU expects to be callable later, when file-reading is no + // longer allowed. + base::CPU cpu_info; +#endif + InitializeResourceBundle(); } +int CastMainDelegate::RunProcess( + const std::string& process_type, + const content::MainFunctionParams& main_function_params) { +#if defined(OS_ANDROID) + if (!process_type.empty()) + return -1; + + // Note: Android must handle running its own browser process. + // See ChromeMainDelegateAndroid::RunProcess. + browser_runner_.reset(content::BrowserMainRunner::Create()); + return browser_runner_->Initialize(main_function_params); +#else + return -1; +#endif // defined(OS_ANDROID) +} + +#if !defined(OS_ANDROID) void CastMainDelegate::ZygoteForked() { } +#endif // !defined(OS_ANDROID) void CastMainDelegate::InitializeResourceBundle() { +#if defined(OS_ANDROID) + // On Android, the renderer runs with a different UID and can never access + // the file system. Use the file descriptor passed in at launch time. + int pak_fd = + base::GlobalDescriptors::GetInstance()->MaybeGet(kAndroidPakDescriptor); + if (pak_fd >= 0) { + ui::ResourceBundle::InitSharedInstanceWithPakFileRegion( + base::File(pak_fd), base::MemoryMappedFile::Region::kWholeFile); + ui::ResourceBundle::GetSharedInstance().AddDataPackFromFile( + base::File(pak_fd), ui::SCALE_FACTOR_100P); + return; + } +#endif + resource_delegate_.reset(new CastResourceDelegate()); // TODO(gunsch): Use LOAD_COMMON_RESOURCES once ResourceBundle no longer // hardcodes resource file names. diff --git a/chromecast/shell/app/cast_main_delegate.h b/chromecast/shell/app/cast_main_delegate.h index 8c9ed62..4240953 100644 --- a/chromecast/shell/app/cast_main_delegate.h +++ b/chromecast/shell/app/cast_main_delegate.h @@ -10,6 +10,10 @@ #include "chromecast/shell/common/cast_content_client.h" #include "content/public/app/content_main_delegate.h" +namespace content { +class BrowserMainRunner; +} // namespace content + namespace chromecast { class CastResourceDelegate; @@ -27,7 +31,12 @@ class CastMainDelegate : public content::ContentMainDelegate { // content::ContentMainDelegate implementation: virtual bool BasicStartupComplete(int* exit_code) OVERRIDE; virtual void PreSandboxStartup() OVERRIDE; + virtual int RunProcess( + const std::string& process_type, + const content::MainFunctionParams& main_function_params) OVERRIDE; +#if !defined(OS_ANDROID) virtual void ZygoteForked() OVERRIDE; +#endif // !defined(OS_ANDROID) virtual content::ContentBrowserClient* CreateContentBrowserClient() OVERRIDE; virtual content::ContentRendererClient* CreateContentRendererClient() OVERRIDE; @@ -40,6 +49,10 @@ class CastMainDelegate : public content::ContentMainDelegate { scoped_ptr<CastResourceDelegate> resource_delegate_; CastContentClient content_client_; +#if defined(OS_ANDROID) + scoped_ptr<content::BrowserMainRunner> browser_runner_; +#endif // defined(OS_ANDROID) + DISALLOW_COPY_AND_ASSIGN(CastMainDelegate); }; diff --git a/chromecast/shell/browser/android/cast_window_android.cc b/chromecast/shell/browser/android/cast_window_android.cc new file mode 100644 index 0000000..65380b1 --- /dev/null +++ b/chromecast/shell/browser/android/cast_window_android.cc @@ -0,0 +1,130 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromecast/shell/browser/android/cast_window_android.h" + +#include "base/message_loop/message_loop.h" +#include "base/path_service.h" +#include "chromecast/shell/browser/android/cast_window_manager.h" +#include "content/public/browser/devtools_agent_host.h" +#include "content/public/browser/navigation_controller.h" +#include "content/public/browser/navigation_entry.h" +#include "content/public/browser/render_view_host.h" +#include "content/public/common/renderer_preferences.h" +#include "jni/CastWindowAndroid_jni.h" + +namespace chromecast { +namespace shell { + +// static +bool CastWindowAndroid::RegisterJni(JNIEnv* env) { + return RegisterNativesImpl(env); +} + +CastWindowAndroid::CastWindowAndroid(content::WebContents* web_contents) + : content::WebContentsObserver(web_contents) { +} + +CastWindowAndroid::~CastWindowAndroid() { +} + +// static +CastWindowAndroid* CastWindowAndroid::CreateNewWindow( + content::BrowserContext* browser_context, + const GURL& url) { + content::WebContents::CreateParams create_params(browser_context); + create_params.routing_id = MSG_ROUTING_NONE; + content::WebContents* web_contents = + content::WebContents::Create(create_params); + CastWindowAndroid* shell = CreateCastWindowAndroid( + web_contents, + create_params.initial_size); + if (!url.is_empty()) + shell->LoadURL(url); + return shell; +} + +// static +CastWindowAndroid* CastWindowAndroid::CreateCastWindowAndroid( + content::WebContents* web_contents, + const gfx::Size& initial_size) { + CastWindowAndroid* shell = new CastWindowAndroid(web_contents); + + JNIEnv* env = base::android::AttachCurrentThread(); + base::android::ScopedJavaLocalRef<jobject> shell_android( + CreateCastWindowView(shell)); + + shell->java_object_.Reset(env, shell_android.Release()); + shell->web_contents_.reset(web_contents); + web_contents->SetDelegate(shell); + + Java_CastWindowAndroid_initFromNativeWebContents( + env, shell->java_object_.obj(), reinterpret_cast<jint>(web_contents)); + + // Enabling hole-punching also requires runtime renderer preference + web_contents->GetMutableRendererPrefs()-> + use_video_overlay_for_embedded_encrypted_video = true; + web_contents->GetRenderViewHost()->SyncRendererPrefs(); + + return shell; +} + +void CastWindowAndroid::Close() { + // Note: if multiple windows becomes supported, this may close other devtools + // sessions. + content::DevToolsAgentHost::DetachAllClients(); + CloseCastWindowView(java_object_.obj()); + delete this; +} + +void CastWindowAndroid::LoadURL(const GURL& url) { + content::NavigationController::LoadURLParams params(url); + params.transition_type = content::PageTransitionFromInt( + content::PAGE_TRANSITION_TYPED | + content::PAGE_TRANSITION_FROM_ADDRESS_BAR); + web_contents_->GetController().LoadURLWithParams(params); + web_contents_->Focus(); +} + +void CastWindowAndroid::AddNewContents(content::WebContents* source, + content::WebContents* new_contents, + WindowOpenDisposition disposition, + const gfx::Rect& initial_pos, + bool user_gesture, + bool* was_blocked) { + NOTIMPLEMENTED(); + if (was_blocked) { + *was_blocked = true; + } +} + +void CastWindowAndroid::CloseContents(content::WebContents* source) { + DCHECK_EQ(source, web_contents_.get()); + Close(); +} + +bool CastWindowAndroid::CanOverscrollContent() const { + return false; +} + +bool CastWindowAndroid::AddMessageToConsole(content::WebContents* source, + int32 level, + const base::string16& message, + int32 line_no, + const base::string16& source_id) { + return false; +} + +void CastWindowAndroid::ActivateContents(content::WebContents* contents) { + DCHECK_EQ(contents, web_contents_.get()); + contents->GetRenderViewHost()->Focus(); +} + +void CastWindowAndroid::DeactivateContents(content::WebContents* contents) { + DCHECK_EQ(contents, web_contents_.get()); + contents->GetRenderViewHost()->Blur(); +} + +} // namespace shell +} // namespace chromecast diff --git a/chromecast/shell/browser/android/cast_window_android.h b/chromecast/shell/browser/android/cast_window_android.h new file mode 100644 index 0000000..979aea9 --- /dev/null +++ b/chromecast/shell/browser/android/cast_window_android.h @@ -0,0 +1,87 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMECAST_SHELL_BROWSER_ANDROID_CAST_WINDOW_ANDROID_H_ +#define CHROMECAST_SHELL_BROWSER_ANDROID_CAST_WINDOW_ANDROID_H_ + +#include <jni.h> +#include <vector> + +#include "base/android/jni_string.h" +#include "base/android/scoped_java_ref.h" +#include "base/basictypes.h" +#include "base/callback_forward.h" +#include "base/command_line.h" +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "build/build_config.h" +#include "content/public/browser/web_contents.h" +#include "content/public/browser/web_contents_delegate.h" +#include "content/public/browser/web_contents_observer.h" +#include "content/public/common/content_switches.h" +#include "ui/gfx/native_widget_types.h" +#include "ui/gfx/size.h" + +class GURL; + +namespace content { +class BrowserContext; +class SiteInstance; +class WebContents; +} // namespace content + +namespace chromecast { +namespace shell { + +class CastWindowAndroid : public content::WebContentsDelegate, + public content::WebContentsObserver { + public: + // Creates a new window and immediately loads the given URL. + static CastWindowAndroid* CreateNewWindow( + content::BrowserContext* browser_context, + const GURL& url); + + virtual ~CastWindowAndroid(); + + void LoadURL(const GURL& url); + void Close(); + + // Registers the JNI methods for CastWindowAndroid. + static bool RegisterJni(JNIEnv* env); + + // WebContentsDelegate implementation. + virtual void AddNewContents(content::WebContents* source, + content::WebContents* new_contents, + WindowOpenDisposition disposition, + const gfx::Rect& initial_pos, + bool user_gesture, + bool* was_blocked) OVERRIDE; + virtual void CloseContents(content::WebContents* source) OVERRIDE; + virtual bool CanOverscrollContent() const OVERRIDE; + virtual bool AddMessageToConsole(content::WebContents* source, + int32 level, + const base::string16& message, + int32 line_no, + const base::string16& source_id) OVERRIDE; + virtual void ActivateContents(content::WebContents* contents) OVERRIDE; + virtual void DeactivateContents(content::WebContents* contents) OVERRIDE; + + private: + explicit CastWindowAndroid(content::WebContents* web_contents); + + // Helper to create a new CastWindowAndroid given a newly created WebContents. + static CastWindowAndroid* CreateCastWindowAndroid( + content::WebContents* web_contents, + const gfx::Size& initial_size); + + base::android::ScopedJavaGlobalRef<jobject> java_object_; + scoped_ptr<content::WebContents> web_contents_; + + DISALLOW_COPY_AND_ASSIGN(CastWindowAndroid); +}; + +} // namespace shell +} // namespace chromecast + +#endif // CHROMECAST_SHELL_BROWSER_ANDROID_CAST_WINDOW_ANDROID_H_ diff --git a/chromecast/shell/browser/android/cast_window_manager.cc b/chromecast/shell/browser/android/cast_window_manager.cc new file mode 100644 index 0000000..daa18af --- /dev/null +++ b/chromecast/shell/browser/android/cast_window_manager.cc @@ -0,0 +1,88 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromecast/shell/browser/android/cast_window_manager.h" + +#include <jni.h> + +#include "base/android/jni_android.h" +#include "base/android/jni_string.h" +#include "base/android/scoped_java_ref.h" +#include "base/bind.h" +#include "base/lazy_instance.h" +#include "chromecast/common/chromecast_config.h" +#include "chromecast/common/pref_names.h" +#include "chromecast/shell/browser/android/cast_window_android.h" +#include "chromecast/shell/browser/cast_browser_context.h" +#include "chromecast/shell/browser/cast_browser_main_parts.h" +#include "chromecast/shell/browser/cast_content_browser_client.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/web_contents.h" +#include "ipc/ipc_channel.h" +#include "jni/CastWindowManager_jni.h" +#include "url/gurl.h" + +namespace { + +base::LazyInstance<base::android::ScopedJavaGlobalRef<jobject>> + g_window_manager = LAZY_INSTANCE_INITIALIZER; + +content::BrowserContext* g_browser_context = NULL; + +} // namespace + +namespace chromecast { +namespace shell { + +void SetBrowserContextAndroid(content::BrowserContext* browser_context) { + g_browser_context = browser_context; +} + +base::android::ScopedJavaLocalRef<jobject> +CreateCastWindowView(CastWindowAndroid* shell) { + JNIEnv* env = base::android::AttachCurrentThread(); + jobject j_window_manager = g_window_manager.Get().obj(); + return Java_CastWindowManager_createCastWindow(env, j_window_manager); +} + +void CloseCastWindowView(jobject shell_wrapper) { + JNIEnv* env = base::android::AttachCurrentThread(); + jobject j_window_manager = g_window_manager.Get().obj(); + Java_CastWindowManager_closeCastWindow(env, j_window_manager, shell_wrapper); +} + +// Register native methods +bool RegisterCastWindowManager(JNIEnv* env) { + return RegisterNativesImpl(env); +} + +void Init(JNIEnv* env, jclass clazz, jobject obj) { + g_window_manager.Get().Reset( + base::android::ScopedJavaLocalRef<jobject>(env, obj)); +} + +jlong LaunchCastWindow(JNIEnv* env, jclass clazz, jstring jurl) { + DCHECK(g_browser_context); + GURL url(base::android::ConvertJavaStringToUTF8(env, jurl)); + return reinterpret_cast<jlong>( + CastWindowAndroid::CreateNewWindow(g_browser_context, url)); +} + +void StopCastWindow(JNIEnv* env, jclass clazz, jlong nativeCastWindow) { + CastWindowAndroid* window = + reinterpret_cast<CastWindowAndroid*>(nativeCastWindow); + DCHECK(window); + window->Close(); +} + +void EnableDevTools(JNIEnv* env, jclass clazz, jboolean enable) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + // The specific port value doesn't matter since Android uses Unix domain + // sockets, only whether or not it is zero. + chromecast::ChromecastConfig::GetInstance()->pref_service()-> + SetInteger(prefs::kRemoteDebuggingPort, enable ? 1 : 0); +} + +} // namespace shell +} // namespace chromecast diff --git a/chromecast/shell/browser/android/cast_window_manager.h b/chromecast/shell/browser/android/cast_window_manager.h new file mode 100644 index 0000000..9a811a9 --- /dev/null +++ b/chromecast/shell/browser/android/cast_window_manager.h @@ -0,0 +1,39 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMECAST_SHELL_BROWSER_ANDROID_CAST_WINDOW_MANAGER_H_ +#define CHROMECAST_SHELL_BROWSER_ANDROID_CAST_WINDOW_MANAGER_H_ + +#include <jni.h> + +#include "base/android/jni_android.h" +#include "base/android/scoped_java_ref.h" + +class CastWindowAndroid; + +namespace content { +class BrowserContext; +} + +namespace chromecast { +namespace shell { + +// Sets the browser context to use for creating windows. Must be invoked before +// a LaunchCastWindow call. +void SetBrowserContextAndroid(content::BrowserContext* browser_context); + +// Given a CastWindowAndroid instance, creates and returns a Java wrapper. +base::android::ScopedJavaLocalRef<jobject> +CreateCastWindowView(CastWindowAndroid* shell); + +// Closes a previously created Java wrapper. +void CloseCastWindowView(jobject shell_wrapper); + +// Registers the CastWindowManager native methods. +bool RegisterCastWindowManager(JNIEnv* env); + +} // namespace shell +} // namespace chromecast + +#endif // CHROMECAST_SHELL_BROWSER_ANDROID_CAST_WINDOW_MANAGER_H_ diff --git a/chromecast/shell/browser/cast_browser_main_parts.cc b/chromecast/shell/browser/cast_browser_main_parts.cc index 3f7b358..24e1342 100644 --- a/chromecast/shell/browser/cast_browser_main_parts.cc +++ b/chromecast/shell/browser/cast_browser_main_parts.cc @@ -5,6 +5,7 @@ #include "chromecast/shell/browser/cast_browser_main_parts.h" #include "base/command_line.h" +#include "base/message_loop/message_loop.h" #include "base/prefs/pref_registry_simple.h" #include "chromecast/common/chromecast_config.h" #include "chromecast/metrics/cast_metrics_service_client.h" @@ -18,6 +19,10 @@ #include "chromecast/shell/browser/webui/webui_cast.h" #include "content/public/common/content_switches.h" +#if defined(OS_ANDROID) +#include "net/android/network_change_notifier_factory_android.h" +#endif // defined(OS_ANDROID) + namespace chromecast { namespace shell { @@ -60,12 +65,19 @@ CastBrowserMainParts::~CastBrowserMainParts() { } void CastBrowserMainParts::PreMainMessageLoopStart() { +#if defined(OS_ANDROID) + net::NetworkChangeNotifier::SetFactory( + new net::NetworkChangeNotifierFactoryAndroid()); +#else net::NetworkChangeNotifier::SetFactory( new NetworkChangeNotifierFactoryCast()); +#endif // defined(OS_ANDROID) } void CastBrowserMainParts::PostMainMessageLoopStart() { - NOTIMPLEMENTED(); +#if defined(OS_ANDROID) + base::MessageLoopForUI::current()->Start(); +#endif // defined(OS_ANDROID) } int CastBrowserMainParts::PreCreateThreads() { diff --git a/chromecast/shell/browser/cast_content_browser_client.cc b/chromecast/shell/browser/cast_content_browser_client.cc index f8d897e..919b190 100644 --- a/chromecast/shell/browser/cast_content_browser_client.cc +++ b/chromecast/shell/browser/cast_content_browser_client.cc @@ -6,12 +6,16 @@ #include "base/command_line.h" #include "base/i18n/rtl.h" +#include "base/path_service.h" +#include "chromecast/common/cast_paths.h" +#include "chromecast/common/global_descriptors.h" #include "chromecast/shell/browser/cast_browser_context.h" #include "chromecast/shell/browser/cast_browser_main_parts.h" #include "chromecast/shell/browser/cast_browser_process.h" #include "chromecast/shell/browser/geolocation/cast_access_token_store.h" #include "chromecast/shell/browser/url_request_context_factory.h" #include "content/public/browser/certificate_request_result_type.h" +#include "content/public/browser/file_descriptor_info.h" #include "content/public/browser/render_process_host.h" #include "content/public/common/content_descriptors.h" #include "content/public/common/content_switches.h" @@ -144,6 +148,19 @@ void CastContentBrowserClient::GetAdditionalMappedFilesForChildProcess( const base::CommandLine& command_line, int child_process_id, std::vector<content::FileDescriptorInfo>* mappings) { +#if defined(OS_ANDROID) + int flags = base::File::FLAG_OPEN | base::File::FLAG_READ; + base::FilePath pak_file; + CHECK(PathService::Get(FILE_CAST_PAK, &pak_file)); + base::File pak_with_flags(pak_file, flags); + if (!pak_with_flags.IsValid()) { + NOTREACHED() << "Failed to open file when creating renderer process: " + << "cast_shell.pak"; + } + mappings->push_back(content::FileDescriptorInfo( + kAndroidPakDescriptor, + base::FileDescriptor(base::File(pak_file, flags)))); +#endif // defined(OS_ANDROID) } } // namespace shell diff --git a/chromecast/shell/browser/cast_http_user_agent_settings.cc b/chromecast/shell/browser/cast_http_user_agent_settings.cc index fbc43b3..e4a1537 100644 --- a/chromecast/shell/browser/cast_http_user_agent_settings.cc +++ b/chromecast/shell/browser/cast_http_user_agent_settings.cc @@ -11,6 +11,10 @@ #include "net/http/http_util.h" #include "ui/base/l10n/l10n_util.h" +#if defined(OS_ANDROID) +#include "ui/base/l10n/l10n_util_android.h" +#endif // defined(OS_ANDROID) + namespace chromecast { namespace shell { @@ -26,7 +30,12 @@ std::string CastHttpUserAgentSettings::GetAcceptLanguage() const { DCHECK_CURRENTLY_ON(content::BrowserThread::IO); if (accept_language_.empty()) { accept_language_ = net::HttpUtil::GenerateAcceptLanguageHeader( - l10n_util::GetStringUTF8(IDS_CHROMECAST_SETTINGS_ACCEPT_LANGUAGES)); +#if defined(OS_ANDROID) + l10n_util::GetDefaultLocale() +#else + l10n_util::GetStringUTF8(IDS_CHROMECAST_SETTINGS_ACCEPT_LANGUAGES) +#endif // defined(OS_ANDROID) + ); } return accept_language_; } diff --git a/chromecast/shell/browser/url_request_context_factory.cc b/chromecast/shell/browser/url_request_context_factory.cc index d214db3..0bac1b3 100644 --- a/chromecast/shell/browser/url_request_context_factory.cc +++ b/chromecast/shell/browser/url_request_context_factory.cc @@ -63,8 +63,10 @@ class URLRequestContextFactory::URLRequestContextGetter request_context_.reset(factory_->CreateMediaRequestContext()); } else { request_context_.reset(factory_->CreateSystemRequestContext()); +#if defined(USE_NSS) // Set request context used by NSS for Crl requests. net::SetURLRequestContextForNSSHttpIO(request_context_.get()); +#endif // defined(USE_NSS) } } return request_context_.get(); @@ -144,6 +146,14 @@ void URLRequestContextFactory::InitializeOnUIThread() { // because it registers itself to pref notification observer which is not // thread safe. http_user_agent_settings_.reset(new CastHttpUserAgentSettings()); + + // Proxy config service should be initialized in UI thread, since + // ProxyConfigServiceDelegate on Android expects UI thread. + proxy_config_service_.reset(net::ProxyService::CreateSystemProxyConfigService( + content::BrowserThread::GetMessageLoopProxyForThread( + content::BrowserThread::IO), + content::BrowserThread::GetMessageLoopProxyForThread( + content::BrowserThread::FILE))); } net::URLRequestContextGetter* URLRequestContextFactory::CreateMainGetter( @@ -202,13 +212,7 @@ void URLRequestContextFactory::InitializeSystemContextDependencies() { http_server_properties_.reset(new net::HttpServerPropertiesImpl); proxy_service_.reset(net::ProxyService::CreateUsingSystemProxyResolver( - net::ProxyService::CreateSystemProxyConfigService( - content::BrowserThread::GetMessageLoopProxyForThread( - content::BrowserThread::IO), - content::BrowserThread::GetMessageLoopProxyForThread( - content::BrowserThread::FILE)), - 0, - NULL)); + proxy_config_service_.release(), 0, NULL)); system_dependencies_initialized_ = true; } diff --git a/chromecast/shell/browser/url_request_context_factory.h b/chromecast/shell/browser/url_request_context_factory.h index 03d062d..304cdea 100644 --- a/chromecast/shell/browser/url_request_context_factory.h +++ b/chromecast/shell/browser/url_request_context_factory.h @@ -11,6 +11,7 @@ namespace net { class HttpTransactionFactory; class HttpUserAgentSettings; +class ProxyConfigService; class URLRequestJobFactory; } // namespace net @@ -83,6 +84,7 @@ class URLRequestContextFactory { scoped_ptr<net::CertVerifier> cert_verifier_; scoped_refptr<net::SSLConfigService> ssl_config_service_; scoped_ptr<net::TransportSecurityState> transport_security_state_; + scoped_ptr<net::ProxyConfigService> proxy_config_service_; scoped_ptr<net::ProxyService> proxy_service_; scoped_ptr<net::HttpAuthHandlerFactory> http_auth_handler_factory_; scoped_ptr<net::HttpServerProperties> http_server_properties_; |