summaryrefslogtreecommitdiffstats
path: root/hal/gonk/GonkHal.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'hal/gonk/GonkHal.cpp')
-rw-r--r--hal/gonk/GonkHal.cpp2045
1 files changed, 0 insertions, 2045 deletions
diff --git a/hal/gonk/GonkHal.cpp b/hal/gonk/GonkHal.cpp
deleted file mode 100644
index 05d9295a2..000000000
--- a/hal/gonk/GonkHal.cpp
+++ /dev/null
@@ -1,2045 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set sw=2 ts=8 et ft=cpp : */
-/* Copyright 2012 Mozilla Foundation and Mozilla contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <ctype.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <linux/android_alarm.h>
-#include <math.h>
-#include <regex.h>
-#include <sched.h>
-#include <stdio.h>
-#include <sys/klog.h>
-#include <sys/stat.h>
-#include <sys/syscall.h>
-#include <sys/resource.h>
-#include <time.h>
-#include <unistd.h>
-
-#include "mozilla/DebugOnly.h"
-
-#include "android/log.h"
-#include "cutils/properties.h"
-#include "hardware/hardware.h"
-#include "hardware/lights.h"
-#include "hardware_legacy/uevent.h"
-#include "hardware_legacy/vibrator.h"
-#include "hardware_legacy/power.h"
-#include "libdisplay/GonkDisplay.h"
-#include "utils/threads.h"
-
-#include "base/message_loop.h"
-#include "base/task.h"
-
-#include "Hal.h"
-#include "HalImpl.h"
-#include "HalLog.h"
-#include "mozilla/ArrayUtils.h"
-#include "mozilla/ClearOnShutdown.h"
-#include "mozilla/dom/battery/Constants.h"
-#include "mozilla/DebugOnly.h"
-#include "mozilla/FileUtils.h"
-#include "mozilla/Monitor.h"
-#include "mozilla/RefPtr.h"
-#include "mozilla/Services.h"
-#include "mozilla/StaticMutex.h"
-#include "mozilla/StaticPtr.h"
-#include "mozilla/Preferences.h"
-#include "mozilla/UniquePtrExtensions.h"
-#include "nsAlgorithm.h"
-#include "nsPrintfCString.h"
-#include "nsIObserver.h"
-#include "nsIObserverService.h"
-#include "nsIRecoveryService.h"
-#include "nsIRunnable.h"
-#include "nsScreenManagerGonk.h"
-#include "nsThreadUtils.h"
-#include "nsThreadUtils.h"
-#include "nsIThread.h"
-#include "nsXULAppAPI.h"
-#include "OrientationObserver.h"
-#include "UeventPoller.h"
-#include "nsIWritablePropertyBag2.h"
-#include <algorithm>
-
-#define NsecPerMsec 1000000LL
-#define NsecPerSec 1000000000
-
-// The header linux/oom.h is not available in bionic libc. We
-// redefine some of its constants here.
-
-#ifndef OOM_DISABLE
-#define OOM_DISABLE (-17)
-#endif
-
-#ifndef OOM_ADJUST_MIN
-#define OOM_ADJUST_MIN (-16)
-#endif
-
-#ifndef OOM_ADJUST_MAX
-#define OOM_ADJUST_MAX 15
-#endif
-
-#ifndef OOM_SCORE_ADJ_MIN
-#define OOM_SCORE_ADJ_MIN (-1000)
-#endif
-
-#ifndef OOM_SCORE_ADJ_MAX
-#define OOM_SCORE_ADJ_MAX 1000
-#endif
-
-#ifndef BATTERY_CHARGING_ARGB
-#define BATTERY_CHARGING_ARGB 0x00FF0000
-#endif
-#ifndef BATTERY_FULL_ARGB
-#define BATTERY_FULL_ARGB 0x0000FF00
-#endif
-
-using namespace mozilla;
-using namespace mozilla::hal;
-using namespace mozilla::dom;
-
-namespace mozilla {
-namespace hal_impl {
-
-/**
- * These are defined by libhardware, specifically, hardware/libhardware/include/hardware/lights.h
- * in the gonk subsystem.
- * If these change and are exposed to JS, make sure nsIHal.idl is updated as well.
- */
-enum LightType {
- eHalLightID_Backlight = 0,
- eHalLightID_Keyboard = 1,
- eHalLightID_Buttons = 2,
- eHalLightID_Battery = 3,
- eHalLightID_Notifications = 4,
- eHalLightID_Attention = 5,
- eHalLightID_Bluetooth = 6,
- eHalLightID_Wifi = 7,
- eHalLightID_Count // This should stay at the end
-};
-enum LightMode {
- eHalLightMode_User = 0, // brightness is managed by user setting
- eHalLightMode_Sensor = 1, // brightness is managed by a light sensor
- eHalLightMode_Count
-};
-enum FlashMode {
- eHalLightFlash_None = 0,
- eHalLightFlash_Timed = 1, // timed flashing. Use flashOnMS and flashOffMS for timing
- eHalLightFlash_Hardware = 2, // hardware assisted flashing
- eHalLightFlash_Count
-};
-
-struct LightConfiguration {
- LightType light;
- LightMode mode;
- FlashMode flash;
- uint32_t flashOnMS;
- uint32_t flashOffMS;
- uint32_t color;
-};
-
-static light_device_t* sLights[eHalLightID_Count]; // will be initialized to nullptr
-
-static light_device_t*
-GetDevice(hw_module_t* module, char const* name)
-{
- int err;
- hw_device_t* device;
- err = module->methods->open(module, name, &device);
- if (err == 0) {
- return (light_device_t*)device;
- } else {
- return nullptr;
- }
-}
-
-static void
-InitLights()
-{
- // assume that if backlight is nullptr, nothing has been set yet
- // if this is not true, the initialization will occur everytime a light is read or set!
- if (!sLights[eHalLightID_Backlight]) {
- int err;
- hw_module_t* module;
-
- err = hw_get_module(LIGHTS_HARDWARE_MODULE_ID, (hw_module_t const**)&module);
- if (err == 0) {
- sLights[eHalLightID_Backlight]
- = GetDevice(module, LIGHT_ID_BACKLIGHT);
- sLights[eHalLightID_Keyboard]
- = GetDevice(module, LIGHT_ID_KEYBOARD);
- sLights[eHalLightID_Buttons]
- = GetDevice(module, LIGHT_ID_BUTTONS);
- sLights[eHalLightID_Battery]
- = GetDevice(module, LIGHT_ID_BATTERY);
- sLights[eHalLightID_Notifications]
- = GetDevice(module, LIGHT_ID_NOTIFICATIONS);
- sLights[eHalLightID_Attention]
- = GetDevice(module, LIGHT_ID_ATTENTION);
- sLights[eHalLightID_Bluetooth]
- = GetDevice(module, LIGHT_ID_BLUETOOTH);
- sLights[eHalLightID_Wifi]
- = GetDevice(module, LIGHT_ID_WIFI);
- }
- }
-}
-
-/**
- * The state last set for the lights until liblights supports
- * getting the light state.
- */
-static light_state_t sStoredLightState[eHalLightID_Count];
-
-/**
-* Set the value of a light to a particular color, with a specific flash pattern.
-* light specifices which light. See Hal.idl for the list of constants
-* mode specifies user set or based on ambient light sensor
-* flash specifies whether or how to flash the light
-* flashOnMS and flashOffMS specify the pattern for XXX flash mode
-* color specifies the color. If the light doesn't support color, the given color is
-* transformed into a brightness, or just an on/off if that is all the light is capable of.
-* returns true if successful and false if failed.
-*/
-static bool
-SetLight(LightType light, const LightConfiguration& aConfig)
-{
- light_state_t state;
-
- InitLights();
-
- if (light < 0 || light >= eHalLightID_Count ||
- sLights[light] == nullptr) {
- return false;
- }
-
- memset(&state, 0, sizeof(light_state_t));
- state.color = aConfig.color;
- state.flashMode = aConfig.flash;
- state.flashOnMS = aConfig.flashOnMS;
- state.flashOffMS = aConfig.flashOffMS;
- state.brightnessMode = aConfig.mode;
-
- sLights[light]->set_light(sLights[light], &state);
- sStoredLightState[light] = state;
- return true;
-}
-
-/**
-* GET the value of a light returning a particular color, with a specific flash pattern.
-* returns true if successful and false if failed.
-*/
-static bool
-GetLight(LightType light, LightConfiguration* aConfig)
-{
- light_state_t state;
-
- if (light < 0 || light >= eHalLightID_Count ||
- sLights[light] == nullptr) {
- return false;
- }
-
- memset(&state, 0, sizeof(light_state_t));
- state = sStoredLightState[light];
-
- aConfig->light = light;
- aConfig->color = state.color;
- aConfig->flash = FlashMode(state.flashMode);
- aConfig->flashOnMS = state.flashOnMS;
- aConfig->flashOffMS = state.flashOffMS;
- aConfig->mode = LightMode(state.brightnessMode);
-
- return true;
-}
-
-namespace {
-
-/**
- * This runnable runs for the lifetime of the program, once started. It's
- * responsible for "playing" vibration patterns.
- */
-class VibratorRunnable final
- : public nsIRunnable
- , public nsIObserver
-{
-public:
- VibratorRunnable()
- : mMonitor("VibratorRunnable")
- , mIndex(0)
- {
- nsCOMPtr<nsIObserverService> os = services::GetObserverService();
- if (!os) {
- NS_WARNING("Could not get observer service!");
- return;
- }
-
- os->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
- }
-
- NS_DECL_THREADSAFE_ISUPPORTS
- NS_DECL_NSIRUNNABLE
- NS_DECL_NSIOBSERVER
-
- // Run on the main thread, not the vibrator thread.
- void Vibrate(const nsTArray<uint32_t> &pattern);
- void CancelVibrate();
-
- static bool ShuttingDown() { return sShuttingDown; }
-
-protected:
- ~VibratorRunnable() {}
-
-private:
- Monitor mMonitor;
-
- // The currently-playing pattern.
- nsTArray<uint32_t> mPattern;
-
- // The index we're at in the currently-playing pattern. If mIndex >=
- // mPattern.Length(), then we're not currently playing anything.
- uint32_t mIndex;
-
- // Set to true in our shutdown observer. When this is true, we kill the
- // vibrator thread.
- static bool sShuttingDown;
-};
-
-NS_IMPL_ISUPPORTS(VibratorRunnable, nsIRunnable, nsIObserver);
-
-bool VibratorRunnable::sShuttingDown = false;
-
-static StaticRefPtr<VibratorRunnable> sVibratorRunnable;
-
-NS_IMETHODIMP
-VibratorRunnable::Run()
-{
- MonitorAutoLock lock(mMonitor);
-
- // We currently assume that mMonitor.Wait(X) waits for X milliseconds. But in
- // reality, the kernel might not switch to this thread for some time after the
- // wait expires. So there's potential for some inaccuracy here.
- //
- // This doesn't worry me too much. Note that we don't even start vibrating
- // immediately when VibratorRunnable::Vibrate is called -- we go through a
- // condvar onto another thread. Better just to be chill about small errors in
- // the timing here.
-
- while (!sShuttingDown) {
- if (mIndex < mPattern.Length()) {
- uint32_t duration = mPattern[mIndex];
- if (mIndex % 2 == 0) {
- vibrator_on(duration);
- }
- mIndex++;
- mMonitor.Wait(PR_MillisecondsToInterval(duration));
- }
- else {
- mMonitor.Wait();
- }
- }
- sVibratorRunnable = nullptr;
- return NS_OK;
-}
-
-NS_IMETHODIMP
-VibratorRunnable::Observe(nsISupports *subject, const char *topic,
- const char16_t *data)
-{
- MOZ_ASSERT(strcmp(topic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0);
- MonitorAutoLock lock(mMonitor);
- sShuttingDown = true;
- mMonitor.Notify();
-
- return NS_OK;
-}
-
-void
-VibratorRunnable::Vibrate(const nsTArray<uint32_t> &pattern)
-{
- MonitorAutoLock lock(mMonitor);
- mPattern = pattern;
- mIndex = 0;
- mMonitor.Notify();
-}
-
-void
-VibratorRunnable::CancelVibrate()
-{
- MonitorAutoLock lock(mMonitor);
- mPattern.Clear();
- mPattern.AppendElement(0);
- mIndex = 0;
- mMonitor.Notify();
-}
-
-void
-EnsureVibratorThreadInitialized()
-{
- if (sVibratorRunnable) {
- return;
- }
-
- sVibratorRunnable = new VibratorRunnable();
- nsCOMPtr<nsIThread> thread;
- NS_NewThread(getter_AddRefs(thread), sVibratorRunnable);
-}
-
-} // namespace
-
-void
-Vibrate(const nsTArray<uint32_t> &pattern, const hal::WindowIdentifier &)
-{
- MOZ_ASSERT(NS_IsMainThread());
- if (VibratorRunnable::ShuttingDown()) {
- return;
- }
- EnsureVibratorThreadInitialized();
- sVibratorRunnable->Vibrate(pattern);
-}
-
-void
-CancelVibrate(const hal::WindowIdentifier &)
-{
- MOZ_ASSERT(NS_IsMainThread());
- if (VibratorRunnable::ShuttingDown()) {
- return;
- }
- EnsureVibratorThreadInitialized();
- sVibratorRunnable->CancelVibrate();
-}
-
-namespace {
-
-class BatteryUpdater : public Runnable {
-public:
- NS_IMETHOD Run() override
- {
- hal::BatteryInformation info;
- hal_impl::GetCurrentBatteryInformation(&info);
-
- // Control the battery indicator (led light) here using BatteryInformation
- // we just retrieved.
- uint32_t color = 0; // Format: 0x00rrggbb.
- if (info.charging() && (info.level() == 1)) {
- // Charging and battery full.
- color = BATTERY_FULL_ARGB;
- } else if (info.charging() && (info.level() < 1)) {
- // Charging but not full.
- color = BATTERY_CHARGING_ARGB;
- } // else turn off battery indicator.
-
- LightConfiguration aConfig;
- aConfig.light = eHalLightID_Battery;
- aConfig.mode = eHalLightMode_User;
- aConfig.flash = eHalLightFlash_None;
- aConfig.flashOnMS = aConfig.flashOffMS = 0;
- aConfig.color = color;
-
- SetLight(eHalLightID_Battery, aConfig);
-
- hal::NotifyBatteryChange(info);
-
- {
- // bug 975667
- // Gecko gonk hal is required to emit battery charging/level notification via nsIObserverService.
- // This is useful for XPCOM components that are not statically linked to Gecko and cannot call
- // hal::EnableBatteryNotifications
- nsCOMPtr<nsIObserverService> obsService = mozilla::services::GetObserverService();
- nsCOMPtr<nsIWritablePropertyBag2> propbag =
- do_CreateInstance("@mozilla.org/hash-property-bag;1");
- if (obsService && propbag) {
- propbag->SetPropertyAsBool(NS_LITERAL_STRING("charging"),
- info.charging());
- propbag->SetPropertyAsDouble(NS_LITERAL_STRING("level"),
- info.level());
-
- obsService->NotifyObservers(propbag, "gonkhal-battery-notifier", nullptr);
- }
- }
-
- return NS_OK;
- }
-};
-
-} // namespace
-
-class BatteryObserver final : public IUeventObserver
-{
-public:
- NS_INLINE_DECL_REFCOUNTING(BatteryObserver)
-
- BatteryObserver()
- :mUpdater(new BatteryUpdater())
- {
- }
-
- virtual void Notify(const NetlinkEvent &aEvent)
- {
- // this will run on IO thread
- NetlinkEvent *event = const_cast<NetlinkEvent*>(&aEvent);
- const char *subsystem = event->getSubsystem();
- // e.g. DEVPATH=/devices/platform/sec-battery/power_supply/battery
- const char *devpath = event->findParam("DEVPATH");
- if (strcmp(subsystem, "power_supply") == 0 &&
- strstr(devpath, "battery")) {
- // aEvent will be valid only in this method.
- NS_DispatchToMainThread(mUpdater);
- }
- }
-
-protected:
- ~BatteryObserver() {}
-
-private:
- RefPtr<BatteryUpdater> mUpdater;
-};
-
-// sBatteryObserver is owned by the IO thread. Only the IO thread may
-// create or destroy it.
-static StaticRefPtr<BatteryObserver> sBatteryObserver;
-
-static void
-RegisterBatteryObserverIOThread()
-{
- MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
- MOZ_ASSERT(!sBatteryObserver);
-
- sBatteryObserver = new BatteryObserver();
- RegisterUeventListener(sBatteryObserver);
-}
-
-void
-EnableBatteryNotifications()
-{
- XRE_GetIOMessageLoop()->PostTask(
- NewRunnableFunction(RegisterBatteryObserverIOThread));
-}
-
-static void
-UnregisterBatteryObserverIOThread()
-{
- MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
- MOZ_ASSERT(sBatteryObserver);
-
- UnregisterUeventListener(sBatteryObserver);
- sBatteryObserver = nullptr;
-}
-
-void
-DisableBatteryNotifications()
-{
- XRE_GetIOMessageLoop()->PostTask(
- NewRunnableFunction(UnregisterBatteryObserverIOThread));
-}
-
-static bool
-GetCurrentBatteryCharge(int* aCharge)
-{
- bool success = ReadSysFile("/sys/class/power_supply/battery/capacity",
- aCharge);
- if (!success) {
- return false;
- }
-
- #ifdef DEBUG
- if ((*aCharge < 0) || (*aCharge > 100)) {
- HAL_LOG("charge level contains unknown value: %d", *aCharge);
- }
- #endif
-
- return (*aCharge >= 0) && (*aCharge <= 100);
-}
-
-static bool
-GetCurrentBatteryCharging(int* aCharging)
-{
- static const DebugOnly<int> BATTERY_NOT_CHARGING = 0;
- static const int BATTERY_CHARGING_USB = 1;
- static const int BATTERY_CHARGING_AC = 2;
-
- // Generic device support
-
- int chargingSrc;
- bool success =
- ReadSysFile("/sys/class/power_supply/battery/charging_source", &chargingSrc);
-
- if (success) {
- #ifdef DEBUG
- if (chargingSrc != BATTERY_NOT_CHARGING &&
- chargingSrc != BATTERY_CHARGING_USB &&
- chargingSrc != BATTERY_CHARGING_AC) {
- HAL_LOG("charging_source contained unknown value: %d", chargingSrc);
- }
- #endif
-
- *aCharging = (chargingSrc == BATTERY_CHARGING_USB ||
- chargingSrc == BATTERY_CHARGING_AC);
- return true;
- }
-
- // Otoro device support
-
- char chargingSrcString[16];
-
- success = ReadSysFile("/sys/class/power_supply/battery/status",
- chargingSrcString, sizeof(chargingSrcString));
- if (success) {
- *aCharging = strcmp(chargingSrcString, "Charging") == 0 ||
- strcmp(chargingSrcString, "Full") == 0;
- return true;
- }
-
- return false;
-}
-
-void
-GetCurrentBatteryInformation(hal::BatteryInformation* aBatteryInfo)
-{
- int charge;
- static bool previousCharging = false;
- static double previousLevel = 0.0, remainingTime = 0.0;
- static struct timespec lastLevelChange;
- struct timespec now;
- double dtime, dlevel;
-
- if (GetCurrentBatteryCharge(&charge)) {
- aBatteryInfo->level() = (double)charge / 100.0;
- } else {
- aBatteryInfo->level() = dom::battery::kDefaultLevel;
- }
-
- int charging;
-
- if (GetCurrentBatteryCharging(&charging)) {
- aBatteryInfo->charging() = charging;
- } else {
- aBatteryInfo->charging() = true;
- }
-
- if (aBatteryInfo->charging() != previousCharging){
- aBatteryInfo->remainingTime() = dom::battery::kUnknownRemainingTime;
- memset(&lastLevelChange, 0, sizeof(struct timespec));
- remainingTime = 0.0;
- }
-
- if (aBatteryInfo->charging()) {
- if (aBatteryInfo->level() == 1.0) {
- aBatteryInfo->remainingTime() = dom::battery::kDefaultRemainingTime;
- } else if (aBatteryInfo->level() != previousLevel){
- if (lastLevelChange.tv_sec != 0) {
- clock_gettime(CLOCK_MONOTONIC, &now);
- dtime = now.tv_sec - lastLevelChange.tv_sec;
- dlevel = aBatteryInfo->level() - previousLevel;
-
- if (dlevel <= 0.0) {
- aBatteryInfo->remainingTime() = dom::battery::kUnknownRemainingTime;
- } else {
- remainingTime = (double) round(dtime / dlevel * (1.0 - aBatteryInfo->level()));
- aBatteryInfo->remainingTime() = remainingTime;
- }
-
- lastLevelChange = now;
- } else { // lastLevelChange.tv_sec == 0
- clock_gettime(CLOCK_MONOTONIC, &lastLevelChange);
- aBatteryInfo->remainingTime() = dom::battery::kUnknownRemainingTime;
- }
-
- } else {
- clock_gettime(CLOCK_MONOTONIC, &now);
- dtime = now.tv_sec - lastLevelChange.tv_sec;
- if (dtime < remainingTime) {
- aBatteryInfo->remainingTime() = round(remainingTime - dtime);
- } else {
- aBatteryInfo->remainingTime() = dom::battery::kUnknownRemainingTime;
- }
-
- }
-
- } else {
- if (aBatteryInfo->level() == 0.0) {
- aBatteryInfo->remainingTime() = dom::battery::kDefaultRemainingTime;
- } else if (aBatteryInfo->level() != previousLevel){
- if (lastLevelChange.tv_sec != 0) {
- clock_gettime(CLOCK_MONOTONIC, &now);
- dtime = now.tv_sec - lastLevelChange.tv_sec;
- dlevel = previousLevel - aBatteryInfo->level();
-
- if (dlevel <= 0.0) {
- aBatteryInfo->remainingTime() = dom::battery::kUnknownRemainingTime;
- } else {
- remainingTime = (double) round(dtime / dlevel * aBatteryInfo->level());
- aBatteryInfo->remainingTime() = remainingTime;
- }
-
- lastLevelChange = now;
- } else { // lastLevelChange.tv_sec == 0
- clock_gettime(CLOCK_MONOTONIC, &lastLevelChange);
- aBatteryInfo->remainingTime() = dom::battery::kUnknownRemainingTime;
- }
-
- } else {
- clock_gettime(CLOCK_MONOTONIC, &now);
- dtime = now.tv_sec - lastLevelChange.tv_sec;
- if (dtime < remainingTime) {
- aBatteryInfo->remainingTime() = round(remainingTime - dtime);
- } else {
- aBatteryInfo->remainingTime() = dom::battery::kUnknownRemainingTime;
- }
-
- }
- }
-
- previousCharging = aBatteryInfo->charging();
- previousLevel = aBatteryInfo->level();
-}
-
-namespace {
-
-// We can write to screenEnabledFilename to enable/disable the screen, but when
-// we read, we always get "mem"! So we have to keep track ourselves whether
-// the screen is on or not.
-bool sScreenEnabled = true;
-
-// We can read wakeLockFilename to find out whether the cpu wake lock
-// is already acquired, but reading and parsing it is a lot more work
-// than tracking it ourselves, and it won't be accurate anyway (kernel
-// internal wake locks aren't counted here.)
-bool sCpuSleepAllowed = true;
-
-// Some CPU wake locks may be acquired internally in HAL. We use a counter to
-// keep track of these needs. Note we have to hold |sInternalLockCpuMutex|
-// when reading or writing this variable to ensure thread-safe.
-int32_t sInternalLockCpuCount = 0;
-
-} // namespace
-
-bool
-GetScreenEnabled()
-{
- return sScreenEnabled;
-}
-
-void
-SetScreenEnabled(bool aEnabled)
-{
- GetGonkDisplay()->SetEnabled(aEnabled);
- sScreenEnabled = aEnabled;
-}
-
-bool
-GetKeyLightEnabled()
-{
- LightConfiguration config;
- bool ok = GetLight(eHalLightID_Buttons, &config);
- if (ok) {
- return (config.color != 0x00000000);
- }
- return false;
-}
-
-void
-SetKeyLightEnabled(bool aEnabled)
-{
- LightConfiguration config;
- config.mode = eHalLightMode_User;
- config.flash = eHalLightFlash_None;
- config.flashOnMS = config.flashOffMS = 0;
- config.color = 0x00000000;
-
- if (aEnabled) {
- // Convert the value in [0, 1] to an int between 0 and 255 and then convert
- // it to a color. Note that the high byte is FF, corresponding to the alpha
- // channel.
- double brightness = GetScreenBrightness();
- uint32_t val = static_cast<int>(round(brightness * 255.0));
- uint32_t color = (0xff<<24) + (val<<16) + (val<<8) + val;
-
- config.color = color;
- }
-
- SetLight(eHalLightID_Buttons, config);
- SetLight(eHalLightID_Keyboard, config);
-}
-
-double
-GetScreenBrightness()
-{
- LightConfiguration config;
- LightType light = eHalLightID_Backlight;
-
- bool ok = GetLight(light, &config);
- if (ok) {
- // backlight is brightness only, so using one of the RGB elements as value.
- int brightness = config.color & 0xFF;
- return brightness / 255.0;
- }
- // If GetLight fails, it's because the light doesn't exist. So return
- // a value corresponding to "off".
- return 0;
-}
-
-void
-SetScreenBrightness(double brightness)
-{
- // Don't use De Morgan's law to push the ! into this expression; we want to
- // catch NaN too.
- if (!(0 <= brightness && brightness <= 1)) {
- HAL_LOG("SetScreenBrightness: Dropping illegal brightness %f.", brightness);
- return;
- }
-
- // Convert the value in [0, 1] to an int between 0 and 255 and convert to a color
- // note that the high byte is FF, corresponding to the alpha channel.
- uint32_t val = static_cast<int>(round(brightness * 255.0));
- uint32_t color = (0xff<<24) + (val<<16) + (val<<8) + val;
-
- LightConfiguration config;
- config.mode = eHalLightMode_User;
- config.flash = eHalLightFlash_None;
- config.flashOnMS = config.flashOffMS = 0;
- config.color = color;
- SetLight(eHalLightID_Backlight, config);
- if (GetKeyLightEnabled()) {
- SetLight(eHalLightID_Buttons, config);
- SetLight(eHalLightID_Keyboard, config);
- }
-}
-
-static StaticMutex sInternalLockCpuMutex;
-
-static void
-UpdateCpuSleepState()
-{
- const char *wakeLockFilename = "/sys/power/wake_lock";
- const char *wakeUnlockFilename = "/sys/power/wake_unlock";
-
- sInternalLockCpuMutex.AssertCurrentThreadOwns();
- bool allowed = sCpuSleepAllowed && !sInternalLockCpuCount;
- WriteSysFile(allowed ? wakeUnlockFilename : wakeLockFilename, "gecko");
-}
-
-static void
-InternalLockCpu() {
- StaticMutexAutoLock lock(sInternalLockCpuMutex);
- ++sInternalLockCpuCount;
- UpdateCpuSleepState();
-}
-
-static void
-InternalUnlockCpu() {
- StaticMutexAutoLock lock(sInternalLockCpuMutex);
- --sInternalLockCpuCount;
- UpdateCpuSleepState();
-}
-
-bool
-GetCpuSleepAllowed()
-{
- return sCpuSleepAllowed;
-}
-
-void
-SetCpuSleepAllowed(bool aAllowed)
-{
- StaticMutexAutoLock lock(sInternalLockCpuMutex);
- sCpuSleepAllowed = aAllowed;
- UpdateCpuSleepState();
-}
-
-void
-AdjustSystemClock(int64_t aDeltaMilliseconds)
-{
- int fd;
- struct timespec now;
-
- if (aDeltaMilliseconds == 0) {
- return;
- }
-
- // Preventing context switch before setting system clock
- sched_yield();
- clock_gettime(CLOCK_REALTIME, &now);
- now.tv_sec += (time_t)(aDeltaMilliseconds / 1000LL);
- now.tv_nsec += (long)((aDeltaMilliseconds % 1000LL) * NsecPerMsec);
- if (now.tv_nsec >= NsecPerSec) {
- now.tv_sec += 1;
- now.tv_nsec -= NsecPerSec;
- }
-
- if (now.tv_nsec < 0) {
- now.tv_nsec += NsecPerSec;
- now.tv_sec -= 1;
- }
-
- do {
- fd = open("/dev/alarm", O_RDWR);
- } while (fd == -1 && errno == EINTR);
- ScopedClose autoClose(fd);
- if (fd < 0) {
- HAL_LOG("Failed to open /dev/alarm: %s", strerror(errno));
- return;
- }
-
- if (ioctl(fd, ANDROID_ALARM_SET_RTC, &now) < 0) {
- HAL_LOG("ANDROID_ALARM_SET_RTC failed: %s", strerror(errno));
- }
-
- hal::NotifySystemClockChange(aDeltaMilliseconds);
-}
-
-int32_t
-GetTimezoneOffset()
-{
- PRExplodedTime prTime;
- PR_ExplodeTime(PR_Now(), PR_LocalTimeParameters, &prTime);
-
- // Daylight saving time (DST) will be taken into account.
- int32_t offset = prTime.tm_params.tp_gmt_offset;
- offset += prTime.tm_params.tp_dst_offset;
-
- // Returns the timezone offset relative to UTC in minutes.
- return -(offset / 60);
-}
-
-static int32_t sKernelTimezoneOffset = 0;
-
-static void
-UpdateKernelTimezone(int32_t timezoneOffset)
-{
- if (sKernelTimezoneOffset == timezoneOffset) {
- return;
- }
-
- // Tell the kernel about the new time zone as well, so that FAT filesystems
- // will get local timestamps rather than UTC timestamps.
- //
- // We assume that /init.rc has a sysclktz entry so that settimeofday has
- // already been called once before we call it (there is a side-effect in
- // the kernel the very first time settimeofday is called where it does some
- // special processing if you only set the timezone).
- struct timezone tz;
- memset(&tz, 0, sizeof(tz));
- tz.tz_minuteswest = timezoneOffset;
- settimeofday(nullptr, &tz);
- sKernelTimezoneOffset = timezoneOffset;
-}
-
-void
-SetTimezone(const nsCString& aTimezoneSpec)
-{
- if (aTimezoneSpec.Equals(GetTimezone())) {
- // Even though the timezone hasn't changed, we still need to tell the
- // kernel what the current timezone is. The timezone is persisted in
- // a property and doesn't change across reboots, but the kernel still
- // needs to be updated on every boot.
- UpdateKernelTimezone(GetTimezoneOffset());
- return;
- }
-
- int32_t oldTimezoneOffsetMinutes = GetTimezoneOffset();
- property_set("persist.sys.timezone", aTimezoneSpec.get());
- // This function is automatically called by the other time conversion
- // functions that depend on the timezone. To be safe, we call it manually.
- tzset();
- int32_t newTimezoneOffsetMinutes = GetTimezoneOffset();
- UpdateKernelTimezone(newTimezoneOffsetMinutes);
- hal::NotifySystemTimezoneChange(
- hal::SystemTimezoneChangeInformation(
- oldTimezoneOffsetMinutes, newTimezoneOffsetMinutes));
-}
-
-nsCString
-GetTimezone()
-{
- char timezone[32];
- property_get("persist.sys.timezone", timezone, "");
- return nsCString(timezone);
-}
-
-void
-EnableSystemClockChangeNotifications()
-{
-}
-
-void
-DisableSystemClockChangeNotifications()
-{
-}
-
-void
-EnableSystemTimezoneChangeNotifications()
-{
-}
-
-void
-DisableSystemTimezoneChangeNotifications()
-{
-}
-
-// Nothing to do here. Gonk widgetry always listens for screen
-// orientation changes.
-void
-EnableScreenConfigurationNotifications()
-{
-}
-
-void
-DisableScreenConfigurationNotifications()
-{
-}
-
-void
-GetCurrentScreenConfiguration(hal::ScreenConfiguration* aScreenConfiguration)
-{
- RefPtr<nsScreenGonk> screen = nsScreenManagerGonk::GetPrimaryScreen();
- *aScreenConfiguration = screen->GetConfiguration();
-}
-
-bool
-LockScreenOrientation(const dom::ScreenOrientationInternal& aOrientation)
-{
- return OrientationObserver::GetInstance()->LockScreenOrientation(aOrientation);
-}
-
-void
-UnlockScreenOrientation()
-{
- OrientationObserver::GetInstance()->UnlockScreenOrientation();
-}
-
-// This thread will wait for the alarm firing by a blocking IO.
-static pthread_t sAlarmFireWatcherThread;
-
-// If |sAlarmData| is non-null, it's owned by the alarm-watcher thread.
-struct AlarmData {
-public:
- AlarmData(int aFd) : mFd(aFd),
- mGeneration(sNextGeneration++),
- mShuttingDown(false) {}
- ScopedClose mFd;
- int mGeneration;
- bool mShuttingDown;
-
- static int sNextGeneration;
-
-};
-
-int AlarmData::sNextGeneration = 0;
-
-AlarmData* sAlarmData = nullptr;
-
-class AlarmFiredEvent : public Runnable {
-public:
- AlarmFiredEvent(int aGeneration) : mGeneration(aGeneration) {}
-
- NS_IMETHOD Run() override {
- // Guard against spurious notifications caused by an alarm firing
- // concurrently with it being disabled.
- if (sAlarmData && !sAlarmData->mShuttingDown &&
- mGeneration == sAlarmData->mGeneration) {
- hal::NotifyAlarmFired();
- }
- // The fired alarm event has been delivered to the observer (if needed);
- // we can now release a CPU wake lock.
- InternalUnlockCpu();
- return NS_OK;
- }
-
-private:
- int mGeneration;
-};
-
-// Runs on alarm-watcher thread.
-static void
-DestroyAlarmData(void* aData)
-{
- AlarmData* alarmData = static_cast<AlarmData*>(aData);
- delete alarmData;
-}
-
-// Runs on alarm-watcher thread.
-void ShutDownAlarm(int aSigno)
-{
- if (aSigno == SIGUSR1 && sAlarmData) {
- sAlarmData->mShuttingDown = true;
- }
- return;
-}
-
-static void*
-WaitForAlarm(void* aData)
-{
- pthread_cleanup_push(DestroyAlarmData, aData);
-
- AlarmData* alarmData = static_cast<AlarmData*>(aData);
-
- while (!alarmData->mShuttingDown) {
- int alarmTypeFlags = 0;
-
- // ALARM_WAIT apparently will block even if an alarm hasn't been
- // programmed, although this behavior doesn't seem to be
- // documented. We rely on that here to avoid spinning the CPU
- // while awaiting an alarm to be programmed.
- do {
- alarmTypeFlags = ioctl(alarmData->mFd, ANDROID_ALARM_WAIT);
- } while (alarmTypeFlags < 0 && errno == EINTR &&
- !alarmData->mShuttingDown);
-
- if (!alarmData->mShuttingDown && alarmTypeFlags >= 0 &&
- (alarmTypeFlags & ANDROID_ALARM_RTC_WAKEUP_MASK)) {
- // To make sure the observer can get the alarm firing notification
- // *on time* (the system won't sleep during the process in any way),
- // we need to acquire a CPU wake lock before firing the alarm event.
- InternalLockCpu();
- RefPtr<AlarmFiredEvent> event =
- new AlarmFiredEvent(alarmData->mGeneration);
- NS_DispatchToMainThread(event);
- }
- }
-
- pthread_cleanup_pop(1);
- return nullptr;
-}
-
-bool
-EnableAlarm()
-{
- MOZ_ASSERT(!sAlarmData);
-
- int alarmFd = open("/dev/alarm", O_RDWR);
- if (alarmFd < 0) {
- HAL_LOG("Failed to open alarm device: %s.", strerror(errno));
- return false;
- }
-
- UniquePtr<AlarmData> alarmData = MakeUnique<AlarmData>(alarmFd);
-
- struct sigaction actions;
- memset(&actions, 0, sizeof(actions));
- sigemptyset(&actions.sa_mask);
- actions.sa_flags = 0;
- actions.sa_handler = ShutDownAlarm;
- if (sigaction(SIGUSR1, &actions, nullptr)) {
- HAL_LOG("Failed to set SIGUSR1 signal for alarm-watcher thread.");
- return false;
- }
-
- pthread_attr_t attr;
- pthread_attr_init(&attr);
- pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
-
- int status = pthread_create(&sAlarmFireWatcherThread, &attr, WaitForAlarm,
- alarmData.get());
- if (status) {
- alarmData.reset();
- HAL_LOG("Failed to create alarm-watcher thread. Status: %d.", status);
- return false;
- }
-
- pthread_attr_destroy(&attr);
-
- // The thread owns this now. We only hold a pointer.
- sAlarmData = alarmData.release();
- return true;
-}
-
-void
-DisableAlarm()
-{
- MOZ_ASSERT(sAlarmData);
-
- // NB: this must happen-before the thread cancellation.
- sAlarmData = nullptr;
-
- // The cancel will interrupt the thread and destroy it, freeing the
- // data pointed at by sAlarmData.
- DebugOnly<int> err = pthread_kill(sAlarmFireWatcherThread, SIGUSR1);
- MOZ_ASSERT(!err);
-}
-
-bool
-SetAlarm(int32_t aSeconds, int32_t aNanoseconds)
-{
- if (!sAlarmData) {
- HAL_LOG("We should have enabled the alarm.");
- return false;
- }
-
- struct timespec ts;
- ts.tv_sec = aSeconds;
- ts.tv_nsec = aNanoseconds;
-
- // Currently we only support RTC wakeup alarm type.
- const int result = ioctl(sAlarmData->mFd,
- ANDROID_ALARM_SET(ANDROID_ALARM_RTC_WAKEUP), &ts);
-
- if (result < 0) {
- HAL_LOG("Unable to set alarm: %s.", strerror(errno));
- return false;
- }
-
- return true;
-}
-
-static int
-OomAdjOfOomScoreAdj(int aOomScoreAdj)
-{
- // Convert OOM adjustment from the domain of /proc/<pid>/oom_score_adj
- // to the domain of /proc/<pid>/oom_adj.
-
- int adj;
-
- if (aOomScoreAdj < 0) {
- adj = (OOM_DISABLE * aOomScoreAdj) / OOM_SCORE_ADJ_MIN;
- } else {
- adj = (OOM_ADJUST_MAX * aOomScoreAdj) / OOM_SCORE_ADJ_MAX;
- }
-
- return adj;
-}
-
-static void
-RoundOomScoreAdjUpWithLRU(int& aOomScoreAdj, uint32_t aLRU)
-{
- // We want to add minimum value to round OomScoreAdj up according to
- // the steps by aLRU.
- aOomScoreAdj +=
- ceil(((float)OOM_SCORE_ADJ_MAX / OOM_ADJUST_MAX) * aLRU);
-}
-
-#define OOM_LOG(level, args...) __android_log_print(level, "OomLogger", ##args)
-class OomVictimLogger final
- : public nsIObserver
-{
-public:
- OomVictimLogger()
- : mLastLineChecked(-1.0),
- mRegexes(nullptr)
- {
- // Enable timestamps in kernel's printk
- WriteSysFile("/sys/module/printk/parameters/time", "Y");
- }
-
- NS_DECL_ISUPPORTS
- NS_DECL_NSIOBSERVER
-
-protected:
- ~OomVictimLogger() {}
-
-private:
- double mLastLineChecked;
- UniqueFreePtr<regex_t> mRegexes;
-};
-NS_IMPL_ISUPPORTS(OomVictimLogger, nsIObserver);
-
-NS_IMETHODIMP
-OomVictimLogger::Observe(
- nsISupports* aSubject,
- const char* aTopic,
- const char16_t* aData)
-{
- nsDependentCString event_type(aTopic);
- if (!event_type.EqualsLiteral("ipc:content-shutdown")) {
- return NS_OK;
- }
-
- // OOM message finding regexes
- const char* const regexes_raw[] = {
- ".*select.*to kill.*",
- ".*send sigkill to.*",
- ".*lowmem_shrink.*",
- ".*[Oo]ut of [Mm]emory.*",
- ".*[Kk]ill [Pp]rocess.*",
- ".*[Kk]illed [Pp]rocess.*",
- ".*oom-killer.*",
- // The regexes below are for the output of dump_task from oom_kill.c
- // 1st - title 2nd - body lines (8 ints and a string)
- // oom_adj and oom_score_adj can be negative
- "\\[ pid \\] uid tgid total_vm rss cpu oom_adj oom_score_adj name",
- "\\[.*[0-9][0-9]*\\][ ]*[0-9][0-9]*[ ]*[0-9][0-9]*[ ]*[0-9][0-9]*[ ]*[0-9][0-9]*[ ]*[0-9][0-9]*[ ]*.[0-9][0-9]*[ ]*.[0-9][0-9]*.*"
- };
- const size_t regex_count = ArrayLength(regexes_raw);
-
- // Compile our regex just in time
- if (!mRegexes) {
- UniqueFreePtr<regex_t> regexes(
- static_cast<regex_t*>(malloc(sizeof(regex_t) * regex_count))
- );
- mRegexes.swap(regexes);
- for (size_t i = 0; i < regex_count; i++) {
- int compilation_err =
- regcomp(&(mRegexes.get()[i]), regexes_raw[i], REG_NOSUB);
- if (compilation_err) {
- OOM_LOG(ANDROID_LOG_ERROR, "Cannot compile regex \"%s\"\n", regexes_raw[i]);
- return NS_OK;
- }
- }
- }
-
-#ifndef KLOG_SIZE_BUFFER
- // Upstream bionic in commit
- // e249b059637b49a285ed9f58a2a18bfd054e5d95
- // deprecated the old klog defs.
- // Our current bionic does not hit this
- // change yet so handle the future change.
- // (ICS doesn't have KLOG_SIZE_BUFFER but
- // JB and onwards does.)
- #define KLOG_SIZE_BUFFER KLOG_WRITE
-#endif
- // Retreive kernel log
- int msg_buf_size = klogctl(KLOG_SIZE_BUFFER, NULL, 0);
- UniqueFreePtr<char> msg_buf(static_cast<char *>(malloc(msg_buf_size + 1)));
- int read_size = klogctl(KLOG_READ_ALL, msg_buf.get(), msg_buf_size);
-
- // Turn buffer into cstring
- read_size = read_size > msg_buf_size ? msg_buf_size : read_size;
- msg_buf.get()[read_size] = '\0';
-
- // Foreach line
- char* line_end;
- char* line_begin = msg_buf.get();
- for (; (line_end = strchr(line_begin, '\n')); line_begin = line_end + 1) {
- // make line into cstring
- *line_end = '\0';
-
- // Note: Kernel messages look like:
- // <5>[63648.286409] sd 35:0:0:0: Attached scsi generic sg1 type 0
- // 5 is the loging level
- // [*] is the time timestamp, seconds since boot
- // last comes the logged message
-
- // Since the logging level can be a string we must
- // skip it since scanf lacks wildcard matching
- char* timestamp_begin = strchr(line_begin, '[');
- char after_float;
- double lineTimestamp = -1;
- bool lineTimestampFound = false;
- if (timestamp_begin &&
- // Note: scanf treats a ' ' as [ ]*
- // Note: scanf treats [ %lf] as [ %lf thus we must check
- // for the closing bracket outselves.
- 2 == sscanf(timestamp_begin, "[ %lf%c", &lineTimestamp, &after_float) &&
- after_float == ']') {
- if (lineTimestamp <= mLastLineChecked) {
- continue;
- }
-
- lineTimestampFound = true;
- mLastLineChecked = lineTimestamp;
- }
-
- // Log interesting lines
- for (size_t i = 0; i < regex_count; i++) {
- int matching = !regexec(&(mRegexes.get()[i]), line_begin, 0, NULL, 0);
- if (matching) {
- // Log content of kernel message. We try to skip the ], but if for
- // some reason (most likely due to buffer overflow/wraparound), we
- // can't find the ] then we just log the entire line.
- char* endOfTimestamp = strchr(line_begin, ']');
- if (endOfTimestamp && endOfTimestamp[1] == ' ') {
- // skip the ] and the space that follows it
- line_begin = endOfTimestamp + 2;
- }
- if (!lineTimestampFound) {
- OOM_LOG(ANDROID_LOG_WARN, "following kill message may be a duplicate");
- }
- OOM_LOG(ANDROID_LOG_ERROR, "[Kill]: %s\n", line_begin);
- break;
- }
- }
- }
-
- return NS_OK;
-}
-
-/**
- * Wraps a particular ProcessPriority, giving us easy access to the prefs that
- * are relevant to it.
- *
- * Creating a PriorityClass also ensures that the control group is created.
- */
-class PriorityClass
-{
-public:
- /**
- * Create a PriorityClass for the given ProcessPriority. This implicitly
- * reads the relevant prefs and opens the cgroup.procs file of the relevant
- * control group caching its file descriptor for later use.
- */
- PriorityClass(ProcessPriority aPriority);
-
- /**
- * Closes the file descriptor for the cgroup.procs file of the associated
- * control group.
- */
- ~PriorityClass();
-
- PriorityClass(const PriorityClass& aOther);
- PriorityClass& operator=(const PriorityClass& aOther);
-
- ProcessPriority Priority()
- {
- return mPriority;
- }
-
- int32_t OomScoreAdj()
- {
- return clamped<int32_t>(mOomScoreAdj, OOM_SCORE_ADJ_MIN, OOM_SCORE_ADJ_MAX);
- }
-
- int32_t KillUnderKB()
- {
- return mKillUnderKB;
- }
-
- nsCString CGroup()
- {
- return mGroup;
- }
-
- /**
- * Adds a process to this priority class, this moves the process' PID into
- * the associated control group.
- *
- * @param aPid The PID of the process to be added.
- */
- void AddProcess(int aPid);
-
-private:
- ProcessPriority mPriority;
- int32_t mOomScoreAdj;
- int32_t mKillUnderKB;
- int mCpuCGroupProcsFd;
- int mMemCGroupProcsFd;
- nsCString mGroup;
-
- /**
- * Return a string that identifies where we can find the value of aPref
- * that's specific to mPriority. For example, we might return
- * "hal.processPriorityManager.gonk.FOREGROUND_HIGH.oomScoreAdjust".
- */
- nsCString PriorityPrefName(const char* aPref)
- {
- return nsPrintfCString("hal.processPriorityManager.gonk.%s.%s",
- ProcessPriorityToString(mPriority), aPref);
- }
-
- /**
- * Get the full path of the cgroup.procs file associated with the group.
- */
- nsCString CpuCGroupProcsFilename()
- {
- nsCString cgroupName = mGroup;
-
- /* If mGroup is empty, our cgroup.procs file is the root procs file,
- * located at /dev/cpuctl/cgroup.procs. Otherwise our procs file is
- * /dev/cpuctl/NAME/cgroup.procs. */
-
- if (!mGroup.IsEmpty()) {
- cgroupName.AppendLiteral("/");
- }
-
- return NS_LITERAL_CSTRING("/dev/cpuctl/") + cgroupName +
- NS_LITERAL_CSTRING("cgroup.procs");
- }
-
- nsCString MemCGroupProcsFilename()
- {
- nsCString cgroupName = mGroup;
-
- /* If mGroup is empty, our cgroup.procs file is the root procs file,
- * located at /sys/fs/cgroup/memory/cgroup.procs. Otherwise our procs
- * file is /sys/fs/cgroup/memory/NAME/cgroup.procs. */
-
- if (!mGroup.IsEmpty()) {
- cgroupName.AppendLiteral("/");
- }
-
- return NS_LITERAL_CSTRING("/sys/fs/cgroup/memory/") + cgroupName +
- NS_LITERAL_CSTRING("cgroup.procs");
- }
-
- int OpenCpuCGroupProcs()
- {
- return open(CpuCGroupProcsFilename().get(), O_WRONLY);
- }
-
- int OpenMemCGroupProcs()
- {
- return open(MemCGroupProcsFilename().get(), O_WRONLY);
- }
-};
-
-/**
- * Try to create the cgroup for the given PriorityClass, if it doesn't already
- * exist. This essentially implements mkdir -p; that is, we create parent
- * cgroups as necessary. The group parameters are also set according to
- * the corresponding preferences.
- *
- * @param aGroup The name of the group.
- * @return true if we successfully created the cgroup, or if it already
- * exists. Otherwise, return false.
- */
-static bool
-EnsureCpuCGroupExists(const nsACString &aGroup)
-{
- NS_NAMED_LITERAL_CSTRING(kDevCpuCtl, "/dev/cpuctl/");
- NS_NAMED_LITERAL_CSTRING(kSlash, "/");
-
- nsAutoCString groupName(aGroup);
- HAL_LOG("EnsureCpuCGroupExists for group '%s'", groupName.get());
-
- nsAutoCString prefPrefix("hal.processPriorityManager.gonk.cgroups.");
-
- /* If cgroup is not empty, append the cgroup name and a dot to obtain the
- * group specific preferences. */
- if (!aGroup.IsEmpty()) {
- prefPrefix += aGroup + NS_LITERAL_CSTRING(".");
- }
-
- nsAutoCString cpuSharesPref(prefPrefix + NS_LITERAL_CSTRING("cpu_shares"));
- int cpuShares = Preferences::GetInt(cpuSharesPref.get());
-
- nsAutoCString cpuNotifyOnMigratePref(prefPrefix
- + NS_LITERAL_CSTRING("cpu_notify_on_migrate"));
- int cpuNotifyOnMigrate = Preferences::GetInt(cpuNotifyOnMigratePref.get());
-
- // Create mCGroup and its parent directories, as necessary.
- nsCString cgroupIter = aGroup + kSlash;
-
- int32_t offset = 0;
- while ((offset = cgroupIter.FindChar('/', offset)) != -1) {
- nsAutoCString path = kDevCpuCtl + Substring(cgroupIter, 0, offset);
- int rv = mkdir(path.get(), 0744);
-
- if (rv == -1 && errno != EEXIST) {
- HAL_LOG("Could not create the %s control group.", path.get());
- return false;
- }
-
- offset++;
- }
- HAL_LOG("EnsureCpuCGroupExists created group '%s'", groupName.get());
-
- nsAutoCString pathPrefix(kDevCpuCtl + aGroup + kSlash);
- nsAutoCString cpuSharesPath(pathPrefix + NS_LITERAL_CSTRING("cpu.shares"));
- if (cpuShares && !WriteSysFile(cpuSharesPath.get(),
- nsPrintfCString("%d", cpuShares).get())) {
- HAL_LOG("Could not set the cpu share for group %s", cpuSharesPath.get());
- return false;
- }
-
- nsAutoCString notifyOnMigratePath(pathPrefix
- + NS_LITERAL_CSTRING("cpu.notify_on_migrate"));
- if (!WriteSysFile(notifyOnMigratePath.get(),
- nsPrintfCString("%d", cpuNotifyOnMigrate).get())) {
- HAL_LOG("Could not set the cpu migration notification flag for group %s",
- notifyOnMigratePath.get());
- return false;
- }
-
- return true;
-}
-
-static bool
-EnsureMemCGroupExists(const nsACString &aGroup)
-{
- NS_NAMED_LITERAL_CSTRING(kMemCtl, "/sys/fs/cgroup/memory/");
- NS_NAMED_LITERAL_CSTRING(kSlash, "/");
-
- nsAutoCString groupName(aGroup);
- HAL_LOG("EnsureMemCGroupExists for group '%s'", groupName.get());
-
- nsAutoCString prefPrefix("hal.processPriorityManager.gonk.cgroups.");
-
- /* If cgroup is not empty, append the cgroup name and a dot to obtain the
- * group specific preferences. */
- if (!aGroup.IsEmpty()) {
- prefPrefix += aGroup + NS_LITERAL_CSTRING(".");
- }
-
- nsAutoCString memSwappinessPref(prefPrefix + NS_LITERAL_CSTRING("memory_swappiness"));
- int memSwappiness = Preferences::GetInt(memSwappinessPref.get());
-
- // Create mCGroup and its parent directories, as necessary.
- nsCString cgroupIter = aGroup + kSlash;
-
- int32_t offset = 0;
- while ((offset = cgroupIter.FindChar('/', offset)) != -1) {
- nsAutoCString path = kMemCtl + Substring(cgroupIter, 0, offset);
- int rv = mkdir(path.get(), 0744);
-
- if (rv == -1 && errno != EEXIST) {
- HAL_LOG("Could not create the %s control group.", path.get());
- return false;
- }
-
- offset++;
- }
- HAL_LOG("EnsureMemCGroupExists created group '%s'", groupName.get());
-
- nsAutoCString pathPrefix(kMemCtl + aGroup + kSlash);
- nsAutoCString memSwappinessPath(pathPrefix + NS_LITERAL_CSTRING("memory.swappiness"));
- if (!WriteSysFile(memSwappinessPath.get(),
- nsPrintfCString("%d", memSwappiness).get())) {
- HAL_LOG("Could not set the memory.swappiness for group %s", memSwappinessPath.get());
- return false;
- }
- HAL_LOG("Set memory.swappiness for group %s to %d", memSwappinessPath.get(), memSwappiness);
-
- return true;
-}
-
-PriorityClass::PriorityClass(ProcessPriority aPriority)
- : mPriority(aPriority)
- , mOomScoreAdj(0)
- , mKillUnderKB(0)
- , mCpuCGroupProcsFd(-1)
- , mMemCGroupProcsFd(-1)
-{
- DebugOnly<nsresult> rv;
-
- rv = Preferences::GetInt(PriorityPrefName("OomScoreAdjust").get(),
- &mOomScoreAdj);
- MOZ_ASSERT(NS_SUCCEEDED(rv), "Missing oom_score_adj preference");
-
- rv = Preferences::GetInt(PriorityPrefName("KillUnderKB").get(),
- &mKillUnderKB);
-
- rv = Preferences::GetCString(PriorityPrefName("cgroup").get(), &mGroup);
- MOZ_ASSERT(NS_SUCCEEDED(rv), "Missing control group preference");
-
- if (EnsureCpuCGroupExists(mGroup)) {
- mCpuCGroupProcsFd = OpenCpuCGroupProcs();
- }
- if (EnsureMemCGroupExists(mGroup)) {
- mMemCGroupProcsFd = OpenMemCGroupProcs();
- }
-}
-
-PriorityClass::~PriorityClass()
-{
- if (mCpuCGroupProcsFd != -1) {
- close(mCpuCGroupProcsFd);
- }
- if (mMemCGroupProcsFd != -1) {
- close(mMemCGroupProcsFd);
- }
-}
-
-PriorityClass::PriorityClass(const PriorityClass& aOther)
- : mPriority(aOther.mPriority)
- , mOomScoreAdj(aOther.mOomScoreAdj)
- , mKillUnderKB(aOther.mKillUnderKB)
- , mGroup(aOther.mGroup)
-{
- mCpuCGroupProcsFd = OpenCpuCGroupProcs();
- mMemCGroupProcsFd = OpenMemCGroupProcs();
-}
-
-PriorityClass& PriorityClass::operator=(const PriorityClass& aOther)
-{
- mPriority = aOther.mPriority;
- mOomScoreAdj = aOther.mOomScoreAdj;
- mKillUnderKB = aOther.mKillUnderKB;
- mGroup = aOther.mGroup;
- mCpuCGroupProcsFd = OpenCpuCGroupProcs();
- mMemCGroupProcsFd = OpenMemCGroupProcs();
- return *this;
-}
-
-void PriorityClass::AddProcess(int aPid)
-{
- if (mCpuCGroupProcsFd >= 0) {
- nsPrintfCString str("%d", aPid);
-
- if (write(mCpuCGroupProcsFd, str.get(), strlen(str.get())) < 0) {
- HAL_ERR("Couldn't add PID %d to the %s cpu control group", aPid, mGroup.get());
- }
- }
- if (mMemCGroupProcsFd >= 0) {
- nsPrintfCString str("%d", aPid);
-
- if (write(mMemCGroupProcsFd, str.get(), strlen(str.get())) < 0) {
- HAL_ERR("Couldn't add PID %d to the %s memory control group", aPid, mGroup.get());
- }
- }
-}
-
-/**
- * Get the PriorityClass associated with the given ProcessPriority.
- *
- * If you pass an invalid ProcessPriority value, we return null.
- *
- * The pointers returned here are owned by GetPriorityClass (don't free them
- * yourself). They are guaranteed to stick around until shutdown.
- */
-PriorityClass*
-GetPriorityClass(ProcessPriority aPriority)
-{
- static StaticAutoPtr<nsTArray<PriorityClass>> priorityClasses;
-
- // Initialize priorityClasses if this is the first time we're running this
- // method.
- if (!priorityClasses) {
- priorityClasses = new nsTArray<PriorityClass>();
- ClearOnShutdown(&priorityClasses);
-
- for (int32_t i = 0; i < NUM_PROCESS_PRIORITY; i++) {
- priorityClasses->AppendElement(PriorityClass(ProcessPriority(i)));
- }
- }
-
- if (aPriority < 0 ||
- static_cast<uint32_t>(aPriority) >= priorityClasses->Length()) {
- return nullptr;
- }
-
- return &(*priorityClasses)[aPriority];
-}
-
-static void
-EnsureKernelLowMemKillerParamsSet()
-{
- static bool kernelLowMemKillerParamsSet;
- if (kernelLowMemKillerParamsSet) {
- return;
- }
- kernelLowMemKillerParamsSet = true;
-
- HAL_LOG("Setting kernel's low-mem killer parameters.");
-
- // Set /sys/module/lowmemorykiller/parameters/{adj,minfree,notify_trigger}
- // according to our prefs. These files let us tune when the kernel kills
- // processes when we're low on memory, and when it notifies us that we're
- // running low on available memory.
- //
- // adj and minfree are both comma-separated lists of integers. If adj="A,B"
- // and minfree="X,Y", then the kernel will kill processes with oom_adj
- // A or higher once we have fewer than X pages of memory free, and will kill
- // processes with oom_adj B or higher once we have fewer than Y pages of
- // memory free.
- //
- // notify_trigger is a single integer. If we set notify_trigger=Z, then
- // we'll get notified when there are fewer than Z pages of memory free. (See
- // GonkMemoryPressureMonitoring.cpp.)
-
- // Build the adj and minfree strings.
- nsAutoCString adjParams;
- nsAutoCString minfreeParams;
-
- DebugOnly<int32_t> lowerBoundOfNextOomScoreAdj = OOM_SCORE_ADJ_MIN - 1;
- DebugOnly<int32_t> lowerBoundOfNextKillUnderKB = 0;
- int32_t countOfLowmemorykillerParametersSets = 0;
-
- long page_size = sysconf(_SC_PAGESIZE);
-
- for (int i = NUM_PROCESS_PRIORITY - 1; i >= 0; i--) {
- // The system doesn't function correctly if we're missing these prefs, so
- // crash loudly.
-
- PriorityClass* pc = GetPriorityClass(static_cast<ProcessPriority>(i));
-
- int32_t oomScoreAdj = pc->OomScoreAdj();
- int32_t killUnderKB = pc->KillUnderKB();
-
- if (killUnderKB == 0) {
- // ProcessPriority values like PROCESS_PRIORITY_FOREGROUND_KEYBOARD,
- // which has only OomScoreAdjust but lacks KillUnderMB value, will not
- // create new LMK parameters.
- continue;
- }
-
- // The LMK in kernel silently malfunctions if we assign the parameters
- // in non-increasing order, so we add this assertion here. See bug 887192.
- MOZ_ASSERT(oomScoreAdj > lowerBoundOfNextOomScoreAdj);
- MOZ_ASSERT(killUnderKB > lowerBoundOfNextKillUnderKB);
-
- // The LMK in kernel only accept 6 sets of LMK parameters. See bug 914728.
- MOZ_ASSERT(countOfLowmemorykillerParametersSets < 6);
-
- // adj is in oom_adj units.
- adjParams.AppendPrintf("%d,", OomAdjOfOomScoreAdj(oomScoreAdj));
-
- // minfree is in pages.
- minfreeParams.AppendPrintf("%ld,", killUnderKB * 1024 / page_size);
-
- lowerBoundOfNextOomScoreAdj = oomScoreAdj;
- lowerBoundOfNextKillUnderKB = killUnderKB;
- countOfLowmemorykillerParametersSets++;
- }
-
- // Strip off trailing commas.
- adjParams.Cut(adjParams.Length() - 1, 1);
- minfreeParams.Cut(minfreeParams.Length() - 1, 1);
- if (!adjParams.IsEmpty() && !minfreeParams.IsEmpty()) {
- WriteSysFile("/sys/module/lowmemorykiller/parameters/adj", adjParams.get());
- WriteSysFile("/sys/module/lowmemorykiller/parameters/minfree",
- minfreeParams.get());
- }
-
- // Set the low-memory-notification threshold.
- int32_t lowMemNotifyThresholdKB;
- if (NS_SUCCEEDED(Preferences::GetInt(
- "hal.processPriorityManager.gonk.notifyLowMemUnderKB",
- &lowMemNotifyThresholdKB))) {
-
- // notify_trigger is in pages.
- WriteSysFile("/sys/module/lowmemorykiller/parameters/notify_trigger",
- nsPrintfCString("%ld", lowMemNotifyThresholdKB * 1024 / page_size).get());
- }
-
- // Ensure OOM events appear in logcat
- RefPtr<OomVictimLogger> oomLogger = new OomVictimLogger();
- nsCOMPtr<nsIObserverService> os = services::GetObserverService();
- if (os) {
- os->AddObserver(oomLogger, "ipc:content-shutdown", false);
- }
-}
-
-void
-SetProcessPriority(int aPid, ProcessPriority aPriority, uint32_t aLRU)
-{
- HAL_LOG("SetProcessPriority(pid=%d, priority=%d, LRU=%u)",
- aPid, aPriority, aLRU);
-
- // If this is the first time SetProcessPriority was called, set the kernel's
- // OOM parameters according to our prefs.
- //
- // We could/should do this on startup instead of waiting for the first
- // SetProcessPriorityCall. But in practice, the master process needs to set
- // its priority early in the game, so we can reasonably rely on
- // SetProcessPriority being called early in startup.
- EnsureKernelLowMemKillerParamsSet();
-
- PriorityClass* pc = GetPriorityClass(aPriority);
-
- int oomScoreAdj = pc->OomScoreAdj();
-
- RoundOomScoreAdjUpWithLRU(oomScoreAdj, aLRU);
-
- // We try the newer interface first, and fall back to the older interface
- // on failure.
- if (!WriteSysFile(nsPrintfCString("/proc/%d/oom_score_adj", aPid).get(),
- nsPrintfCString("%d", oomScoreAdj).get()))
- {
- WriteSysFile(nsPrintfCString("/proc/%d/oom_adj", aPid).get(),
- nsPrintfCString("%d", OomAdjOfOomScoreAdj(oomScoreAdj)).get());
- }
-
- HAL_LOG("Assigning pid %d to cgroup %s", aPid, pc->CGroup().get());
- pc->AddProcess(aPid);
-}
-
-static bool
-IsValidRealTimePriority(int aValue, int aSchedulePolicy)
-{
- return (aValue >= sched_get_priority_min(aSchedulePolicy)) &&
- (aValue <= sched_get_priority_max(aSchedulePolicy));
-}
-
-static void
-SetThreadNiceValue(pid_t aTid, ThreadPriority aThreadPriority, int aValue)
-{
- MOZ_ASSERT(aThreadPriority < NUM_THREAD_PRIORITY);
- MOZ_ASSERT(aThreadPriority >= 0);
-
- HAL_LOG("Setting thread %d to priority level %s; nice level %d",
- aTid, ThreadPriorityToString(aThreadPriority), aValue);
- int rv = setpriority(PRIO_PROCESS, aTid, aValue);
-
- if (rv) {
- HAL_LOG("Failed to set thread %d to priority level %s; error %s", aTid,
- ThreadPriorityToString(aThreadPriority), strerror(errno));
- }
-}
-
-static void
-SetRealTimeThreadPriority(pid_t aTid,
- ThreadPriority aThreadPriority,
- int aValue)
-{
- int policy = SCHED_FIFO;
-
- MOZ_ASSERT(aThreadPriority < NUM_THREAD_PRIORITY);
- MOZ_ASSERT(aThreadPriority >= 0);
- MOZ_ASSERT(IsValidRealTimePriority(aValue, policy), "Invalid real time priority");
-
- // Setting real time priorities requires using sched_setscheduler
- HAL_LOG("Setting thread %d to priority level %s; Real Time priority %d, "
- "Schedule FIFO", aTid, ThreadPriorityToString(aThreadPriority),
- aValue);
- sched_param schedParam;
- schedParam.sched_priority = aValue;
- int rv = sched_setscheduler(aTid, policy, &schedParam);
-
- if (rv) {
- HAL_LOG("Failed to set thread %d to real time priority level %s; error %s",
- aTid, ThreadPriorityToString(aThreadPriority), strerror(errno));
- }
-}
-
-/*
- * Used to store the nice value adjustments and real time priorities for the
- * various thread priority levels.
- */
-struct ThreadPriorityPrefs {
- bool initialized;
- struct {
- int nice;
- int realTime;
- } priorities[NUM_THREAD_PRIORITY];
-};
-
-/*
- * Reads the preferences for the various process priority levels and sets up
- * watchers so that if they're dynamically changed the change is reflected on
- * the appropriate variables.
- */
-void
-EnsureThreadPriorityPrefs(ThreadPriorityPrefs* prefs)
-{
- if (prefs->initialized) {
- return;
- }
-
- for (int i = THREAD_PRIORITY_COMPOSITOR; i < NUM_THREAD_PRIORITY; i++) {
- ThreadPriority priority = static_cast<ThreadPriority>(i);
-
- // Read the nice values
- const char* threadPriorityStr = ThreadPriorityToString(priority);
- nsPrintfCString niceStr("hal.gonk.%s.nice", threadPriorityStr);
- Preferences::AddIntVarCache(&prefs->priorities[i].nice, niceStr.get());
-
- // Read the real-time priorities
- nsPrintfCString realTimeStr("hal.gonk.%s.rt_priority", threadPriorityStr);
- Preferences::AddIntVarCache(&prefs->priorities[i].realTime,
- realTimeStr.get());
- }
-
- prefs->initialized = true;
-}
-
-static void
-DoSetThreadPriority(pid_t aTid, hal::ThreadPriority aThreadPriority)
-{
- // See bug 999115, we can only read preferences on the main thread otherwise
- // we create a race condition in HAL
- MOZ_ASSERT(NS_IsMainThread(), "Can only set thread priorities on main thread");
- MOZ_ASSERT(aThreadPriority >= 0);
-
- static ThreadPriorityPrefs prefs = { 0 };
- EnsureThreadPriorityPrefs(&prefs);
-
- switch (aThreadPriority) {
- case THREAD_PRIORITY_COMPOSITOR:
- break;
- default:
- HAL_ERR("Unrecognized thread priority %d; Doing nothing",
- aThreadPriority);
- return;
- }
-
- int realTimePriority = prefs.priorities[aThreadPriority].realTime;
-
- if (IsValidRealTimePriority(realTimePriority, SCHED_FIFO)) {
- SetRealTimeThreadPriority(aTid, aThreadPriority, realTimePriority);
- return;
- }
-
- SetThreadNiceValue(aTid, aThreadPriority,
- prefs.priorities[aThreadPriority].nice);
-}
-
-namespace {
-
-/**
- * This class sets the priority of threads given the kernel thread's id and a
- * value taken from hal::ThreadPriority.
- *
- * This runnable must always be dispatched to the main thread otherwise it will fail.
- * We have to run this from the main thread since preferences can only be read on
- * main thread.
- */
-class SetThreadPriorityRunnable : public Runnable
-{
-public:
- SetThreadPriorityRunnable(pid_t aThreadId, hal::ThreadPriority aThreadPriority)
- : mThreadId(aThreadId)
- , mThreadPriority(aThreadPriority)
- { }
-
- NS_IMETHOD Run() override
- {
- NS_ASSERTION(NS_IsMainThread(), "Can only set thread priorities on main thread");
- hal_impl::DoSetThreadPriority(mThreadId, mThreadPriority);
- return NS_OK;
- }
-
-private:
- pid_t mThreadId;
- hal::ThreadPriority mThreadPriority;
-};
-
-} // namespace
-
-void
-SetCurrentThreadPriority(ThreadPriority aThreadPriority)
-{
- pid_t threadId = gettid();
- hal_impl::SetThreadPriority(threadId, aThreadPriority);
-}
-
-void
-SetThreadPriority(PlatformThreadId aThreadId,
- ThreadPriority aThreadPriority)
-{
- switch (aThreadPriority) {
- case THREAD_PRIORITY_COMPOSITOR: {
- nsCOMPtr<nsIRunnable> runnable =
- new SetThreadPriorityRunnable(aThreadId, aThreadPriority);
- NS_DispatchToMainThread(runnable);
- break;
- }
- default:
- HAL_LOG("Unrecognized thread priority %d; Doing nothing",
- aThreadPriority);
- return;
- }
-}
-
-void
-FactoryReset(FactoryResetReason& aReason)
-{
- nsCOMPtr<nsIRecoveryService> recoveryService =
- do_GetService("@mozilla.org/recovery-service;1");
- if (!recoveryService) {
- NS_WARNING("Could not get recovery service!");
- return;
- }
-
- if (aReason == FactoryResetReason::Wipe) {
- recoveryService->FactoryReset("wipe");
- } else if (aReason == FactoryResetReason::Root) {
- recoveryService->FactoryReset("root");
- } else {
- recoveryService->FactoryReset("normal");
- }
-}
-
-} // hal_impl
-} // mozilla