diff options
author | mkosiba@chromium.org <mkosiba@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-01-31 21:20:16 +0000 |
---|---|---|
committer | mkosiba@chromium.org <mkosiba@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-01-31 21:20:16 +0000 |
commit | c0d243a6c23df9983e1ba039bbe760d0cf047ca5 (patch) | |
tree | f3bbe0a34e3c59f9834a6aba3da4140d2c267e94 | |
parent | 87eca1838758e781bd4359a8d675bc316b8ee366 (diff) | |
download | chromium_src-c0d243a6c23df9983e1ba039bbe760d0cf047ca5.zip chromium_src-c0d243a6c23df9983e1ba039bbe760d0cf047ca5.tar.gz chromium_src-c0d243a6c23df9983e1ba039bbe760d0cf047ca5.tar.bz2 |
Use a struct to pass parameters in the navigation interception callback.
The number of parameters in the InterceptNavigationResourceThrottle
has gone over the maximum size supported by base::Bind.
BUG=None
TEST=components_unittests,AndroidWebViewTests
TBR=joi@chromium.org
Review URL: https://chromiumcodereview.appspot.com/12082049
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@179956 0039d316-1c4b-4281-b951-d872f2087c98
13 files changed, 232 insertions, 93 deletions
diff --git a/android_webview/java/src/org/chromium/android_webview/AwContents.java b/android_webview/java/src/org/chromium/android_webview/AwContents.java index da14d1a..9d98d6e 100644 --- a/android_webview/java/src/org/chromium/android_webview/AwContents.java +++ b/android_webview/java/src/org/chromium/android_webview/AwContents.java @@ -33,6 +33,7 @@ import org.chromium.content.browser.NavigationHistory; import org.chromium.content.browser.PageTransitionTypes; import org.chromium.content.common.CleanupReference; import org.chromium.components.navigation_interception.InterceptNavigationDelegate; +import org.chromium.components.navigation_interception.NavigationParams; import org.chromium.net.GURLUtils; import org.chromium.net.X509Util; import org.chromium.ui.gfx.DeviceDisplayInfo; @@ -177,8 +178,8 @@ public class AwContents { } @Override - public boolean shouldIgnoreNavigation(String url, boolean isPost, boolean hasUserGesture, - int pageTransition) { + public boolean shouldIgnoreNavigation(NavigationParams navigationParams) { + final String url = navigationParams.url; boolean ignoreNavigation = false; if (mLastLoadUrlAddress != null && mLastLoadUrlAddress.equals(url)) { // Support the case where the user clicks on a link that takes them back to the @@ -189,7 +190,7 @@ public class AwContents { // do not offer it to AwContentsClient.shouldIgnoreNavigation. // The embedder is also not allowed to intercept POST requests because of // crbug.com/155250. - } else if (!isPost) { + } else if (!navigationParams.isPost) { ignoreNavigation = AwContents.this.mContentsClient.shouldIgnoreNavigation(url); } diff --git a/components/navigation_interception.gypi b/components/navigation_interception.gypi index 4912929..8db0e1a 100644 --- a/components/navigation_interception.gypi +++ b/components/navigation_interception.gypi @@ -26,6 +26,8 @@ 'sources': [ 'navigation_interception/intercept_navigation_resource_throttle.cc', 'navigation_interception/intercept_navigation_resource_throttle.h', + 'navigation_interception/navigation_params.h', + 'navigation_interception/navigation_params.cc', ], 'conditions': [ ['OS=="android"', { @@ -37,6 +39,8 @@ 'navigation_interception/component_jni_registrar.h', 'navigation_interception/intercept_navigation_delegate.cc', 'navigation_interception/intercept_navigation_delegate.h', + 'navigation_interception/navigation_params_android.h', + 'navigation_interception/navigation_params_android.cc', ], }], ], @@ -62,6 +66,7 @@ 'type': 'none', 'sources': [ 'navigation_interception/android/java/src/org/chromium/content/components/navigation_interception/InterceptNavigationDelegate.java', + 'navigation_interception/android/java/src/org/chromium/content/components/navigation_interception/NavigationParams.java', ], 'variables': { 'jni_gen_dir': 'navigation_interception', diff --git a/components/navigation_interception/android/java/src/org/chromium/content/components/navigation_interception/InterceptNavigationDelegate.java b/components/navigation_interception/android/java/src/org/chromium/content/components/navigation_interception/InterceptNavigationDelegate.java index 4f3938e..f214467 100644 --- a/components/navigation_interception/android/java/src/org/chromium/content/components/navigation_interception/InterceptNavigationDelegate.java +++ b/components/navigation_interception/android/java/src/org/chromium/content/components/navigation_interception/InterceptNavigationDelegate.java @@ -7,18 +7,15 @@ package org.chromium.components.navigation_interception; import org.chromium.base.CalledByNative; public interface InterceptNavigationDelegate { + /** * This method is called for every top-level navigation within the associated WebContents. * The method allows the embedder to ignore navigations. This is used on Android to 'convert' * certain navigations to Intents to 3rd party applications. * - * @param url the target url of the navigation. - * @param isPost true if the the navigation method is "POST". - * @param isUserGesture true if the navigation was initiated by the user. - * @param pageTransition is the page transition type (e.g. link / typed). + * @param navigationParams parameters describing the navigation. * @return true if the navigation should be ignored. */ @CalledByNative - boolean shouldIgnoreNavigation(String url, boolean isPost, boolean isUserGesture, - int pageTransition); + boolean shouldIgnoreNavigation(NavigationParams navigationParams); } diff --git a/components/navigation_interception/component_jni_registrar.cc b/components/navigation_interception/component_jni_registrar.cc index 9d2c30e..c3fe287 100644 --- a/components/navigation_interception/component_jni_registrar.cc +++ b/components/navigation_interception/component_jni_registrar.cc @@ -7,11 +7,13 @@ #include "base/android/jni_android.h" #include "base/android/jni_registrar.h" #include "components/navigation_interception/intercept_navigation_delegate.h" +#include "components/navigation_interception/navigation_params_android.h" namespace components { static base::android::RegistrationMethod kComponentRegisteredMethods[] = { { "InterceptNavigationDelegate", RegisterInterceptNavigationDelegate }, + { "NavigationParams", RegisterNavigationParams }, }; bool RegisterNavigationInterceptionJni(JNIEnv* env) { diff --git a/components/navigation_interception/intercept_navigation_delegate.cc b/components/navigation_interception/intercept_navigation_delegate.cc index a0cbc86..1a1df05 100644 --- a/components/navigation_interception/intercept_navigation_delegate.cc +++ b/components/navigation_interception/intercept_navigation_delegate.cc @@ -8,6 +8,7 @@ #include "base/android/jni_string.h" #include "base/callback.h" #include "components/navigation_interception/intercept_navigation_resource_throttle.h" +#include "components/navigation_interception/navigation_params_android.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/web_contents.h" @@ -30,11 +31,7 @@ const void* kInterceptNavigationDelegateUserDataKey = &kInterceptNavigationDelegateUserDataKey; bool CheckIfShouldIgnoreNavigationOnUIThread(RenderViewHost* source, - const GURL& url, - const content::Referrer& referrer, - bool is_post, - bool has_user_gesture, - PageTransition transition_type) { + const NavigationParams& params) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); DCHECK(source); @@ -46,8 +43,7 @@ bool CheckIfShouldIgnoreNavigationOnUIThread(RenderViewHost* source, if (!intercept_navigation_delegate) return false; - return intercept_navigation_delegate->ShouldIgnoreNavigation( - url, is_post, has_user_gesture, transition_type); + return intercept_navigation_delegate->ShouldIgnoreNavigation(params); } } // namespace @@ -83,11 +79,8 @@ InterceptNavigationDelegate::~InterceptNavigationDelegate() { } bool InterceptNavigationDelegate::ShouldIgnoreNavigation( - const GURL& url, - bool is_post, - bool has_user_gesture, - PageTransition transition_type) { - if (!url.is_valid()) + const NavigationParams& navigation_params) { + if (!navigation_params.url().is_valid()) return false; JNIEnv* env = base::android::AttachCurrentThread(); @@ -96,15 +89,13 @@ bool InterceptNavigationDelegate::ShouldIgnoreNavigation( if (jdelegate.is_null()) return false; - ScopedJavaLocalRef<jstring> jstring_url = - ConvertUTF8ToJavaString(env, url.spec()); + ScopedJavaLocalRef<jobject> jobject_params = + CreateJavaNavigationParams(env, navigation_params); + return Java_InterceptNavigationDelegate_shouldIgnoreNavigation( env, jdelegate.obj(), - jstring_url.obj(), - is_post, - has_user_gesture, - transition_type); + jobject_params.obj()); } // Register native methods. diff --git a/components/navigation_interception/intercept_navigation_delegate.h b/components/navigation_interception/intercept_navigation_delegate.h index 66a2518..a3d14ce 100644 --- a/components/navigation_interception/intercept_navigation_delegate.h +++ b/components/navigation_interception/intercept_navigation_delegate.h @@ -23,6 +23,8 @@ class URLRequest; namespace components { +class NavigationParams; + // Native side of the InterceptNavigationDelegate Java interface. // This is used to create a InterceptNavigationResourceThrottle that calls the // Java interface method to determine whether a navigation should be ignored or @@ -54,10 +56,8 @@ class InterceptNavigationDelegate : public base::SupportsUserData::Data { static content::ResourceThrottle* CreateThrottleFor( net::URLRequest* request); - virtual bool ShouldIgnoreNavigation(const GURL& url, - bool is_post, - bool has_user_gesture, - content::PageTransition transition_type); + virtual bool ShouldIgnoreNavigation( + const NavigationParams& navigation_params); private: JavaObjectWeakGlobalRef weak_jdelegate_; }; diff --git a/components/navigation_interception/intercept_navigation_resource_throttle.cc b/components/navigation_interception/intercept_navigation_resource_throttle.cc index 22f1244..3d58ae3 100644 --- a/components/navigation_interception/intercept_navigation_resource_throttle.cc +++ b/components/navigation_interception/intercept_navigation_resource_throttle.cc @@ -4,6 +4,7 @@ #include "components/navigation_interception/intercept_navigation_resource_throttle.h" +#include "components/navigation_interception/navigation_params.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/child_process_security_policy.h" #include "content/public/browser/render_process_host.h" @@ -25,37 +26,25 @@ namespace components { namespace { -struct ShouldIgnoreCallbackParams { - int render_process_id; - int render_view_id; - GURL url; - Referrer referrer; - bool has_user_gesture; - bool is_post; - PageTransition transition_type; -}; - void CheckIfShouldIgnoreNavigationOnUIThread( - const ShouldIgnoreCallbackParams& params, + int render_process_id, + int render_view_id, + const NavigationParams& navigation_params, InterceptNavigationResourceThrottle::CheckOnUIThreadCallback should_ignore_callback, base::Callback<void(bool)> callback) { bool should_ignore_navigation = false; RenderViewHost* rvh = - RenderViewHost::FromID(params.render_process_id, params.render_view_id); + RenderViewHost::FromID(render_process_id, render_view_id); if (rvh) { - GURL validated_url(params.url); - RenderViewHost::FilterURL(rvh->GetProcess(), false, &validated_url); - - should_ignore_navigation = should_ignore_callback.Run( - rvh, - validated_url, - params.referrer, - params.is_post, - params.has_user_gesture, - params.transition_type); + NavigationParams validated_params(navigation_params); + RenderViewHost::FilterURL( + rvh->GetProcess(), false, &validated_params.url()); + + should_ignore_navigation = should_ignore_callback.Run(rvh, + validated_params); } BrowserThread::PostTask( @@ -99,26 +88,22 @@ bool InterceptNavigationResourceThrottle::CheckIfShouldIgnoreNavigation( if (!info->GetAssociatedRenderView(&render_process_id, &render_view_id)) return false; - ShouldIgnoreCallbackParams params; - params.render_process_id = render_process_id; - params.render_view_id = render_view_id; - params.url = url; - params.referrer = Referrer(GURL(request_->referrer()), - info->GetReferrerPolicy()); - params.has_user_gesture = info->HasUserGesture(); - params.is_post = request_->method() == "POST"; - params.transition_type = info->GetPageTransition(); - if (is_redirect) { - uint32 transition = params.transition_type | - content::PAGE_TRANSITION_SERVER_REDIRECT; - params.transition_type = static_cast<content::PageTransition>(transition); - } + NavigationParams navigation_params(url, + Referrer(GURL(request_->referrer()), + info->GetReferrerPolicy()), + info->HasUserGesture(), + request_->method() == "POST", + info->GetPageTransition(), + is_redirect); + BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind( &CheckIfShouldIgnoreNavigationOnUIThread, - params, + render_process_id, + render_view_id, + navigation_params, should_ignore_callback_, base::Bind( &InterceptNavigationResourceThrottle::OnResultObtained, diff --git a/components/navigation_interception/intercept_navigation_resource_throttle.h b/components/navigation_interception/intercept_navigation_resource_throttle.h index ee9cc41..ab19556 100644 --- a/components/navigation_interception/intercept_navigation_resource_throttle.h +++ b/components/navigation_interception/intercept_navigation_resource_throttle.h @@ -10,13 +10,11 @@ #include "base/callback.h" #include "base/memory/weak_ptr.h" #include "content/public/browser/resource_throttle.h" -#include "content/public/common/page_transition_types.h" class GURL; namespace content { class RenderViewHost; -struct Referrer; } namespace net { @@ -25,17 +23,15 @@ class URLRequest; namespace components { +class NavigationParams; + // This class allows the provider of the Callback to selectively ignore top // level navigations. class InterceptNavigationResourceThrottle : public content::ResourceThrottle { public: - typedef base::Callback< - bool(content::RenderViewHost* /* source */, - const GURL& /* url */, - const content::Referrer& /*referrer*/, - bool /* is_post */, - bool /* has_user_gesture */, - content::PageTransition /* page transition type */)> + typedef base::Callback<bool( + content::RenderViewHost* /* source */, + const NavigationParams& /* navigation_params */)> CheckOnUIThreadCallback; InterceptNavigationResourceThrottle( diff --git a/components/navigation_interception/intercept_navigation_resource_throttle_unittest.cc b/components/navigation_interception/intercept_navigation_resource_throttle_unittest.cc index 8d9943c..7ca73e7 100644 --- a/components/navigation_interception/intercept_navigation_resource_throttle_unittest.cc +++ b/components/navigation_interception/intercept_navigation_resource_throttle_unittest.cc @@ -8,6 +8,7 @@ #include "base/memory/scoped_vector.h" #include "base/synchronization/waitable_event.h" #include "components/navigation_interception/intercept_navigation_resource_throttle.h" +#include "components/navigation_interception/navigation_params.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/resource_context.h" #include "content/public/browser/resource_controller.h" @@ -27,6 +28,7 @@ using testing::_; using testing::Eq; +using testing::Property; using testing::Ne; using testing::Return; @@ -42,19 +44,30 @@ void ContinueTestCase() { content::BrowserThread::UI, FROM_HERE, MessageLoop::QuitClosure()); } +// The MS C++ compiler complains about not being able to resolve which url() +// method (const or non-const) to use if we use the Property matcher to check +// the return value of the NavigationParams::url() method. +// It is possible to suppress the error by specifying the types directly but +// that results in very ugly syntax, which is why these custom matchers are +// used instead. +MATCHER(NavigationParamsUrlIsTest, "") { + return arg.url() == GURL(kTestUrl); +} + +MATCHER(NavigationParamsUrlIsSafe, "") { + return arg.url() != GURL(kUnsafeTestUrl); +} + } // namespace + // MockInterceptCallbackReceiver ---------------------------------------------- class MockInterceptCallbackReceiver { public: - MOCK_METHOD6(ShouldIgnoreNavigation, + MOCK_METHOD2(ShouldIgnoreNavigation, bool(content::RenderViewHost* source, - const GURL& url, - const content::Referrer& referrer, - bool is_post, - bool has_user_gesture, - content::PageTransition page_transition)); + const NavigationParams& navigation_params)); }; // MockResourceController ----------------------------------------------------- @@ -205,11 +218,10 @@ class InterceptNavigationResourceThrottleTest ShouldIgnoreNavigationCallbackAction callback_action, bool* defer) { - ON_CALL(*mock_callback_receiver_, - ShouldIgnoreNavigation(_, _, _, _, _, _)) + ON_CALL(*mock_callback_receiver_, ShouldIgnoreNavigation(_, _)) .WillByDefault(Return(callback_action == IgnoreNavigation)); EXPECT_CALL(*mock_callback_receiver_, - ShouldIgnoreNavigation(rvh(), Eq(GURL(kTestUrl)), _, _, _, _)) + ShouldIgnoreNavigation(rvh(), NavigationParamsUrlIsTest())) .Times(1); content::BrowserThread::PostTask( @@ -281,7 +293,7 @@ TEST_F(InterceptNavigationResourceThrottleTest, base::Unretained(this))); EXPECT_CALL(*mock_callback_receiver_, - ShouldIgnoreNavigation(_, _, _, _, _, _)) + ShouldIgnoreNavigation(_, _)) .Times(0); content::BrowserThread::PostTask( @@ -336,10 +348,10 @@ TEST_F(InterceptNavigationResourceThrottleTest, bool defer = false; ON_CALL(*mock_callback_receiver_, - ShouldIgnoreNavigation(_, Ne(GURL(kUnsafeTestUrl)), _, _, _, _)) + ShouldIgnoreNavigation(_, NavigationParamsUrlIsSafe())) .WillByDefault(Return(false)); EXPECT_CALL(*mock_callback_receiver_, - ShouldIgnoreNavigation(_, Ne(GURL(kUnsafeTestUrl)), _, _, _, _)) + ShouldIgnoreNavigation(_, NavigationParamsUrlIsSafe())) .Times(1); content::BrowserThread::PostTask( @@ -364,8 +376,9 @@ TEST_F(InterceptNavigationResourceThrottleTest, bool defer = false; EXPECT_CALL(*mock_callback_receiver_, - ShouldIgnoreNavigation(_, Ne(GURL(kUnsafeTestUrl)), _, false, _, - _)) + ShouldIgnoreNavigation(_, AllOf( + NavigationParamsUrlIsSafe(), + Property(&NavigationParams::is_post, Eq(false))))) .WillOnce(Return(false)); content::BrowserThread::PostTask( @@ -390,8 +403,9 @@ TEST_F(InterceptNavigationResourceThrottleTest, bool defer = false; EXPECT_CALL(*mock_callback_receiver_, - ShouldIgnoreNavigation(_, Ne(GURL(kUnsafeTestUrl)), _, true, _, - _)) + ShouldIgnoreNavigation(_, AllOf( + NavigationParamsUrlIsSafe(), + Property(&NavigationParams::is_post, Eq(true))))) .WillOnce(Return(false)); content::BrowserThread::PostTask( diff --git a/components/navigation_interception/navigation_params.cc b/components/navigation_interception/navigation_params.cc new file mode 100644 index 0000000..48d8fb5 --- /dev/null +++ b/components/navigation_interception/navigation_params.cc @@ -0,0 +1,42 @@ +// Copyright (c) 2013 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 "components/navigation_interception/navigation_params.h" + +namespace components { + +NavigationParams::NavigationParams(const NavigationParams& other) { + Assign(other); +} + +NavigationParams::NavigationParams( + const GURL& url, + const content::Referrer& referrer, + bool has_user_gesture, + bool is_post, + content::PageTransition transition_type, + bool is_redirect) + : url_(url), + referrer_(referrer), + has_user_gesture_(has_user_gesture), + is_post_(is_post), + transition_type_(transition_type), + is_redirect_(is_redirect) { +} + +void NavigationParams::operator=(const NavigationParams& rhs) { + Assign(rhs); +} + +void NavigationParams::Assign(const NavigationParams& other) { + url_ = other.url(); + referrer_ = other.referrer(); + has_user_gesture_ = other.has_user_gesture(); + is_post_ = other.is_post(); + transition_type_ = other.transition_type(); + is_redirect_ = other.is_redirect(); +} + +} // namespace components + diff --git a/components/navigation_interception/navigation_params.h b/components/navigation_interception/navigation_params.h new file mode 100644 index 0000000..a0810d4 --- /dev/null +++ b/components/navigation_interception/navigation_params.h @@ -0,0 +1,48 @@ +// Copyright (c) 2013 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 COMPONENTS_NAVIGATION_INTERCEPTION_NAVIGATION_PARAMS_H_ +#define COMPONENTS_NAVIGATION_INTERCEPTION_NAVIGATION_PARAMS_H_ + +#include "content/public/common/page_transition_types.h" +#include "content/public/common/page_transition_types.h" +#include "content/public/common/referrer.h" +#include "googleurl/src/gurl.h" + +namespace components { + +class NavigationParams { + public: + NavigationParams(const GURL& url, + const content::Referrer& referrer, + bool has_user_gesture, + bool is_post, + content::PageTransition page_transition_type, + bool is_redirect); + NavigationParams(const NavigationParams& other); + void operator=(const NavigationParams& rhs); + + const GURL& url() const { return url_; } + GURL& url() { return url_; } + const content::Referrer& referrer() const { return referrer_; } + bool has_user_gesture() const { return has_user_gesture_; } + bool is_post() const { return is_post_; } + content::PageTransition transition_type() const { return transition_type_; } + bool is_redirect() const { return is_redirect_; } + + private: + GURL url_; + content::Referrer referrer_; + bool has_user_gesture_; + bool is_post_; + content::PageTransition transition_type_; + bool is_redirect_; + + void Assign(const NavigationParams& other); +}; + +} // namespace components + +#endif // COMPONENTS_NAVIGATION_INTERCEPTION_NAVIGATION_PARAMS_H_ + diff --git a/components/navigation_interception/navigation_params_android.cc b/components/navigation_interception/navigation_params_android.cc new file mode 100644 index 0000000..8e8c4dd --- /dev/null +++ b/components/navigation_interception/navigation_params_android.cc @@ -0,0 +1,34 @@ +// Copyright (c) 2013 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 "components/navigation_interception/navigation_params_android.h" + +#include "base/android/jni_string.h" +#include "jni/NavigationParams_jni.h" + +using base::android::ConvertUTF8ToJavaString; + +namespace components { + +base::android::ScopedJavaLocalRef<jobject> CreateJavaNavigationParams( + JNIEnv* env, + const NavigationParams& params) { + ScopedJavaLocalRef<jstring> jstring_url = + ConvertUTF8ToJavaString(env, params.url().spec()); + + return Java_NavigationParams_create(env, + jstring_url.obj(), + params.is_post(), + params.has_user_gesture(), + params.transition_type(), + params.is_redirect()); +} + +// Register native methods. + +bool RegisterNavigationParams(JNIEnv* env) { + return RegisterNativesImpl(env); +} + +} // namespace components diff --git a/components/navigation_interception/navigation_params_android.h b/components/navigation_interception/navigation_params_android.h new file mode 100644 index 0000000..e1523c5 --- /dev/null +++ b/components/navigation_interception/navigation_params_android.h @@ -0,0 +1,24 @@ +// Copyright (c) 2013 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 COMPONENTS_NAVIGATION_INTERCEPTION_NAVIGATION_PARAMS_ANDROID_H_ +#define COMPONENTS_NAVIGATION_INTERCEPTION_NAVIGATION_PARAMS_ANDROID_H_ + +#include "base/android/jni_android.h" +#include "base/android/scoped_java_ref.h" +#include "components/navigation_interception/navigation_params.h" + +class GURL; + +namespace components { + +base::android::ScopedJavaLocalRef<jobject> CreateJavaNavigationParams( + JNIEnv* env, + const NavigationParams& params); + +bool RegisterNavigationParams(JNIEnv* env); + +} // namespace components + +#endif // COMPONENTS_NAVIGATION_INTERCEPTION_NAVIGATION_PARAMS_ANDROID_H_ |