summaryrefslogtreecommitdiffstats
path: root/chromecast
diff options
context:
space:
mode:
authorgunsch <gunsch@chromium.org>2014-09-11 15:03:41 -0700
committerCommit bot <commit-bot@chromium.org>2014-09-11 22:25:48 +0000
commit407189f61ab162096ea98c40b2ecd85875bd6049 (patch)
tree30b112aec44856c8f10e6e52a820cf55dcb2affd /chromecast
parent149b92db00bc1deab22599e7d3da3fc8be2c0d8b (diff)
downloadchromium_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')
-rw-r--r--chromecast/DEPS1
-rw-r--r--chromecast/android/cast_jni_registrar.cc30
-rw-r--r--chromecast/android/cast_jni_registrar.h19
-rw-r--r--chromecast/android/chromecast_config_android.cc33
-rw-r--r--chromecast/android/chromecast_config_android.h43
-rw-r--r--chromecast/android/platform_jni_loader.h18
-rw-r--r--chromecast/android/platform_jni_loader_stub.cc16
-rw-r--r--chromecast/android/src/dummy2
-rw-r--r--chromecast/chromecast.gyp179
-rw-r--r--chromecast/common/cast_paths.cc11
-rw-r--r--chromecast/common/cast_paths.h5
-rw-r--r--chromecast/common/global_descriptors.h21
-rw-r--r--chromecast/service/cast_service_android.cc37
-rw-r--r--chromecast/service/cast_service_android.h30
-rw-r--r--chromecast/shell/android/DEPS3
-rw-r--r--chromecast/shell/android/apk/AndroidManifest.xml142
-rw-r--r--chromecast/shell/android/apk/res/layout/cast_shell_activity.xml13
-rw-r--r--chromecast/shell/android/apk/res/layout/cast_window_view.xml18
-rw-r--r--chromecast/shell/android/apk/res/mipmap-hdpi/app_icon.pngbin0 -> 5522 bytes
-rw-r--r--chromecast/shell/android/apk/res/mipmap-mdpi/app_icon.pngbin0 -> 3841 bytes
-rw-r--r--chromecast/shell/android/apk/res/mipmap-xhdpi/app_icon.pngbin0 -> 7377 bytes
-rw-r--r--chromecast/shell/android/apk/res/mipmap-xxhdpi/app_icon.pngbin0 -> 7155 bytes
-rw-r--r--chromecast/shell/android/apk/res/values-v17/styles.xml12
-rw-r--r--chromecast/shell/android/apk/res/values/strings.xml11
-rw-r--r--chromecast/shell/android/apk/src/org/chromium/chromecast/shell/CastApplication.java35
-rw-r--r--chromecast/shell/android/apk/src/org/chromium/chromecast/shell/CastBrowserHelper.java100
-rw-r--r--chromecast/shell/android/apk/src/org/chromium/chromecast/shell/CastShellActivity.java291
-rw-r--r--chromecast/shell/android/apk/src/org/chromium/chromecast/shell/CastWindowAndroid.java155
-rw-r--r--chromecast/shell/android/apk/src/org/chromium/chromecast/shell/CastWindowManager.java155
-rw-r--r--chromecast/shell/app/DEPS1
-rw-r--r--chromecast/shell/app/android/cast_jni_loader.cc36
-rw-r--r--chromecast/shell/app/cast_main_delegate.cc56
-rw-r--r--chromecast/shell/app/cast_main_delegate.h13
-rw-r--r--chromecast/shell/browser/android/cast_window_android.cc130
-rw-r--r--chromecast/shell/browser/android/cast_window_android.h87
-rw-r--r--chromecast/shell/browser/android/cast_window_manager.cc88
-rw-r--r--chromecast/shell/browser/android/cast_window_manager.h39
-rw-r--r--chromecast/shell/browser/cast_browser_main_parts.cc14
-rw-r--r--chromecast/shell/browser/cast_content_browser_client.cc17
-rw-r--r--chromecast/shell/browser/cast_http_user_agent_settings.cc11
-rw-r--r--chromecast/shell/browser/url_request_context_factory.cc18
-rw-r--r--chromecast/shell/browser/url_request_context_factory.h2
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
new file mode 100644
index 0000000..72d8318
--- /dev/null
+++ b/chromecast/shell/android/apk/res/mipmap-hdpi/app_icon.png
Binary files differ
diff --git a/chromecast/shell/android/apk/res/mipmap-mdpi/app_icon.png b/chromecast/shell/android/apk/res/mipmap-mdpi/app_icon.png
new file mode 100644
index 0000000..4076d5d
--- /dev/null
+++ b/chromecast/shell/android/apk/res/mipmap-mdpi/app_icon.png
Binary files differ
diff --git a/chromecast/shell/android/apk/res/mipmap-xhdpi/app_icon.png b/chromecast/shell/android/apk/res/mipmap-xhdpi/app_icon.png
new file mode 100644
index 0000000..c7f092c
--- /dev/null
+++ b/chromecast/shell/android/apk/res/mipmap-xhdpi/app_icon.png
Binary files differ
diff --git a/chromecast/shell/android/apk/res/mipmap-xxhdpi/app_icon.png b/chromecast/shell/android/apk/res/mipmap-xxhdpi/app_icon.png
new file mode 100644
index 0000000..9addc3b
--- /dev/null
+++ b/chromecast/shell/android/apk/res/mipmap-xxhdpi/app_icon.png
Binary files differ
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_;