michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set sw=2 ts=8 et ft=cpp : */ michael@0: /* Copyright 2012 Mozilla Foundation and Mozilla contributors michael@0: * michael@0: * Licensed under the Apache License, Version 2.0 (the "License"); michael@0: * you may not use this file except in compliance with the License. michael@0: * You may obtain a copy of the License at michael@0: * michael@0: * http://www.apache.org/licenses/LICENSE-2.0 michael@0: * michael@0: * Unless required by applicable law or agreed to in writing, software michael@0: * distributed under the License is distributed on an "AS IS" BASIS, michael@0: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. michael@0: * See the License for the specific language governing permissions and michael@0: * limitations under the License. michael@0: */ michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #include "mozilla/DebugOnly.h" michael@0: michael@0: #include "android/log.h" michael@0: #include "cutils/properties.h" michael@0: #include "hardware/hardware.h" michael@0: #include "hardware/lights.h" michael@0: #include "hardware_legacy/uevent.h" michael@0: #include "hardware_legacy/vibrator.h" michael@0: #include "hardware_legacy/power.h" michael@0: #include "libdisplay/GonkDisplay.h" michael@0: michael@0: #include "base/message_loop.h" michael@0: michael@0: #include "Hal.h" michael@0: #include "HalImpl.h" michael@0: #include "mozilla/ArrayUtils.h" michael@0: #include "mozilla/dom/battery/Constants.h" michael@0: #include "mozilla/FileUtils.h" michael@0: #include "mozilla/Monitor.h" michael@0: #include "mozilla/RefPtr.h" michael@0: #include "mozilla/Services.h" michael@0: #include "mozilla/StaticPtr.h" michael@0: #include "mozilla/Preferences.h" michael@0: #include "nsAlgorithm.h" michael@0: #include "nsPrintfCString.h" michael@0: #include "nsIObserver.h" michael@0: #include "nsIObserverService.h" michael@0: #include "nsIRecoveryService.h" michael@0: #include "nsIRunnable.h" michael@0: #include "nsScreenManagerGonk.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "nsIThread.h" michael@0: #include "nsXULAppAPI.h" michael@0: #include "OrientationObserver.h" michael@0: #include "UeventPoller.h" michael@0: #include michael@0: michael@0: #define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "Gonk", args) michael@0: #define NsecPerMsec 1000000LL michael@0: #define NsecPerSec 1000000000 michael@0: michael@0: // The header linux/oom.h is not available in bionic libc. We michael@0: // redefine some of its constants here. michael@0: michael@0: #ifndef OOM_DISABLE michael@0: #define OOM_DISABLE (-17) michael@0: #endif michael@0: michael@0: #ifndef OOM_ADJUST_MIN michael@0: #define OOM_ADJUST_MIN (-16) michael@0: #endif michael@0: michael@0: #ifndef OOM_ADJUST_MAX michael@0: #define OOM_ADJUST_MAX 15 michael@0: #endif michael@0: michael@0: #ifndef OOM_SCORE_ADJ_MIN michael@0: #define OOM_SCORE_ADJ_MIN (-1000) michael@0: #endif michael@0: michael@0: #ifndef OOM_SCORE_ADJ_MAX michael@0: #define OOM_SCORE_ADJ_MAX 1000 michael@0: #endif michael@0: michael@0: #ifndef BATTERY_CHARGING_ARGB michael@0: #define BATTERY_CHARGING_ARGB 0x00FF0000 michael@0: #endif michael@0: #ifndef BATTERY_FULL_ARGB michael@0: #define BATTERY_FULL_ARGB 0x0000FF00 michael@0: #endif michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::hal; michael@0: michael@0: namespace mozilla { michael@0: namespace hal_impl { michael@0: michael@0: namespace { michael@0: michael@0: /** michael@0: * This runnable runs for the lifetime of the program, once started. It's michael@0: * responsible for "playing" vibration patterns. michael@0: */ michael@0: class VibratorRunnable michael@0: : public nsIRunnable michael@0: , public nsIObserver michael@0: { michael@0: public: michael@0: VibratorRunnable() michael@0: : mMonitor("VibratorRunnable") michael@0: , mIndex(0) michael@0: { michael@0: nsCOMPtr os = services::GetObserverService(); michael@0: if (!os) { michael@0: NS_WARNING("Could not get observer service!"); michael@0: return; michael@0: } michael@0: michael@0: os->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false); michael@0: } michael@0: michael@0: NS_DECL_THREADSAFE_ISUPPORTS michael@0: NS_DECL_NSIRUNNABLE michael@0: NS_DECL_NSIOBSERVER michael@0: michael@0: // Run on the main thread, not the vibrator thread. michael@0: void Vibrate(const nsTArray &pattern); michael@0: void CancelVibrate(); michael@0: michael@0: static bool ShuttingDown() { return sShuttingDown; } michael@0: michael@0: private: michael@0: Monitor mMonitor; michael@0: michael@0: // The currently-playing pattern. michael@0: nsTArray mPattern; michael@0: michael@0: // The index we're at in the currently-playing pattern. If mIndex >= michael@0: // mPattern.Length(), then we're not currently playing anything. michael@0: uint32_t mIndex; michael@0: michael@0: // Set to true in our shutdown observer. When this is true, we kill the michael@0: // vibrator thread. michael@0: static bool sShuttingDown; michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS(VibratorRunnable, nsIRunnable, nsIObserver); michael@0: michael@0: bool VibratorRunnable::sShuttingDown = false; michael@0: michael@0: static StaticRefPtr sVibratorRunnable; michael@0: michael@0: NS_IMETHODIMP michael@0: VibratorRunnable::Run() michael@0: { michael@0: MonitorAutoLock lock(mMonitor); michael@0: michael@0: // We currently assume that mMonitor.Wait(X) waits for X milliseconds. But in michael@0: // reality, the kernel might not switch to this thread for some time after the michael@0: // wait expires. So there's potential for some inaccuracy here. michael@0: // michael@0: // This doesn't worry me too much. Note that we don't even start vibrating michael@0: // immediately when VibratorRunnable::Vibrate is called -- we go through a michael@0: // condvar onto another thread. Better just to be chill about small errors in michael@0: // the timing here. michael@0: michael@0: while (!sShuttingDown) { michael@0: if (mIndex < mPattern.Length()) { michael@0: uint32_t duration = mPattern[mIndex]; michael@0: if (mIndex % 2 == 0) { michael@0: vibrator_on(duration); michael@0: } michael@0: mIndex++; michael@0: mMonitor.Wait(PR_MillisecondsToInterval(duration)); michael@0: } michael@0: else { michael@0: mMonitor.Wait(); michael@0: } michael@0: } michael@0: sVibratorRunnable = nullptr; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: VibratorRunnable::Observe(nsISupports *subject, const char *topic, michael@0: const char16_t *data) michael@0: { michael@0: MOZ_ASSERT(strcmp(topic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0); michael@0: MonitorAutoLock lock(mMonitor); michael@0: sShuttingDown = true; michael@0: mMonitor.Notify(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: VibratorRunnable::Vibrate(const nsTArray &pattern) michael@0: { michael@0: MonitorAutoLock lock(mMonitor); michael@0: mPattern = pattern; michael@0: mIndex = 0; michael@0: mMonitor.Notify(); michael@0: } michael@0: michael@0: void michael@0: VibratorRunnable::CancelVibrate() michael@0: { michael@0: MonitorAutoLock lock(mMonitor); michael@0: mPattern.Clear(); michael@0: mPattern.AppendElement(0); michael@0: mIndex = 0; michael@0: mMonitor.Notify(); michael@0: } michael@0: michael@0: void michael@0: EnsureVibratorThreadInitialized() michael@0: { michael@0: if (sVibratorRunnable) { michael@0: return; michael@0: } michael@0: michael@0: sVibratorRunnable = new VibratorRunnable(); michael@0: nsCOMPtr thread; michael@0: NS_NewThread(getter_AddRefs(thread), sVibratorRunnable); michael@0: } michael@0: michael@0: } // anonymous namespace michael@0: michael@0: void michael@0: Vibrate(const nsTArray &pattern, const hal::WindowIdentifier &) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: if (VibratorRunnable::ShuttingDown()) { michael@0: return; michael@0: } michael@0: EnsureVibratorThreadInitialized(); michael@0: sVibratorRunnable->Vibrate(pattern); michael@0: } michael@0: michael@0: void michael@0: CancelVibrate(const hal::WindowIdentifier &) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: if (VibratorRunnable::ShuttingDown()) { michael@0: return; michael@0: } michael@0: EnsureVibratorThreadInitialized(); michael@0: sVibratorRunnable->CancelVibrate(); michael@0: } michael@0: michael@0: namespace { michael@0: michael@0: class BatteryUpdater : public nsRunnable { michael@0: public: michael@0: NS_IMETHOD Run() michael@0: { michael@0: hal::BatteryInformation info; michael@0: hal_impl::GetCurrentBatteryInformation(&info); michael@0: michael@0: // Control the battery indicator (led light) here using BatteryInformation michael@0: // we just retrieved. michael@0: uint32_t color = 0; // Format: 0x00rrggbb. michael@0: if (info.charging() && (info.level() == 1)) { michael@0: // Charging and battery full. michael@0: color = BATTERY_FULL_ARGB; michael@0: } else if (info.charging() && (info.level() < 1)) { michael@0: // Charging but not full. michael@0: color = BATTERY_CHARGING_ARGB; michael@0: } // else turn off battery indicator. michael@0: michael@0: hal::LightConfiguration aConfig(hal::eHalLightID_Battery, michael@0: hal::eHalLightMode_User, michael@0: hal::eHalLightFlash_None, michael@0: 0, michael@0: 0, michael@0: color); michael@0: hal_impl::SetLight(hal::eHalLightID_Battery, aConfig); michael@0: michael@0: hal::NotifyBatteryChange(info); michael@0: return NS_OK; michael@0: } michael@0: }; michael@0: michael@0: } // anonymous namespace michael@0: michael@0: class BatteryObserver : public IUeventObserver michael@0: { michael@0: public: michael@0: NS_INLINE_DECL_REFCOUNTING(BatteryObserver) michael@0: michael@0: BatteryObserver() michael@0: :mUpdater(new BatteryUpdater()) michael@0: { michael@0: } michael@0: michael@0: virtual void Notify(const NetlinkEvent &aEvent) michael@0: { michael@0: // this will run on IO thread michael@0: NetlinkEvent *event = const_cast(&aEvent); michael@0: const char *subsystem = event->getSubsystem(); michael@0: // e.g. DEVPATH=/devices/platform/sec-battery/power_supply/battery michael@0: const char *devpath = event->findParam("DEVPATH"); michael@0: if (strcmp(subsystem, "power_supply") == 0 && michael@0: strstr(devpath, "battery")) { michael@0: // aEvent will be valid only in this method. michael@0: NS_DispatchToMainThread(mUpdater); michael@0: } michael@0: } michael@0: michael@0: private: michael@0: nsRefPtr mUpdater; michael@0: }; michael@0: michael@0: // sBatteryObserver is owned by the IO thread. Only the IO thread may michael@0: // create or destroy it. michael@0: static StaticRefPtr sBatteryObserver; michael@0: michael@0: static void michael@0: RegisterBatteryObserverIOThread() michael@0: { michael@0: MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); michael@0: MOZ_ASSERT(!sBatteryObserver); michael@0: michael@0: sBatteryObserver = new BatteryObserver(); michael@0: RegisterUeventListener(sBatteryObserver); michael@0: } michael@0: michael@0: void michael@0: EnableBatteryNotifications() michael@0: { michael@0: XRE_GetIOMessageLoop()->PostTask( michael@0: FROM_HERE, michael@0: NewRunnableFunction(RegisterBatteryObserverIOThread)); michael@0: } michael@0: michael@0: static void michael@0: UnregisterBatteryObserverIOThread() michael@0: { michael@0: MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); michael@0: MOZ_ASSERT(sBatteryObserver); michael@0: michael@0: UnregisterUeventListener(sBatteryObserver); michael@0: sBatteryObserver = nullptr; michael@0: } michael@0: michael@0: void michael@0: DisableBatteryNotifications() michael@0: { michael@0: XRE_GetIOMessageLoop()->PostTask( michael@0: FROM_HERE, michael@0: NewRunnableFunction(UnregisterBatteryObserverIOThread)); michael@0: } michael@0: michael@0: static bool michael@0: GetCurrentBatteryCharge(int* aCharge) michael@0: { michael@0: bool success = ReadSysFile("/sys/class/power_supply/battery/capacity", michael@0: aCharge); michael@0: if (!success) { michael@0: return false; michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: if ((*aCharge < 0) || (*aCharge > 100)) { michael@0: HAL_LOG(("charge level contains unknown value: %d", *aCharge)); michael@0: } michael@0: #endif michael@0: michael@0: return (*aCharge >= 0) && (*aCharge <= 100); michael@0: } michael@0: michael@0: static bool michael@0: GetCurrentBatteryCharging(int* aCharging) michael@0: { michael@0: static const int BATTERY_NOT_CHARGING = 0; michael@0: static const int BATTERY_CHARGING_USB = 1; michael@0: static const int BATTERY_CHARGING_AC = 2; michael@0: michael@0: // Generic device support michael@0: michael@0: int chargingSrc; michael@0: bool success = michael@0: ReadSysFile("/sys/class/power_supply/battery/charging_source", &chargingSrc); michael@0: michael@0: if (success) { michael@0: #ifdef DEBUG michael@0: if (chargingSrc != BATTERY_NOT_CHARGING && michael@0: chargingSrc != BATTERY_CHARGING_USB && michael@0: chargingSrc != BATTERY_CHARGING_AC) { michael@0: HAL_LOG(("charging_source contained unknown value: %d", chargingSrc)); michael@0: } michael@0: #endif michael@0: michael@0: *aCharging = (chargingSrc == BATTERY_CHARGING_USB || michael@0: chargingSrc == BATTERY_CHARGING_AC); michael@0: return true; michael@0: } michael@0: michael@0: // Otoro device support michael@0: michael@0: char chargingSrcString[16]; michael@0: michael@0: success = ReadSysFile("/sys/class/power_supply/battery/status", michael@0: chargingSrcString, sizeof(chargingSrcString)); michael@0: if (success) { michael@0: *aCharging = strcmp(chargingSrcString, "Charging") == 0 || michael@0: strcmp(chargingSrcString, "Full") == 0; michael@0: return true; michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: void michael@0: GetCurrentBatteryInformation(hal::BatteryInformation* aBatteryInfo) michael@0: { michael@0: int charge; michael@0: michael@0: if (GetCurrentBatteryCharge(&charge)) { michael@0: aBatteryInfo->level() = (double)charge / 100.0; michael@0: } else { michael@0: aBatteryInfo->level() = dom::battery::kDefaultLevel; michael@0: } michael@0: michael@0: int charging; michael@0: michael@0: if (GetCurrentBatteryCharging(&charging)) { michael@0: aBatteryInfo->charging() = charging; michael@0: } else { michael@0: aBatteryInfo->charging() = true; michael@0: } michael@0: michael@0: if (!aBatteryInfo->charging() || (aBatteryInfo->level() < 1.0)) { michael@0: aBatteryInfo->remainingTime() = dom::battery::kUnknownRemainingTime; michael@0: } else { michael@0: aBatteryInfo->remainingTime() = dom::battery::kDefaultRemainingTime; michael@0: } michael@0: } michael@0: michael@0: namespace { michael@0: michael@0: /** michael@0: * RAII class to help us remember to close file descriptors. michael@0: */ michael@0: const char *wakeLockFilename = "/sys/power/wake_lock"; michael@0: const char *wakeUnlockFilename = "/sys/power/wake_unlock"; michael@0: michael@0: template michael@0: bool ReadFromFile(const char *filename, char (&buf)[n]) michael@0: { michael@0: int fd = open(filename, O_RDONLY); michael@0: ScopedClose autoClose(fd); michael@0: if (fd < 0) { michael@0: HAL_LOG(("Unable to open file %s.", filename)); michael@0: return false; michael@0: } michael@0: michael@0: ssize_t numRead = read(fd, buf, n); michael@0: if (numRead < 0) { michael@0: HAL_LOG(("Error reading from file %s.", filename)); michael@0: return false; michael@0: } michael@0: michael@0: buf[std::min(numRead, n - 1)] = '\0'; michael@0: return true; michael@0: } michael@0: michael@0: bool WriteToFile(const char *filename, const char *toWrite) michael@0: { michael@0: int fd = open(filename, O_WRONLY); michael@0: ScopedClose autoClose(fd); michael@0: if (fd < 0) { michael@0: HAL_LOG(("Unable to open file %s.", filename)); michael@0: return false; michael@0: } michael@0: michael@0: if (write(fd, toWrite, strlen(toWrite)) < 0) { michael@0: HAL_LOG(("Unable to write to file %s.", filename)); michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: // We can write to screenEnabledFilename to enable/disable the screen, but when michael@0: // we read, we always get "mem"! So we have to keep track ourselves whether michael@0: // the screen is on or not. michael@0: bool sScreenEnabled = true; michael@0: michael@0: // We can read wakeLockFilename to find out whether the cpu wake lock michael@0: // is already acquired, but reading and parsing it is a lot more work michael@0: // than tracking it ourselves, and it won't be accurate anyway (kernel michael@0: // internal wake locks aren't counted here.) michael@0: bool sCpuSleepAllowed = true; michael@0: michael@0: // Some CPU wake locks may be acquired internally in HAL. We use a counter to michael@0: // keep track of these needs. Note we have to hold |sInternalLockCpuMonitor| michael@0: // when reading or writing this variable to ensure thread-safe. michael@0: int32_t sInternalLockCpuCount = 0; michael@0: michael@0: } // anonymous namespace michael@0: michael@0: bool michael@0: GetScreenEnabled() michael@0: { michael@0: return sScreenEnabled; michael@0: } michael@0: michael@0: void michael@0: SetScreenEnabled(bool enabled) michael@0: { michael@0: GetGonkDisplay()->SetEnabled(enabled); michael@0: sScreenEnabled = enabled; michael@0: } michael@0: michael@0: double michael@0: GetScreenBrightness() michael@0: { michael@0: hal::LightConfiguration aConfig; michael@0: hal::LightType light = hal::eHalLightID_Backlight; michael@0: michael@0: hal::GetLight(light, &aConfig); michael@0: // backlight is brightness only, so using one of the RGB elements as value. michael@0: int brightness = aConfig.color() & 0xFF; michael@0: return brightness / 255.0; michael@0: } michael@0: michael@0: void michael@0: SetScreenBrightness(double brightness) michael@0: { michael@0: // Don't use De Morgan's law to push the ! into this expression; we want to michael@0: // catch NaN too. michael@0: if (!(0 <= brightness && brightness <= 1)) { michael@0: HAL_LOG(("SetScreenBrightness: Dropping illegal brightness %f.", michael@0: brightness)); michael@0: return; michael@0: } michael@0: michael@0: // Convert the value in [0, 1] to an int between 0 and 255 and convert to a color michael@0: // note that the high byte is FF, corresponding to the alpha channel. michael@0: int val = static_cast(round(brightness * 255)); michael@0: uint32_t color = (0xff<<24) + (val<<16) + (val<<8) + val; michael@0: michael@0: hal::LightConfiguration aConfig; michael@0: aConfig.mode() = hal::eHalLightMode_User; michael@0: aConfig.flash() = hal::eHalLightFlash_None; michael@0: aConfig.flashOnMS() = aConfig.flashOffMS() = 0; michael@0: aConfig.color() = color; michael@0: hal::SetLight(hal::eHalLightID_Backlight, aConfig); michael@0: hal::SetLight(hal::eHalLightID_Buttons, aConfig); michael@0: } michael@0: michael@0: static Monitor* sInternalLockCpuMonitor = nullptr; michael@0: michael@0: static void michael@0: UpdateCpuSleepState() michael@0: { michael@0: sInternalLockCpuMonitor->AssertCurrentThreadOwns(); michael@0: bool allowed = sCpuSleepAllowed && !sInternalLockCpuCount; michael@0: WriteToFile(allowed ? wakeUnlockFilename : wakeLockFilename, "gecko"); michael@0: } michael@0: michael@0: static void michael@0: InternalLockCpu() { michael@0: MonitorAutoLock monitor(*sInternalLockCpuMonitor); michael@0: ++sInternalLockCpuCount; michael@0: UpdateCpuSleepState(); michael@0: } michael@0: michael@0: static void michael@0: InternalUnlockCpu() { michael@0: MonitorAutoLock monitor(*sInternalLockCpuMonitor); michael@0: --sInternalLockCpuCount; michael@0: UpdateCpuSleepState(); michael@0: } michael@0: michael@0: bool michael@0: GetCpuSleepAllowed() michael@0: { michael@0: return sCpuSleepAllowed; michael@0: } michael@0: michael@0: void michael@0: SetCpuSleepAllowed(bool aAllowed) michael@0: { michael@0: MonitorAutoLock monitor(*sInternalLockCpuMonitor); michael@0: sCpuSleepAllowed = aAllowed; michael@0: UpdateCpuSleepState(); michael@0: } michael@0: michael@0: static light_device_t* sLights[hal::eHalLightID_Count]; // will be initialized to nullptr michael@0: michael@0: light_device_t* GetDevice(hw_module_t* module, char const* name) michael@0: { michael@0: int err; michael@0: hw_device_t* device; michael@0: err = module->methods->open(module, name, &device); michael@0: if (err == 0) { michael@0: return (light_device_t*)device; michael@0: } else { michael@0: return nullptr; michael@0: } michael@0: } michael@0: michael@0: void michael@0: InitLights() michael@0: { michael@0: // assume that if backlight is nullptr, nothing has been set yet michael@0: // if this is not true, the initialization will occur everytime a light is read or set! michael@0: if (!sLights[hal::eHalLightID_Backlight]) { michael@0: int err; michael@0: hw_module_t* module; michael@0: michael@0: err = hw_get_module(LIGHTS_HARDWARE_MODULE_ID, (hw_module_t const**)&module); michael@0: if (err == 0) { michael@0: sLights[hal::eHalLightID_Backlight] michael@0: = GetDevice(module, LIGHT_ID_BACKLIGHT); michael@0: sLights[hal::eHalLightID_Keyboard] michael@0: = GetDevice(module, LIGHT_ID_KEYBOARD); michael@0: sLights[hal::eHalLightID_Buttons] michael@0: = GetDevice(module, LIGHT_ID_BUTTONS); michael@0: sLights[hal::eHalLightID_Battery] michael@0: = GetDevice(module, LIGHT_ID_BATTERY); michael@0: sLights[hal::eHalLightID_Notifications] michael@0: = GetDevice(module, LIGHT_ID_NOTIFICATIONS); michael@0: sLights[hal::eHalLightID_Attention] michael@0: = GetDevice(module, LIGHT_ID_ATTENTION); michael@0: sLights[hal::eHalLightID_Bluetooth] michael@0: = GetDevice(module, LIGHT_ID_BLUETOOTH); michael@0: sLights[hal::eHalLightID_Wifi] michael@0: = GetDevice(module, LIGHT_ID_WIFI); michael@0: } michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * The state last set for the lights until liblights supports michael@0: * getting the light state. michael@0: */ michael@0: static light_state_t sStoredLightState[hal::eHalLightID_Count]; michael@0: michael@0: bool michael@0: SetLight(hal::LightType light, const hal::LightConfiguration& aConfig) michael@0: { michael@0: light_state_t state; michael@0: michael@0: InitLights(); michael@0: michael@0: if (light < 0 || light >= hal::eHalLightID_Count || michael@0: sLights[light] == nullptr) { michael@0: return false; michael@0: } michael@0: michael@0: memset(&state, 0, sizeof(light_state_t)); michael@0: state.color = aConfig.color(); michael@0: state.flashMode = aConfig.flash(); michael@0: state.flashOnMS = aConfig.flashOnMS(); michael@0: state.flashOffMS = aConfig.flashOffMS(); michael@0: state.brightnessMode = aConfig.mode(); michael@0: michael@0: sLights[light]->set_light(sLights[light], &state); michael@0: sStoredLightState[light] = state; michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: GetLight(hal::LightType light, hal::LightConfiguration* aConfig) michael@0: { michael@0: light_state_t state; michael@0: michael@0: #ifdef HAVEGETLIGHT michael@0: InitLights(); michael@0: #endif michael@0: michael@0: if (light < 0 || light >= hal::eHalLightID_Count || michael@0: sLights[light] == nullptr) { michael@0: return false; michael@0: } michael@0: michael@0: memset(&state, 0, sizeof(light_state_t)); michael@0: michael@0: #ifdef HAVEGETLIGHT michael@0: sLights[light]->get_light(sLights[light], &state); michael@0: #else michael@0: state = sStoredLightState[light]; michael@0: #endif michael@0: michael@0: aConfig->light() = light; michael@0: aConfig->color() = state.color; michael@0: aConfig->flash() = hal::FlashMode(state.flashMode); michael@0: aConfig->flashOnMS() = state.flashOnMS; michael@0: aConfig->flashOffMS() = state.flashOffMS; michael@0: aConfig->mode() = hal::LightMode(state.brightnessMode); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: AdjustSystemClock(int64_t aDeltaMilliseconds) michael@0: { michael@0: int fd; michael@0: struct timespec now; michael@0: michael@0: if (aDeltaMilliseconds == 0) { michael@0: return; michael@0: } michael@0: michael@0: // Preventing context switch before setting system clock michael@0: sched_yield(); michael@0: clock_gettime(CLOCK_REALTIME, &now); michael@0: now.tv_sec += (time_t)(aDeltaMilliseconds / 1000LL); michael@0: now.tv_nsec += (long)((aDeltaMilliseconds % 1000LL) * NsecPerMsec); michael@0: if (now.tv_nsec >= NsecPerSec) { michael@0: now.tv_sec += 1; michael@0: now.tv_nsec -= NsecPerSec; michael@0: } michael@0: michael@0: if (now.tv_nsec < 0) { michael@0: now.tv_nsec += NsecPerSec; michael@0: now.tv_sec -= 1; michael@0: } michael@0: michael@0: do { michael@0: fd = open("/dev/alarm", O_RDWR); michael@0: } while (fd == -1 && errno == EINTR); michael@0: ScopedClose autoClose(fd); michael@0: if (fd < 0) { michael@0: HAL_LOG(("Failed to open /dev/alarm: %s", strerror(errno))); michael@0: return; michael@0: } michael@0: michael@0: if (ioctl(fd, ANDROID_ALARM_SET_RTC, &now) < 0) { michael@0: HAL_LOG(("ANDROID_ALARM_SET_RTC failed: %s", strerror(errno))); michael@0: } michael@0: michael@0: hal::NotifySystemClockChange(aDeltaMilliseconds); michael@0: } michael@0: michael@0: int32_t michael@0: GetTimezoneOffset() michael@0: { michael@0: PRExplodedTime prTime; michael@0: PR_ExplodeTime(PR_Now(), PR_LocalTimeParameters, &prTime); michael@0: michael@0: // Daylight saving time (DST) will be taken into account. michael@0: int32_t offset = prTime.tm_params.tp_gmt_offset; michael@0: offset += prTime.tm_params.tp_dst_offset; michael@0: michael@0: // Returns the timezone offset relative to UTC in minutes. michael@0: return -(offset / 60); michael@0: } michael@0: michael@0: static int32_t sKernelTimezoneOffset = 0; michael@0: michael@0: static void michael@0: UpdateKernelTimezone(int32_t timezoneOffset) michael@0: { michael@0: if (sKernelTimezoneOffset == timezoneOffset) { michael@0: return; michael@0: } michael@0: michael@0: // Tell the kernel about the new time zone as well, so that FAT filesystems michael@0: // will get local timestamps rather than UTC timestamps. michael@0: // michael@0: // We assume that /init.rc has a sysclktz entry so that settimeofday has michael@0: // already been called once before we call it (there is a side-effect in michael@0: // the kernel the very first time settimeofday is called where it does some michael@0: // special processing if you only set the timezone). michael@0: struct timezone tz; michael@0: memset(&tz, 0, sizeof(tz)); michael@0: tz.tz_minuteswest = timezoneOffset; michael@0: settimeofday(nullptr, &tz); michael@0: sKernelTimezoneOffset = timezoneOffset; michael@0: } michael@0: michael@0: void michael@0: SetTimezone(const nsCString& aTimezoneSpec) michael@0: { michael@0: if (aTimezoneSpec.Equals(GetTimezone())) { michael@0: // Even though the timezone hasn't changed, we still need to tell the michael@0: // kernel what the current timezone is. The timezone is persisted in michael@0: // a property and doesn't change across reboots, but the kernel still michael@0: // needs to be updated on every boot. michael@0: UpdateKernelTimezone(GetTimezoneOffset()); michael@0: return; michael@0: } michael@0: michael@0: int32_t oldTimezoneOffsetMinutes = GetTimezoneOffset(); michael@0: property_set("persist.sys.timezone", aTimezoneSpec.get()); michael@0: // This function is automatically called by the other time conversion michael@0: // functions that depend on the timezone. To be safe, we call it manually. michael@0: tzset(); michael@0: int32_t newTimezoneOffsetMinutes = GetTimezoneOffset(); michael@0: UpdateKernelTimezone(newTimezoneOffsetMinutes); michael@0: hal::NotifySystemTimezoneChange( michael@0: hal::SystemTimezoneChangeInformation( michael@0: oldTimezoneOffsetMinutes, newTimezoneOffsetMinutes)); michael@0: } michael@0: michael@0: nsCString michael@0: GetTimezone() michael@0: { michael@0: char timezone[32]; michael@0: property_get("persist.sys.timezone", timezone, ""); michael@0: return nsCString(timezone); michael@0: } michael@0: michael@0: void michael@0: EnableSystemClockChangeNotifications() michael@0: { michael@0: } michael@0: michael@0: void michael@0: DisableSystemClockChangeNotifications() michael@0: { michael@0: } michael@0: michael@0: void michael@0: EnableSystemTimezoneChangeNotifications() michael@0: { michael@0: } michael@0: michael@0: void michael@0: DisableSystemTimezoneChangeNotifications() michael@0: { michael@0: } michael@0: michael@0: // Nothing to do here. Gonk widgetry always listens for screen michael@0: // orientation changes. michael@0: void michael@0: EnableScreenConfigurationNotifications() michael@0: { michael@0: } michael@0: michael@0: void michael@0: DisableScreenConfigurationNotifications() michael@0: { michael@0: } michael@0: michael@0: void michael@0: GetCurrentScreenConfiguration(hal::ScreenConfiguration* aScreenConfiguration) michael@0: { michael@0: *aScreenConfiguration = nsScreenGonk::GetConfiguration(); michael@0: } michael@0: michael@0: bool michael@0: LockScreenOrientation(const dom::ScreenOrientation& aOrientation) michael@0: { michael@0: return OrientationObserver::GetInstance()->LockScreenOrientation(aOrientation); michael@0: } michael@0: michael@0: void michael@0: UnlockScreenOrientation() michael@0: { michael@0: OrientationObserver::GetInstance()->UnlockScreenOrientation(); michael@0: } michael@0: michael@0: // This thread will wait for the alarm firing by a blocking IO. michael@0: static pthread_t sAlarmFireWatcherThread; michael@0: michael@0: // If |sAlarmData| is non-null, it's owned by the alarm-watcher thread. michael@0: struct AlarmData { michael@0: public: michael@0: AlarmData(int aFd) : mFd(aFd), michael@0: mGeneration(sNextGeneration++), michael@0: mShuttingDown(false) {} michael@0: ScopedClose mFd; michael@0: int mGeneration; michael@0: bool mShuttingDown; michael@0: michael@0: static int sNextGeneration; michael@0: michael@0: }; michael@0: michael@0: int AlarmData::sNextGeneration = 0; michael@0: michael@0: AlarmData* sAlarmData = nullptr; michael@0: michael@0: class AlarmFiredEvent : public nsRunnable { michael@0: public: michael@0: AlarmFiredEvent(int aGeneration) : mGeneration(aGeneration) {} michael@0: michael@0: NS_IMETHOD Run() { michael@0: // Guard against spurious notifications caused by an alarm firing michael@0: // concurrently with it being disabled. michael@0: if (sAlarmData && !sAlarmData->mShuttingDown && michael@0: mGeneration == sAlarmData->mGeneration) { michael@0: hal::NotifyAlarmFired(); michael@0: } michael@0: // The fired alarm event has been delivered to the observer (if needed); michael@0: // we can now release a CPU wake lock. michael@0: InternalUnlockCpu(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: private: michael@0: int mGeneration; michael@0: }; michael@0: michael@0: // Runs on alarm-watcher thread. michael@0: static void michael@0: DestroyAlarmData(void* aData) michael@0: { michael@0: AlarmData* alarmData = static_cast(aData); michael@0: delete alarmData; michael@0: } michael@0: michael@0: // Runs on alarm-watcher thread. michael@0: void ShutDownAlarm(int aSigno) michael@0: { michael@0: if (aSigno == SIGUSR1 && sAlarmData) { michael@0: sAlarmData->mShuttingDown = true; michael@0: } michael@0: return; michael@0: } michael@0: michael@0: static void* michael@0: WaitForAlarm(void* aData) michael@0: { michael@0: pthread_cleanup_push(DestroyAlarmData, aData); michael@0: michael@0: AlarmData* alarmData = static_cast(aData); michael@0: michael@0: while (!alarmData->mShuttingDown) { michael@0: int alarmTypeFlags = 0; michael@0: michael@0: // ALARM_WAIT apparently will block even if an alarm hasn't been michael@0: // programmed, although this behavior doesn't seem to be michael@0: // documented. We rely on that here to avoid spinning the CPU michael@0: // while awaiting an alarm to be programmed. michael@0: do { michael@0: alarmTypeFlags = ioctl(alarmData->mFd, ANDROID_ALARM_WAIT); michael@0: } while (alarmTypeFlags < 0 && errno == EINTR && michael@0: !alarmData->mShuttingDown); michael@0: michael@0: if (!alarmData->mShuttingDown && alarmTypeFlags >= 0 && michael@0: (alarmTypeFlags & ANDROID_ALARM_RTC_WAKEUP_MASK)) { michael@0: // To make sure the observer can get the alarm firing notification michael@0: // *on time* (the system won't sleep during the process in any way), michael@0: // we need to acquire a CPU wake lock before firing the alarm event. michael@0: InternalLockCpu(); michael@0: nsRefPtr event = michael@0: new AlarmFiredEvent(alarmData->mGeneration); michael@0: NS_DispatchToMainThread(event); michael@0: } michael@0: } michael@0: michael@0: pthread_cleanup_pop(1); michael@0: return nullptr; michael@0: } michael@0: michael@0: bool michael@0: EnableAlarm() michael@0: { michael@0: MOZ_ASSERT(!sAlarmData); michael@0: michael@0: int alarmFd = open("/dev/alarm", O_RDWR); michael@0: if (alarmFd < 0) { michael@0: HAL_LOG(("Failed to open alarm device: %s.", strerror(errno))); michael@0: return false; michael@0: } michael@0: michael@0: nsAutoPtr alarmData(new AlarmData(alarmFd)); michael@0: michael@0: struct sigaction actions; michael@0: memset(&actions, 0, sizeof(actions)); michael@0: sigemptyset(&actions.sa_mask); michael@0: actions.sa_flags = 0; michael@0: actions.sa_handler = ShutDownAlarm; michael@0: if (sigaction(SIGUSR1, &actions, nullptr)) { michael@0: HAL_LOG(("Failed to set SIGUSR1 signal for alarm-watcher thread.")); michael@0: return false; michael@0: } michael@0: michael@0: pthread_attr_t attr; michael@0: pthread_attr_init(&attr); michael@0: pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); michael@0: michael@0: // Initialize the monitor for internally locking CPU to ensure thread-safe michael@0: // before running the alarm-watcher thread. michael@0: sInternalLockCpuMonitor = new Monitor("sInternalLockCpuMonitor"); michael@0: int status = pthread_create(&sAlarmFireWatcherThread, &attr, WaitForAlarm, michael@0: alarmData.get()); michael@0: if (status) { michael@0: alarmData = nullptr; michael@0: delete sInternalLockCpuMonitor; michael@0: HAL_LOG(("Failed to create alarm-watcher thread. Status: %d.", status)); michael@0: return false; michael@0: } michael@0: michael@0: pthread_attr_destroy(&attr); michael@0: michael@0: // The thread owns this now. We only hold a pointer. michael@0: sAlarmData = alarmData.forget(); michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: DisableAlarm() michael@0: { michael@0: MOZ_ASSERT(sAlarmData); michael@0: michael@0: // NB: this must happen-before the thread cancellation. michael@0: sAlarmData = nullptr; michael@0: michael@0: // The cancel will interrupt the thread and destroy it, freeing the michael@0: // data pointed at by sAlarmData. michael@0: DebugOnly err = pthread_kill(sAlarmFireWatcherThread, SIGUSR1); michael@0: MOZ_ASSERT(!err); michael@0: michael@0: delete sInternalLockCpuMonitor; michael@0: } michael@0: michael@0: bool michael@0: SetAlarm(int32_t aSeconds, int32_t aNanoseconds) michael@0: { michael@0: if (!sAlarmData) { michael@0: HAL_LOG(("We should have enabled the alarm.")); michael@0: return false; michael@0: } michael@0: michael@0: struct timespec ts; michael@0: ts.tv_sec = aSeconds; michael@0: ts.tv_nsec = aNanoseconds; michael@0: michael@0: // Currently we only support RTC wakeup alarm type. michael@0: const int result = ioctl(sAlarmData->mFd, michael@0: ANDROID_ALARM_SET(ANDROID_ALARM_RTC_WAKEUP), &ts); michael@0: michael@0: if (result < 0) { michael@0: HAL_LOG(("Unable to set alarm: %s.", strerror(errno))); michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: static int michael@0: OomAdjOfOomScoreAdj(int aOomScoreAdj) michael@0: { michael@0: // Convert OOM adjustment from the domain of /proc//oom_score_adj michael@0: // to the domain of /proc//oom_adj. michael@0: michael@0: int adj; michael@0: michael@0: if (aOomScoreAdj < 0) { michael@0: adj = (OOM_DISABLE * aOomScoreAdj) / OOM_SCORE_ADJ_MIN; michael@0: } else { michael@0: adj = (OOM_ADJUST_MAX * aOomScoreAdj) / OOM_SCORE_ADJ_MAX; michael@0: } michael@0: michael@0: return adj; michael@0: } michael@0: michael@0: static void michael@0: RoundOomScoreAdjUpWithBackroundLRU(int& aOomScoreAdj, uint32_t aBackgroundLRU) michael@0: { michael@0: // We want to add minimum value to round OomScoreAdj up according to michael@0: // the steps by aBackgroundLRU. michael@0: aOomScoreAdj += michael@0: ceil(((float)OOM_SCORE_ADJ_MAX / OOM_ADJUST_MAX) * aBackgroundLRU); michael@0: } michael@0: michael@0: #define OOM_LOG(level, args...) __android_log_print(level, "OomLogger", ##args) michael@0: class OomVictimLogger MOZ_FINAL michael@0: : public nsIObserver michael@0: { michael@0: public: michael@0: OomVictimLogger() michael@0: : mLastLineChecked(-1.0), michael@0: mRegexes(nullptr) michael@0: { michael@0: // Enable timestamps in kernel's printk michael@0: WriteToFile("/sys/module/printk/parameters/time", "Y"); michael@0: } michael@0: michael@0: NS_DECL_ISUPPORTS michael@0: NS_DECL_NSIOBSERVER michael@0: private: michael@0: double mLastLineChecked; michael@0: ScopedFreePtr mRegexes; michael@0: }; michael@0: NS_IMPL_ISUPPORTS(OomVictimLogger, nsIObserver); michael@0: michael@0: NS_IMETHODIMP michael@0: OomVictimLogger::Observe( michael@0: nsISupports* aSubject, michael@0: const char* aTopic, michael@0: const char16_t* aData) michael@0: { michael@0: nsDependentCString event_type(aTopic); michael@0: if (!event_type.EqualsLiteral("ipc:content-shutdown")) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: // OOM message finding regexes michael@0: const char* const regexes_raw[] = { michael@0: ".*select.*to kill.*", michael@0: ".*send sigkill to.*", michael@0: ".*lowmem_shrink.*, return", michael@0: ".*lowmem_shrink.*, ofree.*"}; michael@0: const size_t regex_count = ArrayLength(regexes_raw); michael@0: michael@0: // Compile our regex just in time michael@0: if (!mRegexes) { michael@0: mRegexes = static_cast(malloc(sizeof(regex_t) * regex_count)); michael@0: for (size_t i = 0; i < regex_count; i++) { michael@0: int compilation_err = regcomp(&(mRegexes[i]), regexes_raw[i], REG_NOSUB); michael@0: if (compilation_err) { michael@0: OOM_LOG(ANDROID_LOG_ERROR, "Cannot compile regex \"%s\"\n", regexes_raw[i]); michael@0: return NS_OK; michael@0: } michael@0: } michael@0: } michael@0: michael@0: #ifndef KLOG_SIZE_BUFFER michael@0: // Upstream bionic in commit michael@0: // e249b059637b49a285ed9f58a2a18bfd054e5d95 michael@0: // deprecated the old klog defs. michael@0: // Our current bionic does not hit this michael@0: // change yet so handle the future change. michael@0: #define KLOG_SIZE_BUFFER KLOG_WRITE michael@0: #else michael@0: // Once the change hits our bionic this ifndef michael@0: // can be removed. michael@0: #warning "Please remove KLOG_UNREAD_SIZE compatability def" michael@0: #endif michael@0: // Retreive kernel log michael@0: int msg_buf_size = klogctl(KLOG_SIZE_BUFFER, NULL, 0); michael@0: ScopedFreePtr msg_buf(static_cast(malloc(msg_buf_size + 1))); michael@0: int read_size = klogctl(KLOG_READ_ALL, msg_buf.rwget(), msg_buf_size); michael@0: michael@0: // Turn buffer into cstring michael@0: read_size = read_size > msg_buf_size ? msg_buf_size : read_size; michael@0: msg_buf.rwget()[read_size] = '\0'; michael@0: michael@0: // Foreach line michael@0: char* line_end; michael@0: char* line_begin = msg_buf.rwget(); michael@0: for (; (line_end = strchr(line_begin, '\n')); line_begin = line_end + 1) { michael@0: // make line into cstring michael@0: *line_end = '\0'; michael@0: michael@0: // Note: Kernel messages look like: michael@0: // <5>[63648.286409] sd 35:0:0:0: Attached scsi generic sg1 type 0 michael@0: // 5 is the loging level michael@0: // [*] is the time timestamp, seconds since boot michael@0: // last comes the logged message michael@0: michael@0: // Since the logging level can be a string we must michael@0: // skip it since scanf lacks wildcard matching michael@0: char* timestamp_begin = strchr(line_begin, '['); michael@0: char after_float; michael@0: double lineTimestamp = -1; michael@0: bool lineTimestampFound = false; michael@0: if (timestamp_begin && michael@0: // Note: scanf treats a ' ' as [ ]* michael@0: // Note: scanf treats [ %lf] as [ %lf thus we must check michael@0: // for the closing bracket outselves. michael@0: 2 == sscanf(timestamp_begin, "[ %lf%c", &lineTimestamp, &after_float) && michael@0: after_float == ']') { michael@0: if (lineTimestamp <= mLastLineChecked) { michael@0: continue; michael@0: } michael@0: michael@0: lineTimestampFound = true; michael@0: mLastLineChecked = lineTimestamp; michael@0: } michael@0: michael@0: michael@0: // Log interesting lines michael@0: for (size_t i = 0; i < regex_count; i++) { michael@0: int matching = !regexec(&(mRegexes[i]), line_begin, 0, NULL, 0); michael@0: if (matching) { michael@0: // Log content of kernel message. We try to skip the ], but if for michael@0: // some reason (most likely due to buffer overflow/wraparound), we michael@0: // can't find the ] then we just log the entire line. michael@0: char* endOfTimestamp = strchr(line_begin, ']'); michael@0: if (endOfTimestamp && endOfTimestamp[1] == ' ') { michael@0: // skip the ] and the space that follows it michael@0: line_begin = endOfTimestamp + 2; michael@0: } michael@0: if (!lineTimestampFound) { michael@0: OOM_LOG(ANDROID_LOG_WARN, "following kill message may be a duplicate"); michael@0: } michael@0: OOM_LOG(ANDROID_LOG_ERROR, "[Kill]: %s\n", line_begin); michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: static void michael@0: EnsureKernelLowMemKillerParamsSet() michael@0: { michael@0: static bool kernelLowMemKillerParamsSet; michael@0: if (kernelLowMemKillerParamsSet) { michael@0: return; michael@0: } michael@0: kernelLowMemKillerParamsSet = true; michael@0: michael@0: HAL_LOG(("Setting kernel's low-mem killer parameters.")); michael@0: michael@0: // Set /sys/module/lowmemorykiller/parameters/{adj,minfree,notify_trigger} michael@0: // according to our prefs. These files let us tune when the kernel kills michael@0: // processes when we're low on memory, and when it notifies us that we're michael@0: // running low on available memory. michael@0: // michael@0: // adj and minfree are both comma-separated lists of integers. If adj="A,B" michael@0: // and minfree="X,Y", then the kernel will kill processes with oom_adj michael@0: // A or higher once we have fewer than X pages of memory free, and will kill michael@0: // processes with oom_adj B or higher once we have fewer than Y pages of michael@0: // memory free. michael@0: // michael@0: // notify_trigger is a single integer. If we set notify_trigger=Z, then michael@0: // we'll get notified when there are fewer than Z pages of memory free. (See michael@0: // GonkMemoryPressureMonitoring.cpp.) michael@0: michael@0: // Build the adj and minfree strings. michael@0: nsAutoCString adjParams; michael@0: nsAutoCString minfreeParams; michael@0: michael@0: int32_t lowerBoundOfNextOomScoreAdj = OOM_SCORE_ADJ_MIN - 1; michael@0: int32_t lowerBoundOfNextKillUnderKB = 0; michael@0: int32_t countOfLowmemorykillerParametersSets = 0; michael@0: michael@0: for (int i = NUM_PROCESS_PRIORITY - 1; i >= 0; i--) { michael@0: // The system doesn't function correctly if we're missing these prefs, so michael@0: // crash loudly. michael@0: michael@0: ProcessPriority priority = static_cast(i); michael@0: michael@0: int32_t oomScoreAdj; michael@0: if (!NS_SUCCEEDED(Preferences::GetInt( michael@0: nsPrintfCString("hal.processPriorityManager.gonk.%s.OomScoreAdjust", michael@0: ProcessPriorityToString(priority)).get(), michael@0: &oomScoreAdj))) { michael@0: MOZ_CRASH(); michael@0: } michael@0: michael@0: int32_t killUnderKB; michael@0: if (!NS_SUCCEEDED(Preferences::GetInt( michael@0: nsPrintfCString("hal.processPriorityManager.gonk.%s.KillUnderKB", michael@0: ProcessPriorityToString(priority)).get(), michael@0: &killUnderKB))) { michael@0: // ProcessPriority values like PROCESS_PRIORITY_FOREGROUND_KEYBOARD, michael@0: // which has only OomScoreAdjust but lacks KillUnderMB value, will not michael@0: // create new LMK parameters. michael@0: continue; michael@0: } michael@0: michael@0: // The LMK in kernel silently malfunctions if we assign the parameters michael@0: // in non-increasing order, so we add this assertion here. See bug 887192. michael@0: MOZ_ASSERT(oomScoreAdj > lowerBoundOfNextOomScoreAdj); michael@0: MOZ_ASSERT(killUnderKB > lowerBoundOfNextKillUnderKB); michael@0: michael@0: // The LMK in kernel only accept 6 sets of LMK parameters. See bug 914728. michael@0: MOZ_ASSERT(countOfLowmemorykillerParametersSets < 6); michael@0: michael@0: // adj is in oom_adj units. michael@0: adjParams.AppendPrintf("%d,", OomAdjOfOomScoreAdj(oomScoreAdj)); michael@0: michael@0: // minfree is in pages. michael@0: minfreeParams.AppendPrintf("%d,", killUnderKB * 1024 / PAGE_SIZE); michael@0: michael@0: lowerBoundOfNextOomScoreAdj = oomScoreAdj; michael@0: lowerBoundOfNextKillUnderKB = killUnderKB; michael@0: countOfLowmemorykillerParametersSets++; michael@0: } michael@0: michael@0: // Strip off trailing commas. michael@0: adjParams.Cut(adjParams.Length() - 1, 1); michael@0: minfreeParams.Cut(minfreeParams.Length() - 1, 1); michael@0: if (!adjParams.IsEmpty() && !minfreeParams.IsEmpty()) { michael@0: WriteToFile("/sys/module/lowmemorykiller/parameters/adj", adjParams.get()); michael@0: WriteToFile("/sys/module/lowmemorykiller/parameters/minfree", minfreeParams.get()); michael@0: } michael@0: michael@0: // Set the low-memory-notification threshold. michael@0: int32_t lowMemNotifyThresholdKB; michael@0: if (NS_SUCCEEDED(Preferences::GetInt( michael@0: "hal.processPriorityManager.gonk.notifyLowMemUnderKB", michael@0: &lowMemNotifyThresholdKB))) { michael@0: michael@0: // notify_trigger is in pages. michael@0: WriteToFile("/sys/module/lowmemorykiller/parameters/notify_trigger", michael@0: nsPrintfCString("%d", lowMemNotifyThresholdKB * 1024 / PAGE_SIZE).get()); michael@0: } michael@0: michael@0: // Ensure OOM events appear in logcat michael@0: nsRefPtr oomLogger = new OomVictimLogger(); michael@0: nsCOMPtr os = services::GetObserverService(); michael@0: if (os) { michael@0: os->AddObserver(oomLogger, "ipc:content-shutdown", false); michael@0: } michael@0: } michael@0: michael@0: static void michael@0: SetNiceForPid(int aPid, int aNice) michael@0: { michael@0: errno = 0; michael@0: int origProcPriority = getpriority(PRIO_PROCESS, aPid); michael@0: if (errno) { michael@0: LOG("Unable to get nice for pid=%d; error %d. SetNiceForPid bailing.", michael@0: aPid, errno); michael@0: return; michael@0: } michael@0: michael@0: int rv = setpriority(PRIO_PROCESS, aPid, aNice); michael@0: if (rv) { michael@0: LOG("Unable to set nice for pid=%d; error %d. SetNiceForPid bailing.", michael@0: aPid, errno); michael@0: return; michael@0: } michael@0: michael@0: // On Linux, setpriority(aPid) modifies the priority only of the main michael@0: // thread of that process. We have to modify the priorities of all of the michael@0: // process's threads as well, so iterate over all the threads and increase michael@0: // each of their priorites by aNice - origProcPriority (and also ensure that michael@0: // none of the tasks has a lower priority than the main thread). michael@0: // michael@0: // This is horribly racy. michael@0: michael@0: DIR* tasksDir = opendir(nsPrintfCString("/proc/%d/task/", aPid).get()); michael@0: if (!tasksDir) { michael@0: LOG("Unable to open /proc/%d/task. SetNiceForPid bailing.", aPid); michael@0: return; michael@0: } michael@0: michael@0: // Be careful not to leak tasksDir; after this point, we must call closedir(). michael@0: michael@0: while (struct dirent* de = readdir(tasksDir)) { michael@0: char* endptr = nullptr; michael@0: long tidlong = strtol(de->d_name, &endptr, /* base */ 10); michael@0: if (*endptr || tidlong < 0 || tidlong > INT32_MAX || tidlong == aPid) { michael@0: // if dp->d_name was not an integer, was negative (?!) or too large, or michael@0: // was the same as aPid, we're not interested. michael@0: // michael@0: // (The |tidlong == aPid| check is very important; without it, we'll michael@0: // renice aPid twice, and the second renice will be relative to the michael@0: // priority set by the first renice.) michael@0: continue; michael@0: } michael@0: michael@0: int tid = static_cast(tidlong); michael@0: michael@0: errno = 0; michael@0: // Get and set the task's new priority. michael@0: int origtaskpriority = getpriority(PRIO_PROCESS, tid); michael@0: if (errno) { michael@0: LOG("Unable to get nice for tid=%d (pid=%d); error %d. This isn't " michael@0: "necessarily a problem; it could be a benign race condition.", michael@0: tid, aPid, errno); michael@0: continue; michael@0: } michael@0: michael@0: int newtaskpriority = michael@0: std::max(origtaskpriority - origProcPriority + aNice, aNice); michael@0: rv = setpriority(PRIO_PROCESS, tid, newtaskpriority); michael@0: michael@0: if (rv) { michael@0: LOG("Unable to set nice for tid=%d (pid=%d); error %d. This isn't " michael@0: "necessarily a problem; it could be a benign race condition.", michael@0: tid, aPid, errno); michael@0: continue; michael@0: } michael@0: } michael@0: michael@0: LOG("Changed nice for pid %d from %d to %d.", michael@0: aPid, origProcPriority, aNice); michael@0: michael@0: closedir(tasksDir); michael@0: } michael@0: michael@0: void michael@0: SetProcessPriority(int aPid, michael@0: ProcessPriority aPriority, michael@0: ProcessCPUPriority aCPUPriority, michael@0: uint32_t aBackgroundLRU) michael@0: { michael@0: HAL_LOG(("SetProcessPriority(pid=%d, priority=%d, cpuPriority=%d, LRU=%u)", michael@0: aPid, aPriority, aCPUPriority, aBackgroundLRU)); michael@0: michael@0: // If this is the first time SetProcessPriority was called, set the kernel's michael@0: // OOM parameters according to our prefs. michael@0: // michael@0: // We could/should do this on startup instead of waiting for the first michael@0: // SetProcessPriorityCall. But in practice, the master process needs to set michael@0: // its priority early in the game, so we can reasonably rely on michael@0: // SetProcessPriority being called early in startup. michael@0: EnsureKernelLowMemKillerParamsSet(); michael@0: michael@0: int32_t oomScoreAdj = 0; michael@0: nsresult rv = Preferences::GetInt(nsPrintfCString( michael@0: "hal.processPriorityManager.gonk.%s.OomScoreAdjust", michael@0: ProcessPriorityToString(aPriority)).get(), &oomScoreAdj); michael@0: michael@0: RoundOomScoreAdjUpWithBackroundLRU(oomScoreAdj, aBackgroundLRU); michael@0: michael@0: if (NS_SUCCEEDED(rv)) { michael@0: int clampedOomScoreAdj = clamped(oomScoreAdj, OOM_SCORE_ADJ_MIN, michael@0: OOM_SCORE_ADJ_MAX); michael@0: if(clampedOomScoreAdj != oomScoreAdj) { michael@0: HAL_LOG(("Clamping OOM adjustment for pid %d to %d", michael@0: aPid, clampedOomScoreAdj)); michael@0: } else { michael@0: HAL_LOG(("Setting OOM adjustment for pid %d to %d", michael@0: aPid, clampedOomScoreAdj)); michael@0: } michael@0: michael@0: // We try the newer interface first, and fall back to the older interface michael@0: // on failure. michael@0: michael@0: if (!WriteToFile(nsPrintfCString("/proc/%d/oom_score_adj", aPid).get(), michael@0: nsPrintfCString("%d", clampedOomScoreAdj).get())) michael@0: { michael@0: int oomAdj = OomAdjOfOomScoreAdj(clampedOomScoreAdj); michael@0: michael@0: WriteToFile(nsPrintfCString("/proc/%d/oom_adj", aPid).get(), michael@0: nsPrintfCString("%d", oomAdj).get()); michael@0: } michael@0: } else { michael@0: LOG("Unable to read oom_score_adj pref for priority %s; " michael@0: "are the prefs messed up?", michael@0: ProcessPriorityToString(aPriority)); michael@0: MOZ_ASSERT(false); michael@0: } michael@0: michael@0: int32_t nice = 0; michael@0: michael@0: if (aCPUPriority == PROCESS_CPU_PRIORITY_NORMAL) { michael@0: rv = Preferences::GetInt( michael@0: nsPrintfCString("hal.processPriorityManager.gonk.%s.Nice", michael@0: ProcessPriorityToString(aPriority)).get(), michael@0: &nice); michael@0: } else if (aCPUPriority == PROCESS_CPU_PRIORITY_LOW) { michael@0: rv = Preferences::GetInt("hal.processPriorityManager.gonk.LowCPUNice", michael@0: &nice); michael@0: } else { michael@0: LOG("Unable to read niceness pref for priority %s; " michael@0: "are the prefs messed up?", michael@0: ProcessPriorityToString(aPriority)); michael@0: MOZ_ASSERT(false); michael@0: rv = NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: if (NS_SUCCEEDED(rv)) { michael@0: LOG("Setting nice for pid %d to %d", aPid, nice); michael@0: SetNiceForPid(aPid, nice); michael@0: } michael@0: } michael@0: michael@0: void michael@0: FactoryReset() michael@0: { michael@0: nsCOMPtr recoveryService = michael@0: do_GetService("@mozilla.org/recovery-service;1"); michael@0: if (!recoveryService) { michael@0: NS_WARNING("Could not get recovery service!"); michael@0: return; michael@0: } michael@0: michael@0: recoveryService->FactoryReset(); michael@0: } michael@0: michael@0: } // hal_impl michael@0: } // mozilla