diff options
Diffstat (limited to 'hal/gonk/GonkHal.cpp')
-rw-r--r-- | hal/gonk/GonkHal.cpp | 2045 |
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 |