diff --git a/webrtc/base/BUILD.gn b/webrtc/base/BUILD.gn index 45f0718402..e94169d0ce 100644 --- a/webrtc/base/BUILD.gn +++ b/webrtc/base/BUILD.gn @@ -979,6 +979,7 @@ if (rtc_include_tests) { if (is_android) { android_library("base_java") { java_files = [ + "java/src/org/webrtc/ContextUtils.java", "java/src/org/webrtc/Logging.java", "java/src/org/webrtc/Size.java", "java/src/org/webrtc/ThreadUtils.java", diff --git a/webrtc/base/java/src/org/webrtc/ContextUtils.java b/webrtc/base/java/src/org/webrtc/ContextUtils.java new file mode 100644 index 0000000000..3b9a652fed --- /dev/null +++ b/webrtc/base/java/src/org/webrtc/ContextUtils.java @@ -0,0 +1,42 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +package org.webrtc; + +import android.content.Context; + +/** + * Class for storing the application context and retrieving it in a static context. Similar to + * org.chromium.base.ContextUtils. + */ +public class ContextUtils { + private static Context applicationContext; + + /** + * Stores the application context that will be returned by getApplicationContext. This is called + * by PeerConnectionFactory.initializeAndroidGlobals. + */ + public static void initialize(Context applicationContext) { + if (ContextUtils.applicationContext != null) { + throw new RuntimeException("Multiple ContextUtils.initialize calls."); + } + if (applicationContext == null) { + throw new RuntimeException("Application context cannot be null for ContextUtils.initialize."); + } + ContextUtils.applicationContext = applicationContext; + } + + /** + * Returns the stored application context. + */ + public static Context getApplicationContext() { + return applicationContext; + } +} diff --git a/webrtc/modules/audio_device/android/audio_manager.cc b/webrtc/modules/audio_device/android/audio_manager.cc index 6925c3d614..d56e9c1bef 100644 --- a/webrtc/modules/audio_device/android/audio_manager.cc +++ b/webrtc/modules/audio_device/android/audio_manager.cc @@ -83,11 +83,10 @@ AudioManager::AudioManager() j_native_registration_ = j_environment_->RegisterNatives( "org/webrtc/voiceengine/WebRtcAudioManager", native_methods, arraysize(native_methods)); - j_audio_manager_.reset(new JavaAudioManager( - j_native_registration_.get(), - j_native_registration_->NewObject( - "", "(Landroid/content/Context;J)V", - JVM::GetInstance()->context(), PointerTojlong(this)))); + j_audio_manager_.reset( + new JavaAudioManager(j_native_registration_.get(), + j_native_registration_->NewObject( + "", "(J)V", PointerTojlong(this)))); } AudioManager::~AudioManager() { diff --git a/webrtc/modules/audio_device/android/audio_record_jni.cc b/webrtc/modules/audio_device/android/audio_record_jni.cc index b826ef6883..2a9eacf838 100644 --- a/webrtc/modules/audio_device/android/audio_record_jni.cc +++ b/webrtc/modules/audio_device/android/audio_record_jni.cc @@ -90,11 +90,10 @@ AudioRecordJni::AudioRecordJni(AudioManager* audio_manager) j_native_registration_ = j_environment_->RegisterNatives( "org/webrtc/voiceengine/WebRtcAudioRecord", native_methods, arraysize(native_methods)); - j_audio_record_.reset(new JavaAudioRecord( - j_native_registration_.get(), - j_native_registration_->NewObject( - "", "(Landroid/content/Context;J)V", - JVM::GetInstance()->context(), PointerTojlong(this)))); + j_audio_record_.reset( + new JavaAudioRecord(j_native_registration_.get(), + j_native_registration_->NewObject( + "", "(J)V", PointerTojlong(this)))); // Detach from this thread since we want to use the checker to verify calls // from the Java based audio thread. thread_checker_java_.DetachFromThread(); diff --git a/webrtc/modules/audio_device/android/audio_track_jni.cc b/webrtc/modules/audio_device/android/audio_track_jni.cc index 72e056f368..83c01c969a 100644 --- a/webrtc/modules/audio_device/android/audio_track_jni.cc +++ b/webrtc/modules/audio_device/android/audio_track_jni.cc @@ -89,11 +89,10 @@ AudioTrackJni::AudioTrackJni(AudioManager* audio_manager) j_native_registration_ = j_environment_->RegisterNatives( "org/webrtc/voiceengine/WebRtcAudioTrack", native_methods, arraysize(native_methods)); - j_audio_track_.reset(new JavaAudioTrack( - j_native_registration_.get(), - j_native_registration_->NewObject( - "", "(Landroid/content/Context;J)V", - JVM::GetInstance()->context(), PointerTojlong(this)))); + j_audio_track_.reset( + new JavaAudioTrack(j_native_registration_.get(), + j_native_registration_->NewObject( + "", "(J)V", PointerTojlong(this)))); // Detach from this thread since we want to use the checker to verify calls // from the Java based audio thread. thread_checker_java_.DetachFromThread(); diff --git a/webrtc/modules/audio_device/android/ensure_initialized.cc b/webrtc/modules/audio_device/android/ensure_initialized.cc index 9bc08ab196..a24d53535f 100644 --- a/webrtc/modules/audio_device/android/ensure_initialized.cc +++ b/webrtc/modules/audio_device/android/ensure_initialized.cc @@ -17,7 +17,7 @@ // Note: this dependency is dangerous since it reaches into Chromium's base. // There's a risk of e.g. macro clashes. This file may only be used in tests. RTC_PUSH_IGNORING_WUNDEF() -#include "base/android/context_utils.h" +// #include "base/android/context_utils.h" #include "base/android/jni_android.h" RTC_POP_IGNORING_WUNDEF() #include "webrtc/base/checks.h" @@ -35,10 +35,9 @@ void EnsureInitializedOnce() { JNIEnv* jni = ::base::android::AttachCurrentThread(); JavaVM* jvm = NULL; RTC_CHECK_EQ(0, jni->GetJavaVM(&jvm)); - jobject context = ::base::android::GetApplicationContext().obj(); // Initialize the Java environment (currently only used by the audio manager). - webrtc::JVM::Initialize(jvm, context); + webrtc::JVM::Initialize(jvm); } void EnsureInitialized() { diff --git a/webrtc/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioManager.java b/webrtc/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioManager.java index 24b8ab8340..76ec4c5ede 100644 --- a/webrtc/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioManager.java +++ b/webrtc/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioManager.java @@ -20,6 +20,7 @@ import android.media.AudioTrack; import android.os.Build; import java.util.Timer; import java.util.TimerTask; +import org.webrtc.ContextUtils; import org.webrtc.Logging; // WebRtcAudioManager handles tasks that uses android.media.AudioManager. @@ -135,7 +136,6 @@ public class WebRtcAudioManager { } private final long nativeAudioManager; - private final Context context; private final AudioManager audioManager; private boolean initialized = false; @@ -156,11 +156,11 @@ public class WebRtcAudioManager { private final VolumeLogger volumeLogger; - WebRtcAudioManager(Context context, long nativeAudioManager) { + WebRtcAudioManager(long nativeAudioManager) { Logging.d(TAG, "ctor" + WebRtcAudioUtils.getThreadInfo()); - this.context = context; this.nativeAudioManager = nativeAudioManager; - audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); + audioManager = + (AudioManager) ContextUtils.getApplicationContext().getSystemService(Context.AUDIO_SERVICE); if (DEBUG) { WebRtcAudioUtils.logDeviceInfo(TAG); } @@ -224,12 +224,14 @@ public class WebRtcAudioManager { // Gets the current earpiece state. private boolean hasEarpiece() { - return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY); + return ContextUtils.getApplicationContext().getPackageManager().hasSystemFeature( + PackageManager.FEATURE_TELEPHONY); } // Returns true if low-latency audio output is supported. private boolean isLowLatencyOutputSupported() { - return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUDIO_LOW_LATENCY); + return ContextUtils.getApplicationContext().getPackageManager().hasSystemFeature( + PackageManager.FEATURE_AUDIO_LOW_LATENCY); } // Returns true if low-latency audio input is supported. @@ -248,7 +250,8 @@ public class WebRtcAudioManager { @TargetApi(23) private boolean isProAudioSupported() { return WebRtcAudioUtils.runningOnMarshmallowOrHigher() - && context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUDIO_PRO); + && ContextUtils.getApplicationContext().getPackageManager().hasSystemFeature( + PackageManager.FEATURE_AUDIO_PRO); } // Returns the native output sample rate for this device's output stream. diff --git a/webrtc/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioRecord.java b/webrtc/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioRecord.java index 762a903f0d..ba93ea1fb2 100644 --- a/webrtc/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioRecord.java +++ b/webrtc/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioRecord.java @@ -19,6 +19,7 @@ import android.os.Process; import java.lang.System; import java.nio.ByteBuffer; import java.util.concurrent.TimeUnit; +import org.webrtc.ContextUtils; import org.webrtc.Logging; import org.webrtc.ThreadUtils; @@ -47,7 +48,6 @@ public class WebRtcAudioRecord { private static final long AUDIO_RECORD_THREAD_JOIN_TIMEOUT_MS = 2000; private final long nativeAudioRecord; - private final Context context; private WebRtcAudioEffects effects = null; @@ -139,9 +139,8 @@ public class WebRtcAudioRecord { } } - WebRtcAudioRecord(Context context, long nativeAudioRecord) { + WebRtcAudioRecord(long nativeAudioRecord) { Logging.d(TAG, "ctor" + WebRtcAudioUtils.getThreadInfo()); - this.context = context; this.nativeAudioRecord = nativeAudioRecord; if (DEBUG) { WebRtcAudioUtils.logDeviceInfo(TAG); @@ -169,7 +168,8 @@ public class WebRtcAudioRecord { private int initRecording(int sampleRate, int channels) { Logging.d(TAG, "initRecording(sampleRate=" + sampleRate + ", channels=" + channels + ")"); - if (!WebRtcAudioUtils.hasPermission(context, android.Manifest.permission.RECORD_AUDIO)) { + if (!WebRtcAudioUtils.hasPermission( + ContextUtils.getApplicationContext(), android.Manifest.permission.RECORD_AUDIO)) { reportWebRtcAudioRecordInitError("RECORD_AUDIO permission is missing"); return -1; } diff --git a/webrtc/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioTrack.java b/webrtc/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioTrack.java index 65c52077c8..796a0821c5 100644 --- a/webrtc/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioTrack.java +++ b/webrtc/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioTrack.java @@ -19,6 +19,7 @@ import android.media.AudioTrack; import android.os.Process; import java.lang.Thread; import java.nio.ByteBuffer; +import org.webrtc.ContextUtils; import org.webrtc.Logging; public class WebRtcAudioTrack { @@ -36,7 +37,6 @@ public class WebRtcAudioTrack { // Average number of callbacks per second. private static final int BUFFERS_PER_SECOND = 1000 / CALLBACK_BUFFER_SIZE_MS; - private final Context context; private final long nativeAudioTrack; private final AudioManager audioManager; @@ -165,11 +165,11 @@ public class WebRtcAudioTrack { } } - WebRtcAudioTrack(Context context, long nativeAudioTrack) { + WebRtcAudioTrack(long nativeAudioTrack) { Logging.d(TAG, "ctor" + WebRtcAudioUtils.getThreadInfo()); - this.context = context; this.nativeAudioTrack = nativeAudioTrack; - audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); + audioManager = + (AudioManager) ContextUtils.getApplicationContext().getSystemService(Context.AUDIO_SERVICE); if (DEBUG) { WebRtcAudioUtils.logDeviceInfo(TAG); } diff --git a/webrtc/modules/utility/include/jvm_android.h b/webrtc/modules/utility/include/jvm_android.h index 51a0bd841a..f21d65aaf9 100644 --- a/webrtc/modules/utility/include/jvm_android.h +++ b/webrtc/modules/utility/include/jvm_android.h @@ -118,8 +118,7 @@ class JNIEnvironment { // JNIEnv* jni = ::base::android::AttachCurrentThread(); // JavaVM* jvm = NULL; // jni->GetJavaVM(&jvm); -// jobject context = ::base::android::GetApplicationContext(); -// webrtc::JVM::Initialize(jvm, context); +// webrtc::JVM::Initialize(jvm); // // // Header (.h) file of example class called User. // std::unique_ptr env; @@ -145,9 +144,9 @@ class JNIEnvironment { // JVM::Uninitialize(); class JVM { public: - // Stores global handles to the Java VM interface and the application context. + // Stores global handles to the Java VM interface. // Should be called once on a thread that is attached to the JVM. - static void Initialize(JavaVM* jvm, jobject context); + static void Initialize(JavaVM* jvm); // Clears handles stored in Initialize(). Must be called on same thread as // Initialize(). static void Uninitialize(); @@ -168,10 +167,9 @@ class JVM { // TODO(henrika): can we make these private? JavaVM* jvm() const { return jvm_; } - jobject context() const { return context_; } protected: - JVM(JavaVM* jvm, jobject context); + JVM(JavaVM* jvm); ~JVM(); private: @@ -179,7 +177,6 @@ class JVM { rtc::ThreadChecker thread_checker_; JavaVM* const jvm_; - jobject context_; }; } // namespace webrtc diff --git a/webrtc/modules/utility/source/jvm_android.cc b/webrtc/modules/utility/source/jvm_android.cc index 9d08688700..2228d3ad25 100644 --- a/webrtc/modules/utility/source/jvm_android.cc +++ b/webrtc/modules/utility/source/jvm_android.cc @@ -217,10 +217,10 @@ std::string JNIEnvironment::JavaToStdString(const jstring& j_string) { } // static -void JVM::Initialize(JavaVM* jvm, jobject context) { +void JVM::Initialize(JavaVM* jvm) { ALOGD("JVM::Initialize%s", GetThreadInfo().c_str()); RTC_CHECK(!g_jvm); - g_jvm = new JVM(jvm, context); + g_jvm = new JVM(jvm); } // static @@ -237,11 +237,9 @@ JVM* JVM::GetInstance() { return g_jvm; } -JVM::JVM(JavaVM* jvm, jobject context) - : jvm_(jvm) { +JVM::JVM(JavaVM* jvm) : jvm_(jvm) { ALOGD("JVM::JVM%s", GetThreadInfo().c_str()); RTC_CHECK(jni()) << "AttachCurrentThread() must be called on this thread."; - context_ = NewGlobalRef(jni(), context); LoadClasses(jni()); } @@ -249,7 +247,6 @@ JVM::~JVM() { ALOGD("JVM::~JVM%s", GetThreadInfo().c_str()); RTC_DCHECK(thread_checker_.CalledOnValidThread()); FreeClassReferences(jni()); - DeleteGlobalRef(jni(), context_); } std::unique_ptr JVM::environment() { diff --git a/webrtc/pc/test/androidtestinitializer.cc b/webrtc/pc/test/androidtestinitializer.cc index 3f2ebd4c3b..f070104177 100644 --- a/webrtc/pc/test/androidtestinitializer.cc +++ b/webrtc/pc/test/androidtestinitializer.cc @@ -19,7 +19,6 @@ // Since we use Chromes build system for creating the gtest binary, this should // be fine. RTC_PUSH_IGNORING_WUNDEF() -#include "base/android/context_utils.h" #include "base/android/jni_android.h" RTC_POP_IGNORING_WUNDEF() @@ -41,11 +40,10 @@ void EnsureInitializedOnce() { JNIEnv* jni = ::base::android::AttachCurrentThread(); JavaVM* jvm = NULL; RTC_CHECK_EQ(0, jni->GetJavaVM(&jvm)); - jobject context = ::base::android::GetApplicationContext().obj(); RTC_CHECK(rtc::InitializeSSL()) << "Failed to InitializeSSL()"; - webrtc::JVM::Initialize(jvm, context); + webrtc::JVM::Initialize(jvm); } } // anonymous namespace diff --git a/webrtc/sdk/android/api/org/webrtc/PeerConnectionFactory.java b/webrtc/sdk/android/api/org/webrtc/PeerConnectionFactory.java index 28b3ca72ab..b7d85f04e3 100644 --- a/webrtc/sdk/android/api/org/webrtc/PeerConnectionFactory.java +++ b/webrtc/sdk/android/api/org/webrtc/PeerConnectionFactory.java @@ -10,6 +10,7 @@ package org.webrtc; +import android.content.Context; import java.util.List; /** @@ -30,6 +31,7 @@ public class PeerConnectionFactory { private static final String TAG = "PeerConnectionFactory"; private final long nativeFactory; + private static Context applicationContext; private static Thread networkThread; private static Thread workerThread; private static Thread signalingThread; @@ -52,14 +54,19 @@ public class PeerConnectionFactory { // Must be called at least once before creating a PeerConnectionFactory // (for example, at application startup time). - public static native void initializeAndroidGlobals( - android.content.Context context, boolean videoHwAcceleration); + public static native void nativeInitializeAndroidGlobals( + Context context, boolean videoHwAcceleration); + + public static void initializeAndroidGlobals(Context context, boolean videoHwAcceleration) { + ContextUtils.initialize(context); + nativeInitializeAndroidGlobals(context, videoHwAcceleration); + } // Older signature of initializeAndroidGlobals. The extra parameters are now meaningless. @Deprecated public static boolean initializeAndroidGlobals(Object context, boolean initializeAudio, boolean initializeVideo, boolean videoHwAcceleration) { - initializeAndroidGlobals((android.content.Context) context, videoHwAcceleration); + initializeAndroidGlobals((Context) context, videoHwAcceleration); return true; } diff --git a/webrtc/sdk/android/src/jni/peerconnection_jni.cc b/webrtc/sdk/android/src/jni/peerconnection_jni.cc index c07d58faff..04090f8b61 100644 --- a/webrtc/sdk/android/src/jni/peerconnection_jni.cc +++ b/webrtc/sdk/android/src/jni/peerconnection_jni.cc @@ -1136,7 +1136,7 @@ JOW(jlong, PeerConnectionFactory_nativeCreateObserver)( return (jlong)new PCOJava(jni, j_observer); } -JOW(void, PeerConnectionFactory_initializeAndroidGlobals) +JOW(void, PeerConnectionFactory_nativeInitializeAndroidGlobals) (JNIEnv* jni, jclass, jobject context, @@ -1146,7 +1146,7 @@ JOW(void, PeerConnectionFactory_initializeAndroidGlobals) if (!factory_static_initialized) { RTC_DCHECK(j_application_context == nullptr); j_application_context = NewGlobalRef(jni, context); - webrtc::JVM::Initialize(GetJVM(), context); + webrtc::JVM::Initialize(GetJVM()); factory_static_initialized = true; } } diff --git a/webrtc/test/BUILD.gn b/webrtc/test/BUILD.gn index aa6f322313..a0d262b165 100644 --- a/webrtc/test/BUILD.gn +++ b/webrtc/test/BUILD.gn @@ -532,3 +532,17 @@ rtc_source_set("audio_codec_mocks") { "//testing/gmock", ] } + +if (is_android) { + android_library("native_test_java") { + testonly = true + java_files = [ + "android/org/webrtc/native_test/RTCNativeUnitTest.java", + "android/org/webrtc/native_test/RTCNativeUnitTestActivity.java", + ] + deps = [ + "//testing/android/native_test:native_test_java", + "//webrtc/base:base_java", + ] + } +} diff --git a/webrtc/test/android/AndroidManifest.xml b/webrtc/test/android/AndroidManifest.xml new file mode 100644 index 0000000000..b5157a75df --- /dev/null +++ b/webrtc/test/android/AndroidManifest.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/webrtc/test/android/org/webrtc/native_test/RTCNativeUnitTest.java b/webrtc/test/android/org/webrtc/native_test/RTCNativeUnitTest.java new file mode 100644 index 0000000000..dede7edd1f --- /dev/null +++ b/webrtc/test/android/org/webrtc/native_test/RTCNativeUnitTest.java @@ -0,0 +1,26 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +package org.webrtc.native_test; + +import android.app.Activity; +import org.chromium.native_test.NativeUnitTest; +import org.webrtc.ContextUtils; + +/** + * Native unit test that calls ContextUtils.initialize for WebRTC. + */ +public class RTCNativeUnitTest extends NativeUnitTest { + @Override + public void preCreate(Activity activity) { + super.preCreate(activity); + ContextUtils.initialize(activity.getApplicationContext()); + } +} diff --git a/webrtc/test/android/org/webrtc/native_test/RTCNativeUnitTestActivity.java b/webrtc/test/android/org/webrtc/native_test/RTCNativeUnitTestActivity.java new file mode 100644 index 0000000000..2a413682fe --- /dev/null +++ b/webrtc/test/android/org/webrtc/native_test/RTCNativeUnitTestActivity.java @@ -0,0 +1,34 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +package org.webrtc.native_test; + +import android.app.Activity; +import android.os.Bundle; + +/** + * Activity that uses RTCNativeUnitTest to run the tests. + */ +public class RTCNativeUnitTestActivity extends Activity { + private RTCNativeUnitTest mTest = new RTCNativeUnitTest(); + + @Override + public void onCreate(Bundle savedInstanceState) { + mTest.preCreate(this); + super.onCreate(savedInstanceState); + mTest.postCreate(this); + } + + @Override + public void onStart() { + super.onStart(); + mTest.postStart(this, false); + } +} diff --git a/webrtc/webrtc.gni b/webrtc/webrtc.gni index 5e1c12dd3f..b3ba9b0101 100644 --- a/webrtc/webrtc.gni +++ b/webrtc/webrtc.gni @@ -266,6 +266,10 @@ template("rtc_test") { if (defined(invoker.public_configs)) { public_configs += invoker.public_configs } + if (is_android) { + android_manifest = "//webrtc/test/android/AndroidManifest.xml" + deps += [ "//webrtc/test:native_test_java" ] + } } }