summaryrefslogtreecommitdiffstats
path: root/hal/android
diff options
context:
space:
mode:
Diffstat (limited to 'hal/android')
-rw-r--r--hal/android/AndroidHal.cpp158
-rw-r--r--hal/android/AndroidHeterogeneousCpuInfo.cpp85
-rw-r--r--hal/android/AndroidPerformanceHintManager.cpp195
-rw-r--r--hal/android/AndroidProcessPriority.cpp62
-rw-r--r--hal/android/AndroidSensor.cpp23
5 files changed, 523 insertions, 0 deletions
diff --git a/hal/android/AndroidHal.cpp b/hal/android/AndroidHal.cpp
new file mode 100644
index 0000000000..3a7e1bf121
--- /dev/null
+++ b/hal/android/AndroidHal.cpp
@@ -0,0 +1,158 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "Hal.h"
+#include "HalImpl.h"
+#include "WindowIdentifier.h"
+#include "AndroidBridge.h"
+#include "mozilla/dom/network/Constants.h"
+#include "mozilla/java/GeckoAppShellWrappers.h"
+#include "mozilla/java/GeckoRuntimeWrappers.h"
+#include "mozilla/widget/ScreenManager.h"
+#include "nsPIDOMWindow.h"
+
+using namespace mozilla::dom;
+using namespace mozilla::hal;
+
+namespace mozilla::hal_impl {
+
+void Vibrate(const nsTArray<uint32_t>& pattern, WindowIdentifier&&) {
+ // Ignore the WindowIdentifier parameter; it's here only because hal::Vibrate,
+ // hal_sandbox::Vibrate, and hal_impl::Vibrate all must have the same
+ // signature.
+
+ // Strangely enough, the Android Java API seems to treat vibrate([0]) as a
+ // nop. But we want to treat vibrate([0]) like CancelVibrate! (Note that we
+ // also need to treat vibrate([]) as a call to CancelVibrate.)
+ bool allZero = true;
+ for (uint32_t i = 0; i < pattern.Length(); i++) {
+ if (pattern[i] != 0) {
+ allZero = false;
+ break;
+ }
+ }
+
+ if (allZero) {
+ hal_impl::CancelVibrate(WindowIdentifier());
+ return;
+ }
+
+ AndroidBridge* b = AndroidBridge::Bridge();
+ if (!b) {
+ return;
+ }
+
+ b->Vibrate(pattern);
+}
+
+void CancelVibrate(WindowIdentifier&&) {
+ // Ignore WindowIdentifier parameter.
+
+ java::GeckoAppShell::CancelVibrate();
+}
+
+void EnableBatteryNotifications() {
+ java::GeckoAppShell::EnableBatteryNotifications();
+}
+
+void DisableBatteryNotifications() {
+ java::GeckoAppShell::DisableBatteryNotifications();
+}
+
+void GetCurrentBatteryInformation(hal::BatteryInformation* aBatteryInfo) {
+ AndroidBridge::Bridge()->GetCurrentBatteryInformation(aBatteryInfo);
+}
+
+void EnableNetworkNotifications() {
+ java::GeckoAppShell::EnableNetworkNotifications();
+}
+
+void DisableNetworkNotifications() {
+ java::GeckoAppShell::DisableNetworkNotifications();
+}
+
+void GetCurrentNetworkInformation(hal::NetworkInformation* aNetworkInfo) {
+ AndroidBridge::Bridge()->GetCurrentNetworkInformation(aNetworkInfo);
+}
+
+static bool IsSupportedScreenOrientation(hal::ScreenOrientation aOrientation) {
+ // The Android backend only supports these orientations.
+ static constexpr ScreenOrientation kSupportedOrientations[] = {
+ ScreenOrientation::PortraitPrimary,
+ ScreenOrientation::PortraitSecondary,
+ ScreenOrientation::PortraitPrimary | ScreenOrientation::PortraitSecondary,
+ ScreenOrientation::LandscapePrimary,
+ ScreenOrientation::LandscapeSecondary,
+ ScreenOrientation::LandscapePrimary |
+ ScreenOrientation::LandscapeSecondary,
+ ScreenOrientation::PortraitPrimary |
+ ScreenOrientation::PortraitSecondary |
+ ScreenOrientation::LandscapePrimary |
+ ScreenOrientation::LandscapeSecondary,
+ ScreenOrientation::Default,
+ };
+ for (auto supportedOrientation : kSupportedOrientations) {
+ if (aOrientation == supportedOrientation) {
+ return true;
+ }
+ }
+ return false;
+}
+
+RefPtr<GenericNonExclusivePromise> LockScreenOrientation(
+ const hal::ScreenOrientation& aOrientation) {
+ if (!IsSupportedScreenOrientation(aOrientation)) {
+ NS_WARNING("Unsupported screen orientation type");
+ return GenericNonExclusivePromise::CreateAndReject(
+ NS_ERROR_DOM_NOT_SUPPORTED_ERR, __func__);
+ }
+
+ java::GeckoRuntime::LocalRef runtime = java::GeckoRuntime::GetInstance();
+ if (!runtime) {
+ return GenericNonExclusivePromise::CreateAndReject(NS_ERROR_DOM_ABORT_ERR,
+ __func__);
+ }
+
+ hal::ScreenOrientation orientation = [&aOrientation]() {
+ if (aOrientation == hal::ScreenOrientation::Default) {
+ // GeckoView only supports single monitor, so get primary screen data for
+ // natural orientation.
+ RefPtr<widget::Screen> screen =
+ widget::ScreenManager::GetSingleton().GetPrimaryScreen();
+ return screen->GetDefaultOrientationType();
+ }
+ return aOrientation;
+ }();
+
+ auto result = runtime->LockScreenOrientation(uint32_t(orientation));
+ auto geckoResult = java::GeckoResult::LocalRef(std::move(result));
+ return GenericNonExclusivePromise::FromGeckoResult(geckoResult)
+ ->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [](const GenericNonExclusivePromise::ResolveOrRejectValue& aValue) {
+ if (aValue.IsResolve()) {
+ if (aValue.ResolveValue()) {
+ return GenericNonExclusivePromise::CreateAndResolve(true,
+ __func__);
+ }
+ // Delegated orientation controller returns failure for
+ // lock.
+ return GenericNonExclusivePromise::CreateAndReject(
+ NS_ERROR_DOM_ABORT_ERR, __func__);
+ }
+ // Browser side doesn't implement orientation delegate.
+ return GenericNonExclusivePromise::CreateAndReject(
+ NS_ERROR_DOM_NOT_SUPPORTED_ERR, __func__);
+ });
+}
+
+void UnlockScreenOrientation() {
+ java::GeckoRuntime::LocalRef runtime = java::GeckoRuntime::GetInstance();
+ if (runtime) {
+ runtime->UnlockScreenOrientation();
+ }
+}
+
+} // namespace mozilla::hal_impl
diff --git a/hal/android/AndroidHeterogeneousCpuInfo.cpp b/hal/android/AndroidHeterogeneousCpuInfo.cpp
new file mode 100644
index 0000000000..e51ebce260
--- /dev/null
+++ b/hal/android/AndroidHeterogeneousCpuInfo.cpp
@@ -0,0 +1,85 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+#include "Hal.h"
+#include "HalLog.h"
+#include "HalTypes.h"
+
+#include "mozilla/BitSet.h"
+#include "mozilla/CheckedInt.h"
+#include "prsystem.h"
+#include <fstream>
+
+namespace mozilla::hal_impl {
+
+mozilla::Maybe<HeterogeneousCpuInfo> CreateHeterogeneousCpuInfo() {
+ CheckedInt<size_t> cpuCount = PR_GetNumberOfProcessors();
+ if (!cpuCount.isValid()) {
+ HAL_ERR("HeterogeneousCpuInfo: Failed to query processor count");
+ return Nothing();
+ }
+
+ if (cpuCount.value() > HeterogeneousCpuInfo::MAX_CPUS) {
+ HAL_ERR("HeterogeneousCpuInfo: Too many cpus");
+ return Nothing();
+ }
+
+ std::vector<std::pair<int, int>> cpu_freqs;
+ cpu_freqs.reserve(cpuCount.value());
+ for (size_t i = 0; i < cpuCount.value(); i++) {
+ std::stringstream freq_filename;
+ // On all devices tested, even with isolated content processes we do have
+ // permission to read this file. If, however, this function stops working
+ // one day, then this may be a good place to start investigating.
+ freq_filename << "/sys/devices/system/cpu/cpu" << i
+ << "/cpufreq/cpuinfo_max_freq";
+ std::ifstream ifs(freq_filename.str());
+ if (!ifs) {
+ HAL_ERR("HeterogeneousCpuInfo: Failed to open file %s",
+ freq_filename.str().c_str());
+ return Nothing();
+ }
+ int freq;
+ if (!(ifs >> freq)) {
+ HAL_ERR("HeterogeneousCpuInfo: Failed to parse file %s",
+ freq_filename.str().c_str());
+ return Nothing();
+ }
+ cpu_freqs.push_back(std::make_pair(i, freq));
+ }
+
+ std::sort(cpu_freqs.begin(), cpu_freqs.end(),
+ [](auto lhs, auto rhs) { return lhs.second < rhs.second; });
+
+ HeterogeneousCpuInfo info;
+ info.mTotalNumCpus = cpuCount.value();
+
+ int lowest_freq = cpu_freqs.front().second;
+ int highest_freq = cpu_freqs.back().second;
+ for (const auto& [cpu, freq] : cpu_freqs) {
+ if (freq == highest_freq) {
+ info.mBigCpus[cpu] = true;
+ } else if (freq == lowest_freq) {
+ info.mLittleCpus[cpu] = true;
+ } else {
+ info.mMediumCpus[cpu] = true;
+ }
+ }
+
+ HAL_LOG("HeterogeneousCpuInfo: little: %zu, med: %zu, big: %zu",
+ info.mLittleCpus.Count(), info.mMediumCpus.Count(),
+ info.mBigCpus.Count());
+
+ return Some(info);
+}
+
+const Maybe<HeterogeneousCpuInfo>& GetHeterogeneousCpuInfo() {
+ static const Maybe<HeterogeneousCpuInfo> cpuInfo =
+ CreateHeterogeneousCpuInfo();
+ return cpuInfo;
+}
+
+} // namespace mozilla::hal_impl
diff --git a/hal/android/AndroidPerformanceHintManager.cpp b/hal/android/AndroidPerformanceHintManager.cpp
new file mode 100644
index 0000000000..a9507a8272
--- /dev/null
+++ b/hal/android/AndroidPerformanceHintManager.cpp
@@ -0,0 +1,195 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+#include "Hal.h"
+#include "HalLog.h"
+#include "HalTypes.h"
+
+#include "AndroidBuild.h"
+
+#include <dlfcn.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+typedef struct APerformanceHintManager APerformanceHintManager;
+typedef struct APerformanceHintSession APerformanceHintSession;
+
+namespace mozilla {
+namespace hal_impl {
+
+#define LOAD_FN(api, lib, name) \
+ do { \
+ api->m##name = reinterpret_cast<Fn##name>(dlsym(handle, #name)); \
+ if (!api->m##name) { \
+ HAL_ERR("Failed to load %s", #name); \
+ return nullptr; \
+ } \
+ } while (false)
+
+class PerformanceHintManagerApi final {
+ public:
+ static PerformanceHintManagerApi* Get() {
+ // C++ guarantees local static variable initialization is thread safe
+ static UniquePtr<PerformanceHintManagerApi> api = Create();
+ return api.get();
+ }
+
+ APerformanceHintManager* APerformanceHint_getManager() const {
+ return mAPerformanceHint_getManager();
+ }
+
+ APerformanceHintSession* APerformanceHint_createSession(
+ APerformanceHintManager* manager, const int32_t* threadIds, size_t size,
+ int64_t initialTargetWorkDurationNanos) const {
+ return mAPerformanceHint_createSession(manager, threadIds, size,
+ initialTargetWorkDurationNanos);
+ }
+
+ int APerformanceHint_updateTargetWorkDuration(
+ APerformanceHintSession* session, int64_t targetDurationNanos) const {
+ return mAPerformanceHint_updateTargetWorkDuration(session,
+ targetDurationNanos);
+ }
+
+ int APerformanceHint_reportActualWorkDuration(
+ APerformanceHintSession* session, int64_t actualDurationNanos) const {
+ return mAPerformanceHint_reportActualWorkDuration(session,
+ actualDurationNanos);
+ }
+
+ void APerformanceHint_closeSession(APerformanceHintSession* session) const {
+ mAPerformanceHint_closeSession(session);
+ }
+
+ private:
+ PerformanceHintManagerApi() = default;
+
+ static UniquePtr<PerformanceHintManagerApi> Create() {
+ if (mozilla::jni::GetAPIVersion() < __ANDROID_API_T__) {
+ return nullptr;
+ }
+
+ void* const handle = dlopen("libandroid.so", RTLD_LAZY | RTLD_LOCAL);
+ if (!handle) {
+ HAL_ERR("Failed to open libandroid.so");
+ return nullptr;
+ }
+
+ auto api = WrapUnique(new PerformanceHintManagerApi());
+ LOAD_FN(api, handle, APerformanceHint_getManager);
+ LOAD_FN(api, handle, APerformanceHint_createSession);
+ LOAD_FN(api, handle, APerformanceHint_updateTargetWorkDuration);
+ LOAD_FN(api, handle, APerformanceHint_reportActualWorkDuration);
+ LOAD_FN(api, handle, APerformanceHint_closeSession);
+
+ return api;
+ }
+
+ using FnAPerformanceHint_getManager = APerformanceHintManager* (*)();
+ using FnAPerformanceHint_createSession =
+ APerformanceHintSession* (*)(APerformanceHintManager* manager,
+ const int32_t* threadIds, size_t size,
+ int64_t initialTargetWorkDurationNanos);
+ using FnAPerformanceHint_updateTargetWorkDuration =
+ int (*)(APerformanceHintSession* session, int64_t targetDurationNanos);
+ using FnAPerformanceHint_reportActualWorkDuration =
+ int (*)(APerformanceHintSession* session, int64_t actualDurationNanos);
+ using FnAPerformanceHint_closeSession =
+ void (*)(APerformanceHintSession* session);
+
+ FnAPerformanceHint_getManager mAPerformanceHint_getManager = nullptr;
+ FnAPerformanceHint_createSession mAPerformanceHint_createSession = nullptr;
+ FnAPerformanceHint_updateTargetWorkDuration
+ mAPerformanceHint_updateTargetWorkDuration = nullptr;
+ FnAPerformanceHint_reportActualWorkDuration
+ mAPerformanceHint_reportActualWorkDuration = nullptr;
+ FnAPerformanceHint_closeSession mAPerformanceHint_closeSession = nullptr;
+};
+
+class AndroidPerformanceHintSession final : public hal::PerformanceHintSession {
+ public:
+ // Creates a PerformanceHintSession wrapping the provided NDK
+ // APerformanceHintSession instance. This assumes ownership of aSession,
+ // therefore the caller must not close the session itself.
+ explicit AndroidPerformanceHintSession(APerformanceHintSession* aSession)
+ : mSession(aSession) {}
+
+ AndroidPerformanceHintSession(AndroidPerformanceHintSession& aOther) = delete;
+ AndroidPerformanceHintSession(AndroidPerformanceHintSession&& aOther) {
+ mSession = aOther.mSession;
+ aOther.mSession = nullptr;
+ }
+
+ ~AndroidPerformanceHintSession() {
+ if (mSession) {
+ PerformanceHintManagerApi::Get()->APerformanceHint_closeSession(mSession);
+ }
+ }
+
+ void UpdateTargetWorkDuration(TimeDuration aDuration) override {
+ PerformanceHintManagerApi::Get()->APerformanceHint_updateTargetWorkDuration(
+ mSession, aDuration.ToMicroseconds() * 1000);
+ }
+
+ void ReportActualWorkDuration(TimeDuration aDuration) override {
+ PerformanceHintManagerApi::Get()->APerformanceHint_reportActualWorkDuration(
+ mSession, aDuration.ToMicroseconds() * 1000);
+ }
+
+ private:
+ APerformanceHintSession* mSession;
+};
+
+static APerformanceHintManager* InitManager() {
+ const auto* api = PerformanceHintManagerApi::Get();
+ if (!api) {
+ return nullptr;
+ }
+
+ // At the time of writing we are only aware of PerformanceHintManager being
+ // implemented on Tensor devices (Pixel 6 and 7 families). On most devices
+ // createSession() will simply return null. However, on some devices
+ // createSession() does return a session but scheduling does not appear to be
+ // affected in any way. Rather than pretending to the caller that
+ // PerformanceHintManager is available on such devices, return null allowing
+ // them to use another means of achieving the performance they require.
+ const auto socManufacturer = java::sdk::Build::SOC_MANUFACTURER()->ToString();
+ if (!socManufacturer.EqualsASCII("Google")) {
+ return nullptr;
+ }
+
+ return api->APerformanceHint_getManager();
+}
+
+UniquePtr<hal::PerformanceHintSession> CreatePerformanceHintSession(
+ const nsTArray<PlatformThreadHandle>& aThreads,
+ mozilla::TimeDuration aTargetWorkDuration) {
+ // C++ guarantees local static variable initialization is thread safe
+ static APerformanceHintManager* manager = InitManager();
+
+ if (!manager) {
+ return nullptr;
+ }
+
+ const auto* api = PerformanceHintManagerApi::Get();
+
+ nsTArray<pid_t> tids(aThreads.Length());
+ std::transform(aThreads.cbegin(), aThreads.cend(), MakeBackInserter(tids),
+ [](pthread_t handle) { return pthread_gettid_np(handle); });
+
+ APerformanceHintSession* session = api->APerformanceHint_createSession(
+ manager, tids.Elements(), tids.Length(),
+ aTargetWorkDuration.ToMicroseconds() * 1000);
+ if (!session) {
+ return nullptr;
+ }
+
+ return MakeUnique<AndroidPerformanceHintSession>(session);
+}
+
+} // namespace hal_impl
+} // namespace mozilla
diff --git a/hal/android/AndroidProcessPriority.cpp b/hal/android/AndroidProcessPriority.cpp
new file mode 100644
index 0000000000..aab2bf50ef
--- /dev/null
+++ b/hal/android/AndroidProcessPriority.cpp
@@ -0,0 +1,62 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+#include "Hal.h"
+
+#include "mozilla/java/GeckoProcessManagerWrappers.h"
+#include "mozilla/java/GeckoProcessTypeWrappers.h"
+#include "mozilla/java/ServiceAllocatorWrappers.h"
+
+using namespace mozilla::hal;
+
+/**
+ * Bucket the Gecko HAL process priority level into one of the three Android
+ * priority levels.
+ */
+static mozilla::java::ServiceAllocator::PriorityLevel::LocalRef
+ToJavaPriorityLevel(const ProcessPriority aPriority) {
+ if (aPriority >= PROCESS_PRIORITY_FOREGROUND) {
+ return mozilla::java::ServiceAllocator::PriorityLevel::FOREGROUND();
+ } else if (aPriority <= PROCESS_PRIORITY_PREALLOC &&
+ aPriority >= PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE) {
+ return mozilla::java::ServiceAllocator::PriorityLevel::BACKGROUND();
+ }
+
+ return mozilla::java::ServiceAllocator::PriorityLevel::IDLE();
+}
+
+namespace mozilla {
+namespace hal_impl {
+
+void SetProcessPriority(int aPid, ProcessPriority aPriority) {
+ if (aPriority == PROCESS_PRIORITY_PARENT_PROCESS) {
+ // This is the parent process itself, which we do not control.
+ return;
+ }
+
+ const int32_t intPriority = static_cast<int32_t>(aPriority);
+ if (intPriority < 0 || intPriority >= NUM_PROCESS_PRIORITY) {
+ return;
+ }
+
+ auto contentProcType = java::GeckoProcessType::CONTENT();
+ auto selector =
+ java::GeckoProcessManager::Selector::New(contentProcType, aPid);
+ auto priorityLevel = ToJavaPriorityLevel(aPriority);
+
+ // To Android, a lower-valued integer is a higher relative priority.
+ // We take the integer value of the enum and subtract it from the value
+ // of the highest Gecko priority level to obtain a 0-based indicator of
+ // the relative priority within the Java PriorityLevel.
+ const int32_t relativeImportance =
+ (static_cast<int32_t>(NUM_PROCESS_PRIORITY) - 1) - intPriority;
+
+ java::GeckoProcessManager::SetProcessPriority(selector, priorityLevel,
+ relativeImportance);
+}
+
+} // namespace hal_impl
+} // namespace mozilla
diff --git a/hal/android/AndroidSensor.cpp b/hal/android/AndroidSensor.cpp
new file mode 100644
index 0000000000..59db64a1d3
--- /dev/null
+++ b/hal/android/AndroidSensor.cpp
@@ -0,0 +1,23 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "Hal.h"
+#include "mozilla/java/GeckoAppShellWrappers.h"
+
+using namespace mozilla::hal;
+
+namespace mozilla {
+namespace hal_impl {
+
+void EnableSensorNotifications(SensorType aSensor) {
+ java::GeckoAppShell::EnableSensor(aSensor);
+}
+
+void DisableSensorNotifications(SensorType aSensor) {
+ java::GeckoAppShell::DisableSensor(aSensor);
+}
+
+} // namespace hal_impl
+} // namespace mozilla