1 /* -*- Mode: c++; c-basic-offset: 2; tab-width: 20; indent-tabs-mode: nil; -*-
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include <android/log.h>
11 #include "mozilla/layers/CompositorBridgeChild.h"
12 #include "mozilla/layers/CompositorBridgeParent.h"
14 #include "mozilla/Hal.h"
15 #include "nsXULAppAPI.h"
17 #include "AndroidBridge.h"
18 #include "AndroidBridgeUtilities.h"
19 #include "AndroidRect.h"
20 #include "nsAlertsUtils.h"
21 #include "nsAppShell.h"
22 #include "nsOSHelperAppService.h"
24 #include "mozilla/Preferences.h"
25 #include "nsThreadUtils.h"
26 #include "gfxPlatform.h"
27 #include "gfxContext.h"
28 #include "mozilla/gfx/2D.h"
30 #include "nsPresContext.h"
31 #include "nsPIDOMWindow.h"
32 #include "mozilla/ClearOnShutdown.h"
33 #include "nsPrintfCString.h"
34 #include "nsContentUtils.h"
36 #include "EventDispatcher.h"
37 #include "MediaCodec.h"
38 #include "SurfaceTexture.h"
39 #include "GLContextProvider.h"
41 #include "mozilla/TimeStamp.h"
42 #include "mozilla/UniquePtr.h"
43 #include "WidgetUtils.h"
45 #include "mozilla/java/EventDispatcherWrappers.h"
46 #include "mozilla/java/GeckoAppShellWrappers.h"
47 #include "mozilla/java/GeckoThreadWrappers.h"
48 #include "mozilla/java/HardwareCodecCapabilityUtilsWrappers.h"
50 using namespace mozilla
;
51 using namespace mozilla::gfx
;
53 AndroidBridge
* AndroidBridge::sBridge
= nullptr;
54 static jobject sGlobalContext
= nullptr;
55 nsTHashMap
<nsStringHashKey
, nsString
> AndroidBridge::sStoragePaths
;
57 jmethodID
AndroidBridge::GetMethodID(JNIEnv
* env
, jclass jClass
,
58 const char* methodName
,
59 const char* methodType
) {
60 jmethodID methodID
= env
->GetMethodID(jClass
, methodName
, methodType
);
63 ">>> FATAL JNI ERROR! GetMethodID(methodName=\"%s\", "
64 "methodType=\"%s\") failed. Did ProGuard optimize away something it "
66 methodName
, methodType
);
67 env
->ExceptionDescribe();
73 jmethodID
AndroidBridge::GetStaticMethodID(JNIEnv
* env
, jclass jClass
,
74 const char* methodName
,
75 const char* methodType
) {
76 jmethodID methodID
= env
->GetStaticMethodID(jClass
, methodName
, methodType
);
79 ">>> FATAL JNI ERROR! GetStaticMethodID(methodName=\"%s\", "
80 "methodType=\"%s\") failed. Did ProGuard optimize away something it "
82 methodName
, methodType
);
83 env
->ExceptionDescribe();
89 jfieldID
AndroidBridge::GetFieldID(JNIEnv
* env
, jclass jClass
,
90 const char* fieldName
,
91 const char* fieldType
) {
92 jfieldID fieldID
= env
->GetFieldID(jClass
, fieldName
, fieldType
);
95 ">>> FATAL JNI ERROR! GetFieldID(fieldName=\"%s\", "
96 "fieldType=\"%s\") failed. Did ProGuard optimize away something it "
98 fieldName
, fieldType
);
99 env
->ExceptionDescribe();
105 jfieldID
AndroidBridge::GetStaticFieldID(JNIEnv
* env
, jclass jClass
,
106 const char* fieldName
,
107 const char* fieldType
) {
108 jfieldID fieldID
= env
->GetStaticFieldID(jClass
, fieldName
, fieldType
);
111 ">>> FATAL JNI ERROR! GetStaticFieldID(fieldName=\"%s\", "
112 "fieldType=\"%s\") failed. Did ProGuard optimize away something it "
114 fieldName
, fieldType
);
115 env
->ExceptionDescribe();
121 void AndroidBridge::ConstructBridge() {
122 /* NSS hack -- bionic doesn't handle recursive unloads correctly,
123 * because library finalizer functions are called with the dynamic
124 * linker lock still held. This results in a deadlock when trying
125 * to call dlclose() while we're already inside dlclose().
126 * Conveniently, NSS has an env var that can prevent it from unloading.
128 putenv(const_cast<char*>("NSS_DISABLE_UNLOAD=1"));
130 MOZ_ASSERT(!sBridge
);
131 sBridge
= new AndroidBridge();
134 void AndroidBridge::DeconstructBridge() {
137 // AndroidBridge destruction requires sBridge to still be valid,
138 // so we set sBridge to nullptr after deleting it.
143 AndroidBridge::~AndroidBridge() {}
145 AndroidBridge::AndroidBridge() {
146 ALOG_BRIDGE("AndroidBridge::Init");
148 JNIEnv
* const jEnv
= jni::GetGeckoThreadEnv();
149 AutoLocalJNIFrame
jniFrame(jEnv
);
151 mMessageQueue
= java::GeckoThread::MsgQueue();
152 auto msgQueueClass
= jni::Class::LocalRef::Adopt(
153 jEnv
, jEnv
->GetObjectClass(mMessageQueue
.Get()));
154 // mMessageQueueNext must not be null
156 GetMethodID(jEnv
, msgQueueClass
.Get(), "next", "()Landroid/os/Message;");
157 // mMessageQueueMessages may be null (e.g. due to proguard optimization)
158 mMessageQueueMessages
= jEnv
->GetFieldID(msgQueueClass
.Get(), "mMessages",
159 "Landroid/os/Message;");
162 bool AndroidBridge::HasHWVP8Encoder() {
163 ALOG_BRIDGE("AndroidBridge::HasHWVP8Encoder");
165 bool value
= java::GeckoAppShell::HasHWVP8Encoder();
170 bool AndroidBridge::HasHWVP8Decoder() {
171 ALOG_BRIDGE("AndroidBridge::HasHWVP8Decoder");
173 bool value
= java::GeckoAppShell::HasHWVP8Decoder();
178 bool AndroidBridge::HasHWH264() {
179 ALOG_BRIDGE("AndroidBridge::HasHWH264");
181 return java::HardwareCodecCapabilityUtils::HasHWH264();
184 gfx::Rect
AndroidBridge::getScreenSize() {
185 ALOG_BRIDGE("AndroidBridge::getScreenSize");
187 java::sdk::Rect::LocalRef screenrect
= java::GeckoAppShell::GetScreenSize();
188 gfx::Rect
screensize(screenrect
->Left(), screenrect
->Top(),
189 screenrect
->Width(), screenrect
->Height());
194 int AndroidBridge::GetScreenDepth() {
195 ALOG_BRIDGE("%s", __PRETTY_FUNCTION__
);
197 static int sDepth
= 0;
198 if (sDepth
) return sDepth
;
200 const int DEFAULT_DEPTH
= 16;
202 if (jni::IsAvailable()) {
203 sDepth
= java::GeckoAppShell::GetScreenDepth();
205 if (!sDepth
) return DEFAULT_DEPTH
;
210 void AndroidBridge::Vibrate(const nsTArray
<uint32_t>& aPattern
) {
211 ALOG_BRIDGE("%s", __PRETTY_FUNCTION__
);
213 uint32_t len
= aPattern
.Length();
215 ALOG_BRIDGE(" invalid 0-length array");
219 // It's clear if this worth special-casing, but it creates less
220 // java junk, so dodges the GC.
222 jlong d
= aPattern
[0];
224 ALOG_BRIDGE(" invalid vibration duration < 0");
227 java::GeckoAppShell::Vibrate(d
);
231 // First element of the array vibrate() expects is how long to wait
232 // *before* vibrating. For us, this is always 0.
234 JNIEnv
* const env
= jni::GetGeckoThreadEnv();
235 AutoLocalJNIFrame
jniFrame(env
, 1);
237 jlongArray array
= env
->NewLongArray(len
+ 1);
239 ALOG_BRIDGE(" failed to allocate array");
243 jlong
* elts
= env
->GetLongArrayElements(array
, nullptr);
245 for (uint32_t i
= 0; i
< aPattern
.Length(); ++i
) {
246 jlong d
= aPattern
[i
];
248 ALOG_BRIDGE(" invalid vibration duration < 0");
249 env
->ReleaseLongArrayElements(array
, elts
, JNI_ABORT
);
254 env
->ReleaseLongArrayElements(array
, elts
, 0);
256 java::GeckoAppShell::Vibrate(jni::LongArray::Ref::From(array
),
257 -1 /* don't repeat */);
260 void AndroidBridge::GetIconForExtension(const nsACString
& aFileExt
,
262 uint8_t* const aBuf
) {
263 ALOG_BRIDGE("AndroidBridge::GetIconForExtension");
264 NS_ASSERTION(aBuf
!= nullptr,
265 "AndroidBridge::GetIconForExtension: aBuf is null!");
268 auto arr
= java::GeckoAppShell::GetIconForExtension(
269 NS_ConvertUTF8toUTF16(aFileExt
), aIconSize
);
273 "AndroidBridge::GetIconForExtension: Returned pixels array is null!");
276 JNIEnv
* const env
= arr
.Env();
277 uint32_t len
= static_cast<uint32_t>(env
->GetArrayLength(arr
.Get()));
278 jbyte
* elements
= env
->GetByteArrayElements(arr
.Get(), 0);
280 uint32_t bufSize
= aIconSize
* aIconSize
* 4;
283 "AndroidBridge::GetIconForExtension: Pixels array is incomplete!");
284 if (len
== bufSize
) memcpy(aBuf
, elements
, bufSize
);
286 env
->ReleaseByteArrayElements(arr
.Get(), elements
, 0);
289 bool AndroidBridge::GetStaticIntField(const char* className
,
290 const char* fieldName
, int32_t* aInt
,
291 JNIEnv
* jEnv
/* = nullptr */) {
292 ALOG_BRIDGE("AndroidBridge::GetStaticIntField %s", fieldName
);
295 if (!jni::IsAvailable()) {
298 jEnv
= jni::GetGeckoThreadEnv();
301 AutoJNIClass
cls(jEnv
, className
);
302 jfieldID field
= cls
.getStaticField(fieldName
, "I");
308 *aInt
= static_cast<int32_t>(jEnv
->GetStaticIntField(cls
.getRawRef(), field
));
312 bool AndroidBridge::GetStaticStringField(const char* className
,
313 const char* fieldName
,
315 JNIEnv
* jEnv
/* = nullptr */) {
316 ALOG_BRIDGE("AndroidBridge::GetStaticStringField %s", fieldName
);
319 if (!jni::IsAvailable()) {
322 jEnv
= jni::GetGeckoThreadEnv();
325 AutoLocalJNIFrame
jniFrame(jEnv
, 1);
326 AutoJNIClass
cls(jEnv
, className
);
327 jfieldID field
= cls
.getStaticField(fieldName
, "Ljava/lang/String;");
333 jstring jstr
= (jstring
)jEnv
->GetStaticObjectField(cls
.getRawRef(), field
);
334 if (!jstr
) return false;
336 result
.Assign(jni::String::Ref::From(jstr
)->ToString());
341 class TracerRunnable
: public Runnable
{
343 TracerRunnable() : Runnable("TracerRunnable") {
344 mTracerLock
= new Mutex("TracerRunnable");
345 mTracerCondVar
= new CondVar(*mTracerLock
, "TracerRunnable");
346 mMainThread
= do_GetMainThread();
349 delete mTracerCondVar
;
351 mTracerLock
= nullptr;
352 mTracerCondVar
= nullptr;
355 virtual nsresult
Run() {
356 MutexAutoLock
lock(*mTracerLock
);
357 if (!AndroidBridge::Bridge()) return NS_OK
;
360 mTracerCondVar
->Notify();
365 if (!mTracerLock
|| !mTracerCondVar
) return false;
366 MutexAutoLock
lock(*mTracerLock
);
368 mMainThread
->Dispatch(this, NS_DISPATCH_NORMAL
);
369 while (!mHasRun
) mTracerCondVar
->Wait();
374 MutexAutoLock
lock(*mTracerLock
);
376 mTracerCondVar
->Notify();
381 CondVar
* mTracerCondVar
;
383 nsCOMPtr
<nsIThread
> mMainThread
;
385 StaticRefPtr
<TracerRunnable
> sTracerRunnable
;
387 bool InitWidgetTracing() {
388 if (!sTracerRunnable
) sTracerRunnable
= new TracerRunnable();
392 void CleanUpWidgetTracing() { sTracerRunnable
= nullptr; }
394 bool FireAndWaitForTracerEvent() {
395 if (sTracerRunnable
) return sTracerRunnable
->Fire();
399 void SignalTracerThread() {
400 if (sTracerRunnable
) return sTracerRunnable
->Signal();
403 } // namespace mozilla
405 void AndroidBridge::GetCurrentBatteryInformation(
406 hal::BatteryInformation
* aBatteryInfo
) {
407 ALOG_BRIDGE("AndroidBridge::GetCurrentBatteryInformation");
409 // To prevent calling too many methods through JNI, the Java method returns
410 // an array of double even if we actually want a double and a boolean.
411 auto arr
= java::GeckoAppShell::GetCurrentBatteryInformation();
413 JNIEnv
* const env
= arr
.Env();
414 if (!arr
|| env
->GetArrayLength(arr
.Get()) != 3) {
418 jdouble
* info
= env
->GetDoubleArrayElements(arr
.Get(), 0);
420 aBatteryInfo
->level() = info
[0];
421 aBatteryInfo
->charging() = info
[1] == 1.0f
;
422 aBatteryInfo
->remainingTime() = info
[2];
424 env
->ReleaseDoubleArrayElements(arr
.Get(), info
, 0);
427 void AndroidBridge::GetCurrentNetworkInformation(
428 hal::NetworkInformation
* aNetworkInfo
) {
429 ALOG_BRIDGE("AndroidBridge::GetCurrentNetworkInformation");
431 // To prevent calling too many methods through JNI, the Java method returns
432 // an array of double even if we actually want an integer, a boolean, and an
435 auto arr
= java::GeckoAppShell::GetCurrentNetworkInformation();
437 JNIEnv
* const env
= arr
.Env();
438 if (!arr
|| env
->GetArrayLength(arr
.Get()) != 3) {
442 jdouble
* info
= env
->GetDoubleArrayElements(arr
.Get(), 0);
444 aNetworkInfo
->type() = info
[0];
445 aNetworkInfo
->isWifi() = info
[1] == 1.0f
;
446 aNetworkInfo
->dhcpGateway() = info
[2];
448 env
->ReleaseDoubleArrayElements(arr
.Get(), info
, 0);
451 jobject
AndroidBridge::GetGlobalContextRef() {
452 // The context object can change, so get a fresh copy every time.
453 auto context
= java::GeckoAppShell::GetApplicationContext();
454 sGlobalContext
= jni::Object::GlobalRef(context
).Forget();
455 MOZ_ASSERT(sGlobalContext
);
456 return sGlobalContext
;
459 /* Implementation file */
460 NS_IMPL_ISUPPORTS(nsAndroidBridge
, nsIAndroidEventDispatcher
, nsIAndroidBridge
)
462 nsAndroidBridge::nsAndroidBridge() {
463 if (jni::IsAvailable()) {
464 RefPtr
<widget::EventDispatcher
> dispatcher
= new widget::EventDispatcher();
465 dispatcher
->Attach(java::EventDispatcher::GetInstance(),
466 /* window */ nullptr);
467 mEventDispatcher
= dispatcher
;
472 nsAndroidBridge::GetDispatcherByName(const char* aName
,
473 nsIAndroidEventDispatcher
** aResult
) {
474 if (!jni::IsAvailable()) {
475 return NS_ERROR_FAILURE
;
478 RefPtr
<widget::EventDispatcher
> dispatcher
= new widget::EventDispatcher();
479 dispatcher
->Attach(java::EventDispatcher::ByName(aName
),
480 /* window */ nullptr);
481 dispatcher
.forget(aResult
);
485 nsAndroidBridge::~nsAndroidBridge() {}
487 uint32_t AndroidBridge::GetScreenOrientation() {
488 ALOG_BRIDGE("AndroidBridge::GetScreenOrientation");
490 int16_t orientation
= java::GeckoAppShell::GetScreenOrientation();
492 return static_cast<hal::ScreenOrientation
>(orientation
);
495 uint16_t AndroidBridge::GetScreenAngle() {
496 return java::GeckoAppShell::GetScreenAngle();
499 nsresult
AndroidBridge::GetProxyForURI(const nsACString
& aSpec
,
500 const nsACString
& aScheme
,
501 const nsACString
& aHost
,
503 nsACString
& aResult
) {
504 if (!jni::IsAvailable()) {
505 return NS_ERROR_FAILURE
;
509 java::GeckoAppShell::GetProxyForURI(aSpec
, aScheme
, aHost
, aPort
);
511 if (!jstrRet
) return NS_ERROR_FAILURE
;
513 aResult
= jstrRet
->ToCString();
517 bool AndroidBridge::PumpMessageLoop() {
518 JNIEnv
* const env
= jni::GetGeckoThreadEnv();
520 if (mMessageQueueMessages
) {
521 auto msg
= jni::Object::LocalRef::Adopt(
522 env
, env
->GetObjectField(mMessageQueue
.Get(), mMessageQueueMessages
));
523 // if queue.mMessages is null, queue.next() will block, which we don't
524 // want. It turns out to be an order of magnitude more performant to do
525 // this extra check here and block less vs. one fewer checks here and
532 auto msg
= jni::Object::LocalRef::Adopt(
533 env
, env
->CallObjectMethod(mMessageQueue
.Get(), mMessageQueueNext
));
538 return java::GeckoThread::PumpMessageLoop(msg
);