Bug 1716846 [wpt PR 29402] - Update wpt metadata, a=testonly
[gecko.git] / widget / android / AndroidBridge.cpp
blob5a879630be45fb1b8b07c6a0c192ad241d6d79aa
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>
7 #include <dlfcn.h>
8 #include <math.h>
9 #include <GLES2/gl2.h>
11 #include "mozilla/layers/CompositorBridgeChild.h"
12 #include "mozilla/layers/CompositorBridgeParent.h"
14 #include "mozilla/Hal.h"
15 #include "nsXULAppAPI.h"
16 #include <prthread.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"
23 #include "nsWindow.h"
24 #include "mozilla/Preferences.h"
25 #include "nsThreadUtils.h"
26 #include "gfxPlatform.h"
27 #include "gfxContext.h"
28 #include "mozilla/gfx/2D.h"
29 #include "gfxUtils.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);
61 if (!methodID) {
62 ALOG(
63 ">>> FATAL JNI ERROR! GetMethodID(methodName=\"%s\", "
64 "methodType=\"%s\") failed. Did ProGuard optimize away something it "
65 "shouldn't have?",
66 methodName, methodType);
67 env->ExceptionDescribe();
68 MOZ_CRASH();
70 return methodID;
73 jmethodID AndroidBridge::GetStaticMethodID(JNIEnv* env, jclass jClass,
74 const char* methodName,
75 const char* methodType) {
76 jmethodID methodID = env->GetStaticMethodID(jClass, methodName, methodType);
77 if (!methodID) {
78 ALOG(
79 ">>> FATAL JNI ERROR! GetStaticMethodID(methodName=\"%s\", "
80 "methodType=\"%s\") failed. Did ProGuard optimize away something it "
81 "shouldn't have?",
82 methodName, methodType);
83 env->ExceptionDescribe();
84 MOZ_CRASH();
86 return methodID;
89 jfieldID AndroidBridge::GetFieldID(JNIEnv* env, jclass jClass,
90 const char* fieldName,
91 const char* fieldType) {
92 jfieldID fieldID = env->GetFieldID(jClass, fieldName, fieldType);
93 if (!fieldID) {
94 ALOG(
95 ">>> FATAL JNI ERROR! GetFieldID(fieldName=\"%s\", "
96 "fieldType=\"%s\") failed. Did ProGuard optimize away something it "
97 "shouldn't have?",
98 fieldName, fieldType);
99 env->ExceptionDescribe();
100 MOZ_CRASH();
102 return fieldID;
105 jfieldID AndroidBridge::GetStaticFieldID(JNIEnv* env, jclass jClass,
106 const char* fieldName,
107 const char* fieldType) {
108 jfieldID fieldID = env->GetStaticFieldID(jClass, fieldName, fieldType);
109 if (!fieldID) {
110 ALOG(
111 ">>> FATAL JNI ERROR! GetStaticFieldID(fieldName=\"%s\", "
112 "fieldType=\"%s\") failed. Did ProGuard optimize away something it "
113 "shouldn't have?",
114 fieldName, fieldType);
115 env->ExceptionDescribe();
116 MOZ_CRASH();
118 return fieldID;
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() {
135 if (sBridge) {
136 delete sBridge;
137 // AndroidBridge destruction requires sBridge to still be valid,
138 // so we set sBridge to nullptr after deleting it.
139 sBridge = nullptr;
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
155 mMessageQueueNext =
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();
167 return value;
170 bool AndroidBridge::HasHWVP8Decoder() {
171 ALOG_BRIDGE("AndroidBridge::HasHWVP8Decoder");
173 bool value = java::GeckoAppShell::HasHWVP8Decoder();
175 return value;
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());
191 return screensize;
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;
207 return sDepth;
210 void AndroidBridge::Vibrate(const nsTArray<uint32_t>& aPattern) {
211 ALOG_BRIDGE("%s", __PRETTY_FUNCTION__);
213 uint32_t len = aPattern.Length();
214 if (!len) {
215 ALOG_BRIDGE(" invalid 0-length array");
216 return;
219 // It's clear if this worth special-casing, but it creates less
220 // java junk, so dodges the GC.
221 if (len == 1) {
222 jlong d = aPattern[0];
223 if (d < 0) {
224 ALOG_BRIDGE(" invalid vibration duration < 0");
225 return;
227 java::GeckoAppShell::Vibrate(d);
228 return;
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);
238 if (!array) {
239 ALOG_BRIDGE(" failed to allocate array");
240 return;
243 jlong* elts = env->GetLongArrayElements(array, nullptr);
244 elts[0] = 0;
245 for (uint32_t i = 0; i < aPattern.Length(); ++i) {
246 jlong d = aPattern[i];
247 if (d < 0) {
248 ALOG_BRIDGE(" invalid vibration duration < 0");
249 env->ReleaseLongArrayElements(array, elts, JNI_ABORT);
250 return;
252 elts[i + 1] = d;
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,
261 uint32_t aIconSize,
262 uint8_t* const aBuf) {
263 ALOG_BRIDGE("AndroidBridge::GetIconForExtension");
264 NS_ASSERTION(aBuf != nullptr,
265 "AndroidBridge::GetIconForExtension: aBuf is null!");
266 if (!aBuf) return;
268 auto arr = java::GeckoAppShell::GetIconForExtension(
269 NS_ConvertUTF8toUTF16(aFileExt), aIconSize);
271 NS_ASSERTION(
272 arr != nullptr,
273 "AndroidBridge::GetIconForExtension: Returned pixels array is null!");
274 if (!arr) return;
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;
281 NS_ASSERTION(
282 len == bufSize,
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);
294 if (!jEnv) {
295 if (!jni::IsAvailable()) {
296 return false;
298 jEnv = jni::GetGeckoThreadEnv();
301 AutoJNIClass cls(jEnv, className);
302 jfieldID field = cls.getStaticField(fieldName, "I");
304 if (!field) {
305 return false;
308 *aInt = static_cast<int32_t>(jEnv->GetStaticIntField(cls.getRawRef(), field));
309 return true;
312 bool AndroidBridge::GetStaticStringField(const char* className,
313 const char* fieldName,
314 nsAString& result,
315 JNIEnv* jEnv /* = nullptr */) {
316 ALOG_BRIDGE("AndroidBridge::GetStaticStringField %s", fieldName);
318 if (!jEnv) {
319 if (!jni::IsAvailable()) {
320 return false;
322 jEnv = jni::GetGeckoThreadEnv();
325 AutoLocalJNIFrame jniFrame(jEnv, 1);
326 AutoJNIClass cls(jEnv, className);
327 jfieldID field = cls.getStaticField(fieldName, "Ljava/lang/String;");
329 if (!field) {
330 return false;
333 jstring jstr = (jstring)jEnv->GetStaticObjectField(cls.getRawRef(), field);
334 if (!jstr) return false;
336 result.Assign(jni::String::Ref::From(jstr)->ToString());
337 return true;
340 namespace mozilla {
341 class TracerRunnable : public Runnable {
342 public:
343 TracerRunnable() : Runnable("TracerRunnable") {
344 mTracerLock = new Mutex("TracerRunnable");
345 mTracerCondVar = new CondVar(*mTracerLock, "TracerRunnable");
346 mMainThread = do_GetMainThread();
348 ~TracerRunnable() {
349 delete mTracerCondVar;
350 delete mTracerLock;
351 mTracerLock = nullptr;
352 mTracerCondVar = nullptr;
355 virtual nsresult Run() {
356 MutexAutoLock lock(*mTracerLock);
357 if (!AndroidBridge::Bridge()) return NS_OK;
359 mHasRun = true;
360 mTracerCondVar->Notify();
361 return NS_OK;
364 bool Fire() {
365 if (!mTracerLock || !mTracerCondVar) return false;
366 MutexAutoLock lock(*mTracerLock);
367 mHasRun = false;
368 mMainThread->Dispatch(this, NS_DISPATCH_NORMAL);
369 while (!mHasRun) mTracerCondVar->Wait();
370 return true;
373 void Signal() {
374 MutexAutoLock lock(*mTracerLock);
375 mHasRun = true;
376 mTracerCondVar->Notify();
379 private:
380 Mutex* mTracerLock;
381 CondVar* mTracerCondVar;
382 bool mHasRun;
383 nsCOMPtr<nsIThread> mMainThread;
385 StaticRefPtr<TracerRunnable> sTracerRunnable;
387 bool InitWidgetTracing() {
388 if (!sTracerRunnable) sTracerRunnable = new TracerRunnable();
389 return true;
392 void CleanUpWidgetTracing() { sTracerRunnable = nullptr; }
394 bool FireAndWaitForTracerEvent() {
395 if (sTracerRunnable) return sTracerRunnable->Fire();
396 return false;
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) {
415 return;
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
433 // integer.
435 auto arr = java::GeckoAppShell::GetCurrentNetworkInformation();
437 JNIEnv* const env = arr.Env();
438 if (!arr || env->GetArrayLength(arr.Get()) != 3) {
439 return;
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;
471 NS_IMETHODIMP
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);
482 return NS_OK;
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,
502 const int32_t aPort,
503 nsACString& aResult) {
504 if (!jni::IsAvailable()) {
505 return NS_ERROR_FAILURE;
508 auto jstrRet =
509 java::GeckoAppShell::GetProxyForURI(aSpec, aScheme, aHost, aPort);
511 if (!jstrRet) return NS_ERROR_FAILURE;
513 aResult = jstrRet->ToCString();
514 return NS_OK;
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
526 // more blocking.
527 if (!msg) {
528 return false;
532 auto msg = jni::Object::LocalRef::Adopt(
533 env, env->CallObjectMethod(mMessageQueue.Get(), mMessageQueueNext));
534 if (!msg) {
535 return false;
538 return java::GeckoThread::PumpMessageLoop(msg);