|  | // Copyright (c) 2012 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/scoped_java_ref.h" | 
|  |  | 
|  | #include "base/android/jni_android.h" | 
|  | #include "base/android/jni_string.h" | 
|  | #include "testing/gtest/include/gtest/gtest.h" | 
|  |  | 
|  | namespace base { | 
|  | namespace android { | 
|  |  | 
|  | namespace { | 
|  | int g_local_refs = 0; | 
|  | int g_global_refs = 0; | 
|  |  | 
|  | const JNINativeInterface* g_previous_functions; | 
|  |  | 
|  | jobject NewGlobalRef(JNIEnv* env, jobject obj) { | 
|  | ++g_global_refs; | 
|  | return g_previous_functions->NewGlobalRef(env, obj); | 
|  | } | 
|  |  | 
|  | void DeleteGlobalRef(JNIEnv* env, jobject obj) { | 
|  | --g_global_refs; | 
|  | return g_previous_functions->DeleteGlobalRef(env, obj); | 
|  | } | 
|  |  | 
|  | jobject NewLocalRef(JNIEnv* env, jobject obj) { | 
|  | ++g_local_refs; | 
|  | return g_previous_functions->NewLocalRef(env, obj); | 
|  | } | 
|  |  | 
|  | void DeleteLocalRef(JNIEnv* env, jobject obj) { | 
|  | --g_local_refs; | 
|  | return g_previous_functions->DeleteLocalRef(env, obj); | 
|  | } | 
|  | }  // namespace | 
|  |  | 
|  | class ScopedJavaRefTest : public testing::Test { | 
|  | protected: | 
|  | void SetUp() override { | 
|  | g_local_refs = 0; | 
|  | g_global_refs = 0; | 
|  | JNIEnv* env = AttachCurrentThread(); | 
|  | g_previous_functions = env->functions; | 
|  | hooked_functions = *g_previous_functions; | 
|  | env->functions = &hooked_functions; | 
|  | // We inject our own functions in JNINativeInterface so we can keep track | 
|  | // of the reference counting ourselves. | 
|  | hooked_functions.NewGlobalRef = &NewGlobalRef; | 
|  | hooked_functions.DeleteGlobalRef = &DeleteGlobalRef; | 
|  | hooked_functions.NewLocalRef = &NewLocalRef; | 
|  | hooked_functions.DeleteLocalRef = &DeleteLocalRef; | 
|  | } | 
|  |  | 
|  | void TearDown() override { | 
|  | JNIEnv* env = AttachCurrentThread(); | 
|  | env->functions = g_previous_functions; | 
|  | } | 
|  | // From JellyBean release, the instance of this struct provided in JNIEnv is | 
|  | // read-only, so we deep copy it to allow individual functions to be hooked. | 
|  | JNINativeInterface hooked_functions; | 
|  | }; | 
|  |  | 
|  | // The main purpose of this is testing the various conversions compile. | 
|  | TEST_F(ScopedJavaRefTest, Conversions) { | 
|  | JNIEnv* env = AttachCurrentThread(); | 
|  | ScopedJavaLocalRef<jstring> str = ConvertUTF8ToJavaString(env, "string"); | 
|  | ScopedJavaGlobalRef<jstring> global(str); | 
|  | { | 
|  | ScopedJavaGlobalRef<jobject> global_obj(str); | 
|  | ScopedJavaLocalRef<jobject> local_obj(global); | 
|  | const JavaRef<jobject>& obj_ref1(str); | 
|  | const JavaRef<jobject>& obj_ref2(global); | 
|  | EXPECT_TRUE(env->IsSameObject(obj_ref1.obj(), obj_ref2.obj())); | 
|  | EXPECT_TRUE(env->IsSameObject(global_obj.obj(), obj_ref2.obj())); | 
|  | } | 
|  | global.Reset(str); | 
|  | const JavaRef<jstring>& str_ref = str; | 
|  | EXPECT_EQ("string", ConvertJavaStringToUTF8(str_ref)); | 
|  | str.Reset(); | 
|  | } | 
|  |  | 
|  | TEST_F(ScopedJavaRefTest, RefCounts) { | 
|  | JNIEnv* env = AttachCurrentThread(); | 
|  | ScopedJavaLocalRef<jstring> str; | 
|  | // The ConvertJavaStringToUTF8 below creates a new string that would normally | 
|  | // return a local ref. We simulate that by starting the g_local_refs count at | 
|  | // 1. | 
|  | g_local_refs = 1; | 
|  | str.Reset(ConvertUTF8ToJavaString(env, "string")); | 
|  | EXPECT_EQ(1, g_local_refs); | 
|  | EXPECT_EQ(0, g_global_refs); | 
|  | { | 
|  | ScopedJavaGlobalRef<jstring> global_str(str); | 
|  | ScopedJavaGlobalRef<jobject> global_obj(global_str); | 
|  | EXPECT_EQ(1, g_local_refs); | 
|  | EXPECT_EQ(2, g_global_refs); | 
|  |  | 
|  | ScopedJavaLocalRef<jstring> str2(env, str.Release()); | 
|  | EXPECT_EQ(1, g_local_refs); | 
|  | { | 
|  | ScopedJavaLocalRef<jstring> str3(str2); | 
|  | EXPECT_EQ(2, g_local_refs); | 
|  | } | 
|  | EXPECT_EQ(1, g_local_refs); | 
|  | str2.Reset(); | 
|  | EXPECT_EQ(0, g_local_refs); | 
|  | global_str.Reset(); | 
|  | EXPECT_EQ(1, g_global_refs); | 
|  | ScopedJavaGlobalRef<jobject> global_obj2(global_obj); | 
|  | EXPECT_EQ(2, g_global_refs); | 
|  | } | 
|  |  | 
|  | EXPECT_EQ(0, g_local_refs); | 
|  | EXPECT_EQ(0, g_global_refs); | 
|  | } | 
|  |  | 
|  | }  // namespace android | 
|  | }  // namespace base |