hal/gonk/GonkHal.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim: set sw=2 ts=8 et ft=cpp : */
michael@0 3 /* Copyright 2012 Mozilla Foundation and Mozilla contributors
michael@0 4 *
michael@0 5 * Licensed under the Apache License, Version 2.0 (the "License");
michael@0 6 * you may not use this file except in compliance with the License.
michael@0 7 * You may obtain a copy of the License at
michael@0 8 *
michael@0 9 * http://www.apache.org/licenses/LICENSE-2.0
michael@0 10 *
michael@0 11 * Unless required by applicable law or agreed to in writing, software
michael@0 12 * distributed under the License is distributed on an "AS IS" BASIS,
michael@0 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
michael@0 14 * See the License for the specific language governing permissions and
michael@0 15 * limitations under the License.
michael@0 16 */
michael@0 17
michael@0 18 #include <ctype.h>
michael@0 19 #include <errno.h>
michael@0 20 #include <fcntl.h>
michael@0 21 #include <linux/android_alarm.h>
michael@0 22 #include <math.h>
michael@0 23 #include <regex.h>
michael@0 24 #include <stdio.h>
michael@0 25 #include <sys/klog.h>
michael@0 26 #include <sys/syscall.h>
michael@0 27 #include <sys/resource.h>
michael@0 28 #include <time.h>
michael@0 29 #include <asm/page.h>
michael@0 30
michael@0 31 #include "mozilla/DebugOnly.h"
michael@0 32
michael@0 33 #include "android/log.h"
michael@0 34 #include "cutils/properties.h"
michael@0 35 #include "hardware/hardware.h"
michael@0 36 #include "hardware/lights.h"
michael@0 37 #include "hardware_legacy/uevent.h"
michael@0 38 #include "hardware_legacy/vibrator.h"
michael@0 39 #include "hardware_legacy/power.h"
michael@0 40 #include "libdisplay/GonkDisplay.h"
michael@0 41
michael@0 42 #include "base/message_loop.h"
michael@0 43
michael@0 44 #include "Hal.h"
michael@0 45 #include "HalImpl.h"
michael@0 46 #include "mozilla/ArrayUtils.h"
michael@0 47 #include "mozilla/dom/battery/Constants.h"
michael@0 48 #include "mozilla/FileUtils.h"
michael@0 49 #include "mozilla/Monitor.h"
michael@0 50 #include "mozilla/RefPtr.h"
michael@0 51 #include "mozilla/Services.h"
michael@0 52 #include "mozilla/StaticPtr.h"
michael@0 53 #include "mozilla/Preferences.h"
michael@0 54 #include "nsAlgorithm.h"
michael@0 55 #include "nsPrintfCString.h"
michael@0 56 #include "nsIObserver.h"
michael@0 57 #include "nsIObserverService.h"
michael@0 58 #include "nsIRecoveryService.h"
michael@0 59 #include "nsIRunnable.h"
michael@0 60 #include "nsScreenManagerGonk.h"
michael@0 61 #include "nsThreadUtils.h"
michael@0 62 #include "nsThreadUtils.h"
michael@0 63 #include "nsIThread.h"
michael@0 64 #include "nsXULAppAPI.h"
michael@0 65 #include "OrientationObserver.h"
michael@0 66 #include "UeventPoller.h"
michael@0 67 #include <algorithm>
michael@0 68
michael@0 69 #define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "Gonk", args)
michael@0 70 #define NsecPerMsec 1000000LL
michael@0 71 #define NsecPerSec 1000000000
michael@0 72
michael@0 73 // The header linux/oom.h is not available in bionic libc. We
michael@0 74 // redefine some of its constants here.
michael@0 75
michael@0 76 #ifndef OOM_DISABLE
michael@0 77 #define OOM_DISABLE (-17)
michael@0 78 #endif
michael@0 79
michael@0 80 #ifndef OOM_ADJUST_MIN
michael@0 81 #define OOM_ADJUST_MIN (-16)
michael@0 82 #endif
michael@0 83
michael@0 84 #ifndef OOM_ADJUST_MAX
michael@0 85 #define OOM_ADJUST_MAX 15
michael@0 86 #endif
michael@0 87
michael@0 88 #ifndef OOM_SCORE_ADJ_MIN
michael@0 89 #define OOM_SCORE_ADJ_MIN (-1000)
michael@0 90 #endif
michael@0 91
michael@0 92 #ifndef OOM_SCORE_ADJ_MAX
michael@0 93 #define OOM_SCORE_ADJ_MAX 1000
michael@0 94 #endif
michael@0 95
michael@0 96 #ifndef BATTERY_CHARGING_ARGB
michael@0 97 #define BATTERY_CHARGING_ARGB 0x00FF0000
michael@0 98 #endif
michael@0 99 #ifndef BATTERY_FULL_ARGB
michael@0 100 #define BATTERY_FULL_ARGB 0x0000FF00
michael@0 101 #endif
michael@0 102
michael@0 103 using namespace mozilla;
michael@0 104 using namespace mozilla::hal;
michael@0 105
michael@0 106 namespace mozilla {
michael@0 107 namespace hal_impl {
michael@0 108
michael@0 109 namespace {
michael@0 110
michael@0 111 /**
michael@0 112 * This runnable runs for the lifetime of the program, once started. It's
michael@0 113 * responsible for "playing" vibration patterns.
michael@0 114 */
michael@0 115 class VibratorRunnable
michael@0 116 : public nsIRunnable
michael@0 117 , public nsIObserver
michael@0 118 {
michael@0 119 public:
michael@0 120 VibratorRunnable()
michael@0 121 : mMonitor("VibratorRunnable")
michael@0 122 , mIndex(0)
michael@0 123 {
michael@0 124 nsCOMPtr<nsIObserverService> os = services::GetObserverService();
michael@0 125 if (!os) {
michael@0 126 NS_WARNING("Could not get observer service!");
michael@0 127 return;
michael@0 128 }
michael@0 129
michael@0 130 os->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
michael@0 131 }
michael@0 132
michael@0 133 NS_DECL_THREADSAFE_ISUPPORTS
michael@0 134 NS_DECL_NSIRUNNABLE
michael@0 135 NS_DECL_NSIOBSERVER
michael@0 136
michael@0 137 // Run on the main thread, not the vibrator thread.
michael@0 138 void Vibrate(const nsTArray<uint32_t> &pattern);
michael@0 139 void CancelVibrate();
michael@0 140
michael@0 141 static bool ShuttingDown() { return sShuttingDown; }
michael@0 142
michael@0 143 private:
michael@0 144 Monitor mMonitor;
michael@0 145
michael@0 146 // The currently-playing pattern.
michael@0 147 nsTArray<uint32_t> mPattern;
michael@0 148
michael@0 149 // The index we're at in the currently-playing pattern. If mIndex >=
michael@0 150 // mPattern.Length(), then we're not currently playing anything.
michael@0 151 uint32_t mIndex;
michael@0 152
michael@0 153 // Set to true in our shutdown observer. When this is true, we kill the
michael@0 154 // vibrator thread.
michael@0 155 static bool sShuttingDown;
michael@0 156 };
michael@0 157
michael@0 158 NS_IMPL_ISUPPORTS(VibratorRunnable, nsIRunnable, nsIObserver);
michael@0 159
michael@0 160 bool VibratorRunnable::sShuttingDown = false;
michael@0 161
michael@0 162 static StaticRefPtr<VibratorRunnable> sVibratorRunnable;
michael@0 163
michael@0 164 NS_IMETHODIMP
michael@0 165 VibratorRunnable::Run()
michael@0 166 {
michael@0 167 MonitorAutoLock lock(mMonitor);
michael@0 168
michael@0 169 // We currently assume that mMonitor.Wait(X) waits for X milliseconds. But in
michael@0 170 // reality, the kernel might not switch to this thread for some time after the
michael@0 171 // wait expires. So there's potential for some inaccuracy here.
michael@0 172 //
michael@0 173 // This doesn't worry me too much. Note that we don't even start vibrating
michael@0 174 // immediately when VibratorRunnable::Vibrate is called -- we go through a
michael@0 175 // condvar onto another thread. Better just to be chill about small errors in
michael@0 176 // the timing here.
michael@0 177
michael@0 178 while (!sShuttingDown) {
michael@0 179 if (mIndex < mPattern.Length()) {
michael@0 180 uint32_t duration = mPattern[mIndex];
michael@0 181 if (mIndex % 2 == 0) {
michael@0 182 vibrator_on(duration);
michael@0 183 }
michael@0 184 mIndex++;
michael@0 185 mMonitor.Wait(PR_MillisecondsToInterval(duration));
michael@0 186 }
michael@0 187 else {
michael@0 188 mMonitor.Wait();
michael@0 189 }
michael@0 190 }
michael@0 191 sVibratorRunnable = nullptr;
michael@0 192 return NS_OK;
michael@0 193 }
michael@0 194
michael@0 195 NS_IMETHODIMP
michael@0 196 VibratorRunnable::Observe(nsISupports *subject, const char *topic,
michael@0 197 const char16_t *data)
michael@0 198 {
michael@0 199 MOZ_ASSERT(strcmp(topic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0);
michael@0 200 MonitorAutoLock lock(mMonitor);
michael@0 201 sShuttingDown = true;
michael@0 202 mMonitor.Notify();
michael@0 203
michael@0 204 return NS_OK;
michael@0 205 }
michael@0 206
michael@0 207 void
michael@0 208 VibratorRunnable::Vibrate(const nsTArray<uint32_t> &pattern)
michael@0 209 {
michael@0 210 MonitorAutoLock lock(mMonitor);
michael@0 211 mPattern = pattern;
michael@0 212 mIndex = 0;
michael@0 213 mMonitor.Notify();
michael@0 214 }
michael@0 215
michael@0 216 void
michael@0 217 VibratorRunnable::CancelVibrate()
michael@0 218 {
michael@0 219 MonitorAutoLock lock(mMonitor);
michael@0 220 mPattern.Clear();
michael@0 221 mPattern.AppendElement(0);
michael@0 222 mIndex = 0;
michael@0 223 mMonitor.Notify();
michael@0 224 }
michael@0 225
michael@0 226 void
michael@0 227 EnsureVibratorThreadInitialized()
michael@0 228 {
michael@0 229 if (sVibratorRunnable) {
michael@0 230 return;
michael@0 231 }
michael@0 232
michael@0 233 sVibratorRunnable = new VibratorRunnable();
michael@0 234 nsCOMPtr<nsIThread> thread;
michael@0 235 NS_NewThread(getter_AddRefs(thread), sVibratorRunnable);
michael@0 236 }
michael@0 237
michael@0 238 } // anonymous namespace
michael@0 239
michael@0 240 void
michael@0 241 Vibrate(const nsTArray<uint32_t> &pattern, const hal::WindowIdentifier &)
michael@0 242 {
michael@0 243 MOZ_ASSERT(NS_IsMainThread());
michael@0 244 if (VibratorRunnable::ShuttingDown()) {
michael@0 245 return;
michael@0 246 }
michael@0 247 EnsureVibratorThreadInitialized();
michael@0 248 sVibratorRunnable->Vibrate(pattern);
michael@0 249 }
michael@0 250
michael@0 251 void
michael@0 252 CancelVibrate(const hal::WindowIdentifier &)
michael@0 253 {
michael@0 254 MOZ_ASSERT(NS_IsMainThread());
michael@0 255 if (VibratorRunnable::ShuttingDown()) {
michael@0 256 return;
michael@0 257 }
michael@0 258 EnsureVibratorThreadInitialized();
michael@0 259 sVibratorRunnable->CancelVibrate();
michael@0 260 }
michael@0 261
michael@0 262 namespace {
michael@0 263
michael@0 264 class BatteryUpdater : public nsRunnable {
michael@0 265 public:
michael@0 266 NS_IMETHOD Run()
michael@0 267 {
michael@0 268 hal::BatteryInformation info;
michael@0 269 hal_impl::GetCurrentBatteryInformation(&info);
michael@0 270
michael@0 271 // Control the battery indicator (led light) here using BatteryInformation
michael@0 272 // we just retrieved.
michael@0 273 uint32_t color = 0; // Format: 0x00rrggbb.
michael@0 274 if (info.charging() && (info.level() == 1)) {
michael@0 275 // Charging and battery full.
michael@0 276 color = BATTERY_FULL_ARGB;
michael@0 277 } else if (info.charging() && (info.level() < 1)) {
michael@0 278 // Charging but not full.
michael@0 279 color = BATTERY_CHARGING_ARGB;
michael@0 280 } // else turn off battery indicator.
michael@0 281
michael@0 282 hal::LightConfiguration aConfig(hal::eHalLightID_Battery,
michael@0 283 hal::eHalLightMode_User,
michael@0 284 hal::eHalLightFlash_None,
michael@0 285 0,
michael@0 286 0,
michael@0 287 color);
michael@0 288 hal_impl::SetLight(hal::eHalLightID_Battery, aConfig);
michael@0 289
michael@0 290 hal::NotifyBatteryChange(info);
michael@0 291 return NS_OK;
michael@0 292 }
michael@0 293 };
michael@0 294
michael@0 295 } // anonymous namespace
michael@0 296
michael@0 297 class BatteryObserver : public IUeventObserver
michael@0 298 {
michael@0 299 public:
michael@0 300 NS_INLINE_DECL_REFCOUNTING(BatteryObserver)
michael@0 301
michael@0 302 BatteryObserver()
michael@0 303 :mUpdater(new BatteryUpdater())
michael@0 304 {
michael@0 305 }
michael@0 306
michael@0 307 virtual void Notify(const NetlinkEvent &aEvent)
michael@0 308 {
michael@0 309 // this will run on IO thread
michael@0 310 NetlinkEvent *event = const_cast<NetlinkEvent*>(&aEvent);
michael@0 311 const char *subsystem = event->getSubsystem();
michael@0 312 // e.g. DEVPATH=/devices/platform/sec-battery/power_supply/battery
michael@0 313 const char *devpath = event->findParam("DEVPATH");
michael@0 314 if (strcmp(subsystem, "power_supply") == 0 &&
michael@0 315 strstr(devpath, "battery")) {
michael@0 316 // aEvent will be valid only in this method.
michael@0 317 NS_DispatchToMainThread(mUpdater);
michael@0 318 }
michael@0 319 }
michael@0 320
michael@0 321 private:
michael@0 322 nsRefPtr<BatteryUpdater> mUpdater;
michael@0 323 };
michael@0 324
michael@0 325 // sBatteryObserver is owned by the IO thread. Only the IO thread may
michael@0 326 // create or destroy it.
michael@0 327 static StaticRefPtr<BatteryObserver> sBatteryObserver;
michael@0 328
michael@0 329 static void
michael@0 330 RegisterBatteryObserverIOThread()
michael@0 331 {
michael@0 332 MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
michael@0 333 MOZ_ASSERT(!sBatteryObserver);
michael@0 334
michael@0 335 sBatteryObserver = new BatteryObserver();
michael@0 336 RegisterUeventListener(sBatteryObserver);
michael@0 337 }
michael@0 338
michael@0 339 void
michael@0 340 EnableBatteryNotifications()
michael@0 341 {
michael@0 342 XRE_GetIOMessageLoop()->PostTask(
michael@0 343 FROM_HERE,
michael@0 344 NewRunnableFunction(RegisterBatteryObserverIOThread));
michael@0 345 }
michael@0 346
michael@0 347 static void
michael@0 348 UnregisterBatteryObserverIOThread()
michael@0 349 {
michael@0 350 MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
michael@0 351 MOZ_ASSERT(sBatteryObserver);
michael@0 352
michael@0 353 UnregisterUeventListener(sBatteryObserver);
michael@0 354 sBatteryObserver = nullptr;
michael@0 355 }
michael@0 356
michael@0 357 void
michael@0 358 DisableBatteryNotifications()
michael@0 359 {
michael@0 360 XRE_GetIOMessageLoop()->PostTask(
michael@0 361 FROM_HERE,
michael@0 362 NewRunnableFunction(UnregisterBatteryObserverIOThread));
michael@0 363 }
michael@0 364
michael@0 365 static bool
michael@0 366 GetCurrentBatteryCharge(int* aCharge)
michael@0 367 {
michael@0 368 bool success = ReadSysFile("/sys/class/power_supply/battery/capacity",
michael@0 369 aCharge);
michael@0 370 if (!success) {
michael@0 371 return false;
michael@0 372 }
michael@0 373
michael@0 374 #ifdef DEBUG
michael@0 375 if ((*aCharge < 0) || (*aCharge > 100)) {
michael@0 376 HAL_LOG(("charge level contains unknown value: %d", *aCharge));
michael@0 377 }
michael@0 378 #endif
michael@0 379
michael@0 380 return (*aCharge >= 0) && (*aCharge <= 100);
michael@0 381 }
michael@0 382
michael@0 383 static bool
michael@0 384 GetCurrentBatteryCharging(int* aCharging)
michael@0 385 {
michael@0 386 static const int BATTERY_NOT_CHARGING = 0;
michael@0 387 static const int BATTERY_CHARGING_USB = 1;
michael@0 388 static const int BATTERY_CHARGING_AC = 2;
michael@0 389
michael@0 390 // Generic device support
michael@0 391
michael@0 392 int chargingSrc;
michael@0 393 bool success =
michael@0 394 ReadSysFile("/sys/class/power_supply/battery/charging_source", &chargingSrc);
michael@0 395
michael@0 396 if (success) {
michael@0 397 #ifdef DEBUG
michael@0 398 if (chargingSrc != BATTERY_NOT_CHARGING &&
michael@0 399 chargingSrc != BATTERY_CHARGING_USB &&
michael@0 400 chargingSrc != BATTERY_CHARGING_AC) {
michael@0 401 HAL_LOG(("charging_source contained unknown value: %d", chargingSrc));
michael@0 402 }
michael@0 403 #endif
michael@0 404
michael@0 405 *aCharging = (chargingSrc == BATTERY_CHARGING_USB ||
michael@0 406 chargingSrc == BATTERY_CHARGING_AC);
michael@0 407 return true;
michael@0 408 }
michael@0 409
michael@0 410 // Otoro device support
michael@0 411
michael@0 412 char chargingSrcString[16];
michael@0 413
michael@0 414 success = ReadSysFile("/sys/class/power_supply/battery/status",
michael@0 415 chargingSrcString, sizeof(chargingSrcString));
michael@0 416 if (success) {
michael@0 417 *aCharging = strcmp(chargingSrcString, "Charging") == 0 ||
michael@0 418 strcmp(chargingSrcString, "Full") == 0;
michael@0 419 return true;
michael@0 420 }
michael@0 421
michael@0 422 return false;
michael@0 423 }
michael@0 424
michael@0 425 void
michael@0 426 GetCurrentBatteryInformation(hal::BatteryInformation* aBatteryInfo)
michael@0 427 {
michael@0 428 int charge;
michael@0 429
michael@0 430 if (GetCurrentBatteryCharge(&charge)) {
michael@0 431 aBatteryInfo->level() = (double)charge / 100.0;
michael@0 432 } else {
michael@0 433 aBatteryInfo->level() = dom::battery::kDefaultLevel;
michael@0 434 }
michael@0 435
michael@0 436 int charging;
michael@0 437
michael@0 438 if (GetCurrentBatteryCharging(&charging)) {
michael@0 439 aBatteryInfo->charging() = charging;
michael@0 440 } else {
michael@0 441 aBatteryInfo->charging() = true;
michael@0 442 }
michael@0 443
michael@0 444 if (!aBatteryInfo->charging() || (aBatteryInfo->level() < 1.0)) {
michael@0 445 aBatteryInfo->remainingTime() = dom::battery::kUnknownRemainingTime;
michael@0 446 } else {
michael@0 447 aBatteryInfo->remainingTime() = dom::battery::kDefaultRemainingTime;
michael@0 448 }
michael@0 449 }
michael@0 450
michael@0 451 namespace {
michael@0 452
michael@0 453 /**
michael@0 454 * RAII class to help us remember to close file descriptors.
michael@0 455 */
michael@0 456 const char *wakeLockFilename = "/sys/power/wake_lock";
michael@0 457 const char *wakeUnlockFilename = "/sys/power/wake_unlock";
michael@0 458
michael@0 459 template<ssize_t n>
michael@0 460 bool ReadFromFile(const char *filename, char (&buf)[n])
michael@0 461 {
michael@0 462 int fd = open(filename, O_RDONLY);
michael@0 463 ScopedClose autoClose(fd);
michael@0 464 if (fd < 0) {
michael@0 465 HAL_LOG(("Unable to open file %s.", filename));
michael@0 466 return false;
michael@0 467 }
michael@0 468
michael@0 469 ssize_t numRead = read(fd, buf, n);
michael@0 470 if (numRead < 0) {
michael@0 471 HAL_LOG(("Error reading from file %s.", filename));
michael@0 472 return false;
michael@0 473 }
michael@0 474
michael@0 475 buf[std::min(numRead, n - 1)] = '\0';
michael@0 476 return true;
michael@0 477 }
michael@0 478
michael@0 479 bool WriteToFile(const char *filename, const char *toWrite)
michael@0 480 {
michael@0 481 int fd = open(filename, O_WRONLY);
michael@0 482 ScopedClose autoClose(fd);
michael@0 483 if (fd < 0) {
michael@0 484 HAL_LOG(("Unable to open file %s.", filename));
michael@0 485 return false;
michael@0 486 }
michael@0 487
michael@0 488 if (write(fd, toWrite, strlen(toWrite)) < 0) {
michael@0 489 HAL_LOG(("Unable to write to file %s.", filename));
michael@0 490 return false;
michael@0 491 }
michael@0 492
michael@0 493 return true;
michael@0 494 }
michael@0 495
michael@0 496 // We can write to screenEnabledFilename to enable/disable the screen, but when
michael@0 497 // we read, we always get "mem"! So we have to keep track ourselves whether
michael@0 498 // the screen is on or not.
michael@0 499 bool sScreenEnabled = true;
michael@0 500
michael@0 501 // We can read wakeLockFilename to find out whether the cpu wake lock
michael@0 502 // is already acquired, but reading and parsing it is a lot more work
michael@0 503 // than tracking it ourselves, and it won't be accurate anyway (kernel
michael@0 504 // internal wake locks aren't counted here.)
michael@0 505 bool sCpuSleepAllowed = true;
michael@0 506
michael@0 507 // Some CPU wake locks may be acquired internally in HAL. We use a counter to
michael@0 508 // keep track of these needs. Note we have to hold |sInternalLockCpuMonitor|
michael@0 509 // when reading or writing this variable to ensure thread-safe.
michael@0 510 int32_t sInternalLockCpuCount = 0;
michael@0 511
michael@0 512 } // anonymous namespace
michael@0 513
michael@0 514 bool
michael@0 515 GetScreenEnabled()
michael@0 516 {
michael@0 517 return sScreenEnabled;
michael@0 518 }
michael@0 519
michael@0 520 void
michael@0 521 SetScreenEnabled(bool enabled)
michael@0 522 {
michael@0 523 GetGonkDisplay()->SetEnabled(enabled);
michael@0 524 sScreenEnabled = enabled;
michael@0 525 }
michael@0 526
michael@0 527 double
michael@0 528 GetScreenBrightness()
michael@0 529 {
michael@0 530 hal::LightConfiguration aConfig;
michael@0 531 hal::LightType light = hal::eHalLightID_Backlight;
michael@0 532
michael@0 533 hal::GetLight(light, &aConfig);
michael@0 534 // backlight is brightness only, so using one of the RGB elements as value.
michael@0 535 int brightness = aConfig.color() & 0xFF;
michael@0 536 return brightness / 255.0;
michael@0 537 }
michael@0 538
michael@0 539 void
michael@0 540 SetScreenBrightness(double brightness)
michael@0 541 {
michael@0 542 // Don't use De Morgan's law to push the ! into this expression; we want to
michael@0 543 // catch NaN too.
michael@0 544 if (!(0 <= brightness && brightness <= 1)) {
michael@0 545 HAL_LOG(("SetScreenBrightness: Dropping illegal brightness %f.",
michael@0 546 brightness));
michael@0 547 return;
michael@0 548 }
michael@0 549
michael@0 550 // Convert the value in [0, 1] to an int between 0 and 255 and convert to a color
michael@0 551 // note that the high byte is FF, corresponding to the alpha channel.
michael@0 552 int val = static_cast<int>(round(brightness * 255));
michael@0 553 uint32_t color = (0xff<<24) + (val<<16) + (val<<8) + val;
michael@0 554
michael@0 555 hal::LightConfiguration aConfig;
michael@0 556 aConfig.mode() = hal::eHalLightMode_User;
michael@0 557 aConfig.flash() = hal::eHalLightFlash_None;
michael@0 558 aConfig.flashOnMS() = aConfig.flashOffMS() = 0;
michael@0 559 aConfig.color() = color;
michael@0 560 hal::SetLight(hal::eHalLightID_Backlight, aConfig);
michael@0 561 hal::SetLight(hal::eHalLightID_Buttons, aConfig);
michael@0 562 }
michael@0 563
michael@0 564 static Monitor* sInternalLockCpuMonitor = nullptr;
michael@0 565
michael@0 566 static void
michael@0 567 UpdateCpuSleepState()
michael@0 568 {
michael@0 569 sInternalLockCpuMonitor->AssertCurrentThreadOwns();
michael@0 570 bool allowed = sCpuSleepAllowed && !sInternalLockCpuCount;
michael@0 571 WriteToFile(allowed ? wakeUnlockFilename : wakeLockFilename, "gecko");
michael@0 572 }
michael@0 573
michael@0 574 static void
michael@0 575 InternalLockCpu() {
michael@0 576 MonitorAutoLock monitor(*sInternalLockCpuMonitor);
michael@0 577 ++sInternalLockCpuCount;
michael@0 578 UpdateCpuSleepState();
michael@0 579 }
michael@0 580
michael@0 581 static void
michael@0 582 InternalUnlockCpu() {
michael@0 583 MonitorAutoLock monitor(*sInternalLockCpuMonitor);
michael@0 584 --sInternalLockCpuCount;
michael@0 585 UpdateCpuSleepState();
michael@0 586 }
michael@0 587
michael@0 588 bool
michael@0 589 GetCpuSleepAllowed()
michael@0 590 {
michael@0 591 return sCpuSleepAllowed;
michael@0 592 }
michael@0 593
michael@0 594 void
michael@0 595 SetCpuSleepAllowed(bool aAllowed)
michael@0 596 {
michael@0 597 MonitorAutoLock monitor(*sInternalLockCpuMonitor);
michael@0 598 sCpuSleepAllowed = aAllowed;
michael@0 599 UpdateCpuSleepState();
michael@0 600 }
michael@0 601
michael@0 602 static light_device_t* sLights[hal::eHalLightID_Count]; // will be initialized to nullptr
michael@0 603
michael@0 604 light_device_t* GetDevice(hw_module_t* module, char const* name)
michael@0 605 {
michael@0 606 int err;
michael@0 607 hw_device_t* device;
michael@0 608 err = module->methods->open(module, name, &device);
michael@0 609 if (err == 0) {
michael@0 610 return (light_device_t*)device;
michael@0 611 } else {
michael@0 612 return nullptr;
michael@0 613 }
michael@0 614 }
michael@0 615
michael@0 616 void
michael@0 617 InitLights()
michael@0 618 {
michael@0 619 // assume that if backlight is nullptr, nothing has been set yet
michael@0 620 // if this is not true, the initialization will occur everytime a light is read or set!
michael@0 621 if (!sLights[hal::eHalLightID_Backlight]) {
michael@0 622 int err;
michael@0 623 hw_module_t* module;
michael@0 624
michael@0 625 err = hw_get_module(LIGHTS_HARDWARE_MODULE_ID, (hw_module_t const**)&module);
michael@0 626 if (err == 0) {
michael@0 627 sLights[hal::eHalLightID_Backlight]
michael@0 628 = GetDevice(module, LIGHT_ID_BACKLIGHT);
michael@0 629 sLights[hal::eHalLightID_Keyboard]
michael@0 630 = GetDevice(module, LIGHT_ID_KEYBOARD);
michael@0 631 sLights[hal::eHalLightID_Buttons]
michael@0 632 = GetDevice(module, LIGHT_ID_BUTTONS);
michael@0 633 sLights[hal::eHalLightID_Battery]
michael@0 634 = GetDevice(module, LIGHT_ID_BATTERY);
michael@0 635 sLights[hal::eHalLightID_Notifications]
michael@0 636 = GetDevice(module, LIGHT_ID_NOTIFICATIONS);
michael@0 637 sLights[hal::eHalLightID_Attention]
michael@0 638 = GetDevice(module, LIGHT_ID_ATTENTION);
michael@0 639 sLights[hal::eHalLightID_Bluetooth]
michael@0 640 = GetDevice(module, LIGHT_ID_BLUETOOTH);
michael@0 641 sLights[hal::eHalLightID_Wifi]
michael@0 642 = GetDevice(module, LIGHT_ID_WIFI);
michael@0 643 }
michael@0 644 }
michael@0 645 }
michael@0 646
michael@0 647 /**
michael@0 648 * The state last set for the lights until liblights supports
michael@0 649 * getting the light state.
michael@0 650 */
michael@0 651 static light_state_t sStoredLightState[hal::eHalLightID_Count];
michael@0 652
michael@0 653 bool
michael@0 654 SetLight(hal::LightType light, const hal::LightConfiguration& aConfig)
michael@0 655 {
michael@0 656 light_state_t state;
michael@0 657
michael@0 658 InitLights();
michael@0 659
michael@0 660 if (light < 0 || light >= hal::eHalLightID_Count ||
michael@0 661 sLights[light] == nullptr) {
michael@0 662 return false;
michael@0 663 }
michael@0 664
michael@0 665 memset(&state, 0, sizeof(light_state_t));
michael@0 666 state.color = aConfig.color();
michael@0 667 state.flashMode = aConfig.flash();
michael@0 668 state.flashOnMS = aConfig.flashOnMS();
michael@0 669 state.flashOffMS = aConfig.flashOffMS();
michael@0 670 state.brightnessMode = aConfig.mode();
michael@0 671
michael@0 672 sLights[light]->set_light(sLights[light], &state);
michael@0 673 sStoredLightState[light] = state;
michael@0 674 return true;
michael@0 675 }
michael@0 676
michael@0 677 bool
michael@0 678 GetLight(hal::LightType light, hal::LightConfiguration* aConfig)
michael@0 679 {
michael@0 680 light_state_t state;
michael@0 681
michael@0 682 #ifdef HAVEGETLIGHT
michael@0 683 InitLights();
michael@0 684 #endif
michael@0 685
michael@0 686 if (light < 0 || light >= hal::eHalLightID_Count ||
michael@0 687 sLights[light] == nullptr) {
michael@0 688 return false;
michael@0 689 }
michael@0 690
michael@0 691 memset(&state, 0, sizeof(light_state_t));
michael@0 692
michael@0 693 #ifdef HAVEGETLIGHT
michael@0 694 sLights[light]->get_light(sLights[light], &state);
michael@0 695 #else
michael@0 696 state = sStoredLightState[light];
michael@0 697 #endif
michael@0 698
michael@0 699 aConfig->light() = light;
michael@0 700 aConfig->color() = state.color;
michael@0 701 aConfig->flash() = hal::FlashMode(state.flashMode);
michael@0 702 aConfig->flashOnMS() = state.flashOnMS;
michael@0 703 aConfig->flashOffMS() = state.flashOffMS;
michael@0 704 aConfig->mode() = hal::LightMode(state.brightnessMode);
michael@0 705
michael@0 706 return true;
michael@0 707 }
michael@0 708
michael@0 709 void
michael@0 710 AdjustSystemClock(int64_t aDeltaMilliseconds)
michael@0 711 {
michael@0 712 int fd;
michael@0 713 struct timespec now;
michael@0 714
michael@0 715 if (aDeltaMilliseconds == 0) {
michael@0 716 return;
michael@0 717 }
michael@0 718
michael@0 719 // Preventing context switch before setting system clock
michael@0 720 sched_yield();
michael@0 721 clock_gettime(CLOCK_REALTIME, &now);
michael@0 722 now.tv_sec += (time_t)(aDeltaMilliseconds / 1000LL);
michael@0 723 now.tv_nsec += (long)((aDeltaMilliseconds % 1000LL) * NsecPerMsec);
michael@0 724 if (now.tv_nsec >= NsecPerSec) {
michael@0 725 now.tv_sec += 1;
michael@0 726 now.tv_nsec -= NsecPerSec;
michael@0 727 }
michael@0 728
michael@0 729 if (now.tv_nsec < 0) {
michael@0 730 now.tv_nsec += NsecPerSec;
michael@0 731 now.tv_sec -= 1;
michael@0 732 }
michael@0 733
michael@0 734 do {
michael@0 735 fd = open("/dev/alarm", O_RDWR);
michael@0 736 } while (fd == -1 && errno == EINTR);
michael@0 737 ScopedClose autoClose(fd);
michael@0 738 if (fd < 0) {
michael@0 739 HAL_LOG(("Failed to open /dev/alarm: %s", strerror(errno)));
michael@0 740 return;
michael@0 741 }
michael@0 742
michael@0 743 if (ioctl(fd, ANDROID_ALARM_SET_RTC, &now) < 0) {
michael@0 744 HAL_LOG(("ANDROID_ALARM_SET_RTC failed: %s", strerror(errno)));
michael@0 745 }
michael@0 746
michael@0 747 hal::NotifySystemClockChange(aDeltaMilliseconds);
michael@0 748 }
michael@0 749
michael@0 750 int32_t
michael@0 751 GetTimezoneOffset()
michael@0 752 {
michael@0 753 PRExplodedTime prTime;
michael@0 754 PR_ExplodeTime(PR_Now(), PR_LocalTimeParameters, &prTime);
michael@0 755
michael@0 756 // Daylight saving time (DST) will be taken into account.
michael@0 757 int32_t offset = prTime.tm_params.tp_gmt_offset;
michael@0 758 offset += prTime.tm_params.tp_dst_offset;
michael@0 759
michael@0 760 // Returns the timezone offset relative to UTC in minutes.
michael@0 761 return -(offset / 60);
michael@0 762 }
michael@0 763
michael@0 764 static int32_t sKernelTimezoneOffset = 0;
michael@0 765
michael@0 766 static void
michael@0 767 UpdateKernelTimezone(int32_t timezoneOffset)
michael@0 768 {
michael@0 769 if (sKernelTimezoneOffset == timezoneOffset) {
michael@0 770 return;
michael@0 771 }
michael@0 772
michael@0 773 // Tell the kernel about the new time zone as well, so that FAT filesystems
michael@0 774 // will get local timestamps rather than UTC timestamps.
michael@0 775 //
michael@0 776 // We assume that /init.rc has a sysclktz entry so that settimeofday has
michael@0 777 // already been called once before we call it (there is a side-effect in
michael@0 778 // the kernel the very first time settimeofday is called where it does some
michael@0 779 // special processing if you only set the timezone).
michael@0 780 struct timezone tz;
michael@0 781 memset(&tz, 0, sizeof(tz));
michael@0 782 tz.tz_minuteswest = timezoneOffset;
michael@0 783 settimeofday(nullptr, &tz);
michael@0 784 sKernelTimezoneOffset = timezoneOffset;
michael@0 785 }
michael@0 786
michael@0 787 void
michael@0 788 SetTimezone(const nsCString& aTimezoneSpec)
michael@0 789 {
michael@0 790 if (aTimezoneSpec.Equals(GetTimezone())) {
michael@0 791 // Even though the timezone hasn't changed, we still need to tell the
michael@0 792 // kernel what the current timezone is. The timezone is persisted in
michael@0 793 // a property and doesn't change across reboots, but the kernel still
michael@0 794 // needs to be updated on every boot.
michael@0 795 UpdateKernelTimezone(GetTimezoneOffset());
michael@0 796 return;
michael@0 797 }
michael@0 798
michael@0 799 int32_t oldTimezoneOffsetMinutes = GetTimezoneOffset();
michael@0 800 property_set("persist.sys.timezone", aTimezoneSpec.get());
michael@0 801 // This function is automatically called by the other time conversion
michael@0 802 // functions that depend on the timezone. To be safe, we call it manually.
michael@0 803 tzset();
michael@0 804 int32_t newTimezoneOffsetMinutes = GetTimezoneOffset();
michael@0 805 UpdateKernelTimezone(newTimezoneOffsetMinutes);
michael@0 806 hal::NotifySystemTimezoneChange(
michael@0 807 hal::SystemTimezoneChangeInformation(
michael@0 808 oldTimezoneOffsetMinutes, newTimezoneOffsetMinutes));
michael@0 809 }
michael@0 810
michael@0 811 nsCString
michael@0 812 GetTimezone()
michael@0 813 {
michael@0 814 char timezone[32];
michael@0 815 property_get("persist.sys.timezone", timezone, "");
michael@0 816 return nsCString(timezone);
michael@0 817 }
michael@0 818
michael@0 819 void
michael@0 820 EnableSystemClockChangeNotifications()
michael@0 821 {
michael@0 822 }
michael@0 823
michael@0 824 void
michael@0 825 DisableSystemClockChangeNotifications()
michael@0 826 {
michael@0 827 }
michael@0 828
michael@0 829 void
michael@0 830 EnableSystemTimezoneChangeNotifications()
michael@0 831 {
michael@0 832 }
michael@0 833
michael@0 834 void
michael@0 835 DisableSystemTimezoneChangeNotifications()
michael@0 836 {
michael@0 837 }
michael@0 838
michael@0 839 // Nothing to do here. Gonk widgetry always listens for screen
michael@0 840 // orientation changes.
michael@0 841 void
michael@0 842 EnableScreenConfigurationNotifications()
michael@0 843 {
michael@0 844 }
michael@0 845
michael@0 846 void
michael@0 847 DisableScreenConfigurationNotifications()
michael@0 848 {
michael@0 849 }
michael@0 850
michael@0 851 void
michael@0 852 GetCurrentScreenConfiguration(hal::ScreenConfiguration* aScreenConfiguration)
michael@0 853 {
michael@0 854 *aScreenConfiguration = nsScreenGonk::GetConfiguration();
michael@0 855 }
michael@0 856
michael@0 857 bool
michael@0 858 LockScreenOrientation(const dom::ScreenOrientation& aOrientation)
michael@0 859 {
michael@0 860 return OrientationObserver::GetInstance()->LockScreenOrientation(aOrientation);
michael@0 861 }
michael@0 862
michael@0 863 void
michael@0 864 UnlockScreenOrientation()
michael@0 865 {
michael@0 866 OrientationObserver::GetInstance()->UnlockScreenOrientation();
michael@0 867 }
michael@0 868
michael@0 869 // This thread will wait for the alarm firing by a blocking IO.
michael@0 870 static pthread_t sAlarmFireWatcherThread;
michael@0 871
michael@0 872 // If |sAlarmData| is non-null, it's owned by the alarm-watcher thread.
michael@0 873 struct AlarmData {
michael@0 874 public:
michael@0 875 AlarmData(int aFd) : mFd(aFd),
michael@0 876 mGeneration(sNextGeneration++),
michael@0 877 mShuttingDown(false) {}
michael@0 878 ScopedClose mFd;
michael@0 879 int mGeneration;
michael@0 880 bool mShuttingDown;
michael@0 881
michael@0 882 static int sNextGeneration;
michael@0 883
michael@0 884 };
michael@0 885
michael@0 886 int AlarmData::sNextGeneration = 0;
michael@0 887
michael@0 888 AlarmData* sAlarmData = nullptr;
michael@0 889
michael@0 890 class AlarmFiredEvent : public nsRunnable {
michael@0 891 public:
michael@0 892 AlarmFiredEvent(int aGeneration) : mGeneration(aGeneration) {}
michael@0 893
michael@0 894 NS_IMETHOD Run() {
michael@0 895 // Guard against spurious notifications caused by an alarm firing
michael@0 896 // concurrently with it being disabled.
michael@0 897 if (sAlarmData && !sAlarmData->mShuttingDown &&
michael@0 898 mGeneration == sAlarmData->mGeneration) {
michael@0 899 hal::NotifyAlarmFired();
michael@0 900 }
michael@0 901 // The fired alarm event has been delivered to the observer (if needed);
michael@0 902 // we can now release a CPU wake lock.
michael@0 903 InternalUnlockCpu();
michael@0 904 return NS_OK;
michael@0 905 }
michael@0 906
michael@0 907 private:
michael@0 908 int mGeneration;
michael@0 909 };
michael@0 910
michael@0 911 // Runs on alarm-watcher thread.
michael@0 912 static void
michael@0 913 DestroyAlarmData(void* aData)
michael@0 914 {
michael@0 915 AlarmData* alarmData = static_cast<AlarmData*>(aData);
michael@0 916 delete alarmData;
michael@0 917 }
michael@0 918
michael@0 919 // Runs on alarm-watcher thread.
michael@0 920 void ShutDownAlarm(int aSigno)
michael@0 921 {
michael@0 922 if (aSigno == SIGUSR1 && sAlarmData) {
michael@0 923 sAlarmData->mShuttingDown = true;
michael@0 924 }
michael@0 925 return;
michael@0 926 }
michael@0 927
michael@0 928 static void*
michael@0 929 WaitForAlarm(void* aData)
michael@0 930 {
michael@0 931 pthread_cleanup_push(DestroyAlarmData, aData);
michael@0 932
michael@0 933 AlarmData* alarmData = static_cast<AlarmData*>(aData);
michael@0 934
michael@0 935 while (!alarmData->mShuttingDown) {
michael@0 936 int alarmTypeFlags = 0;
michael@0 937
michael@0 938 // ALARM_WAIT apparently will block even if an alarm hasn't been
michael@0 939 // programmed, although this behavior doesn't seem to be
michael@0 940 // documented. We rely on that here to avoid spinning the CPU
michael@0 941 // while awaiting an alarm to be programmed.
michael@0 942 do {
michael@0 943 alarmTypeFlags = ioctl(alarmData->mFd, ANDROID_ALARM_WAIT);
michael@0 944 } while (alarmTypeFlags < 0 && errno == EINTR &&
michael@0 945 !alarmData->mShuttingDown);
michael@0 946
michael@0 947 if (!alarmData->mShuttingDown && alarmTypeFlags >= 0 &&
michael@0 948 (alarmTypeFlags & ANDROID_ALARM_RTC_WAKEUP_MASK)) {
michael@0 949 // To make sure the observer can get the alarm firing notification
michael@0 950 // *on time* (the system won't sleep during the process in any way),
michael@0 951 // we need to acquire a CPU wake lock before firing the alarm event.
michael@0 952 InternalLockCpu();
michael@0 953 nsRefPtr<AlarmFiredEvent> event =
michael@0 954 new AlarmFiredEvent(alarmData->mGeneration);
michael@0 955 NS_DispatchToMainThread(event);
michael@0 956 }
michael@0 957 }
michael@0 958
michael@0 959 pthread_cleanup_pop(1);
michael@0 960 return nullptr;
michael@0 961 }
michael@0 962
michael@0 963 bool
michael@0 964 EnableAlarm()
michael@0 965 {
michael@0 966 MOZ_ASSERT(!sAlarmData);
michael@0 967
michael@0 968 int alarmFd = open("/dev/alarm", O_RDWR);
michael@0 969 if (alarmFd < 0) {
michael@0 970 HAL_LOG(("Failed to open alarm device: %s.", strerror(errno)));
michael@0 971 return false;
michael@0 972 }
michael@0 973
michael@0 974 nsAutoPtr<AlarmData> alarmData(new AlarmData(alarmFd));
michael@0 975
michael@0 976 struct sigaction actions;
michael@0 977 memset(&actions, 0, sizeof(actions));
michael@0 978 sigemptyset(&actions.sa_mask);
michael@0 979 actions.sa_flags = 0;
michael@0 980 actions.sa_handler = ShutDownAlarm;
michael@0 981 if (sigaction(SIGUSR1, &actions, nullptr)) {
michael@0 982 HAL_LOG(("Failed to set SIGUSR1 signal for alarm-watcher thread."));
michael@0 983 return false;
michael@0 984 }
michael@0 985
michael@0 986 pthread_attr_t attr;
michael@0 987 pthread_attr_init(&attr);
michael@0 988 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
michael@0 989
michael@0 990 // Initialize the monitor for internally locking CPU to ensure thread-safe
michael@0 991 // before running the alarm-watcher thread.
michael@0 992 sInternalLockCpuMonitor = new Monitor("sInternalLockCpuMonitor");
michael@0 993 int status = pthread_create(&sAlarmFireWatcherThread, &attr, WaitForAlarm,
michael@0 994 alarmData.get());
michael@0 995 if (status) {
michael@0 996 alarmData = nullptr;
michael@0 997 delete sInternalLockCpuMonitor;
michael@0 998 HAL_LOG(("Failed to create alarm-watcher thread. Status: %d.", status));
michael@0 999 return false;
michael@0 1000 }
michael@0 1001
michael@0 1002 pthread_attr_destroy(&attr);
michael@0 1003
michael@0 1004 // The thread owns this now. We only hold a pointer.
michael@0 1005 sAlarmData = alarmData.forget();
michael@0 1006 return true;
michael@0 1007 }
michael@0 1008
michael@0 1009 void
michael@0 1010 DisableAlarm()
michael@0 1011 {
michael@0 1012 MOZ_ASSERT(sAlarmData);
michael@0 1013
michael@0 1014 // NB: this must happen-before the thread cancellation.
michael@0 1015 sAlarmData = nullptr;
michael@0 1016
michael@0 1017 // The cancel will interrupt the thread and destroy it, freeing the
michael@0 1018 // data pointed at by sAlarmData.
michael@0 1019 DebugOnly<int> err = pthread_kill(sAlarmFireWatcherThread, SIGUSR1);
michael@0 1020 MOZ_ASSERT(!err);
michael@0 1021
michael@0 1022 delete sInternalLockCpuMonitor;
michael@0 1023 }
michael@0 1024
michael@0 1025 bool
michael@0 1026 SetAlarm(int32_t aSeconds, int32_t aNanoseconds)
michael@0 1027 {
michael@0 1028 if (!sAlarmData) {
michael@0 1029 HAL_LOG(("We should have enabled the alarm."));
michael@0 1030 return false;
michael@0 1031 }
michael@0 1032
michael@0 1033 struct timespec ts;
michael@0 1034 ts.tv_sec = aSeconds;
michael@0 1035 ts.tv_nsec = aNanoseconds;
michael@0 1036
michael@0 1037 // Currently we only support RTC wakeup alarm type.
michael@0 1038 const int result = ioctl(sAlarmData->mFd,
michael@0 1039 ANDROID_ALARM_SET(ANDROID_ALARM_RTC_WAKEUP), &ts);
michael@0 1040
michael@0 1041 if (result < 0) {
michael@0 1042 HAL_LOG(("Unable to set alarm: %s.", strerror(errno)));
michael@0 1043 return false;
michael@0 1044 }
michael@0 1045
michael@0 1046 return true;
michael@0 1047 }
michael@0 1048
michael@0 1049 static int
michael@0 1050 OomAdjOfOomScoreAdj(int aOomScoreAdj)
michael@0 1051 {
michael@0 1052 // Convert OOM adjustment from the domain of /proc/<pid>/oom_score_adj
michael@0 1053 // to the domain of /proc/<pid>/oom_adj.
michael@0 1054
michael@0 1055 int adj;
michael@0 1056
michael@0 1057 if (aOomScoreAdj < 0) {
michael@0 1058 adj = (OOM_DISABLE * aOomScoreAdj) / OOM_SCORE_ADJ_MIN;
michael@0 1059 } else {
michael@0 1060 adj = (OOM_ADJUST_MAX * aOomScoreAdj) / OOM_SCORE_ADJ_MAX;
michael@0 1061 }
michael@0 1062
michael@0 1063 return adj;
michael@0 1064 }
michael@0 1065
michael@0 1066 static void
michael@0 1067 RoundOomScoreAdjUpWithBackroundLRU(int& aOomScoreAdj, uint32_t aBackgroundLRU)
michael@0 1068 {
michael@0 1069 // We want to add minimum value to round OomScoreAdj up according to
michael@0 1070 // the steps by aBackgroundLRU.
michael@0 1071 aOomScoreAdj +=
michael@0 1072 ceil(((float)OOM_SCORE_ADJ_MAX / OOM_ADJUST_MAX) * aBackgroundLRU);
michael@0 1073 }
michael@0 1074
michael@0 1075 #define OOM_LOG(level, args...) __android_log_print(level, "OomLogger", ##args)
michael@0 1076 class OomVictimLogger MOZ_FINAL
michael@0 1077 : public nsIObserver
michael@0 1078 {
michael@0 1079 public:
michael@0 1080 OomVictimLogger()
michael@0 1081 : mLastLineChecked(-1.0),
michael@0 1082 mRegexes(nullptr)
michael@0 1083 {
michael@0 1084 // Enable timestamps in kernel's printk
michael@0 1085 WriteToFile("/sys/module/printk/parameters/time", "Y");
michael@0 1086 }
michael@0 1087
michael@0 1088 NS_DECL_ISUPPORTS
michael@0 1089 NS_DECL_NSIOBSERVER
michael@0 1090 private:
michael@0 1091 double mLastLineChecked;
michael@0 1092 ScopedFreePtr<regex_t> mRegexes;
michael@0 1093 };
michael@0 1094 NS_IMPL_ISUPPORTS(OomVictimLogger, nsIObserver);
michael@0 1095
michael@0 1096 NS_IMETHODIMP
michael@0 1097 OomVictimLogger::Observe(
michael@0 1098 nsISupports* aSubject,
michael@0 1099 const char* aTopic,
michael@0 1100 const char16_t* aData)
michael@0 1101 {
michael@0 1102 nsDependentCString event_type(aTopic);
michael@0 1103 if (!event_type.EqualsLiteral("ipc:content-shutdown")) {
michael@0 1104 return NS_OK;
michael@0 1105 }
michael@0 1106
michael@0 1107 // OOM message finding regexes
michael@0 1108 const char* const regexes_raw[] = {
michael@0 1109 ".*select.*to kill.*",
michael@0 1110 ".*send sigkill to.*",
michael@0 1111 ".*lowmem_shrink.*, return",
michael@0 1112 ".*lowmem_shrink.*, ofree.*"};
michael@0 1113 const size_t regex_count = ArrayLength(regexes_raw);
michael@0 1114
michael@0 1115 // Compile our regex just in time
michael@0 1116 if (!mRegexes) {
michael@0 1117 mRegexes = static_cast<regex_t*>(malloc(sizeof(regex_t) * regex_count));
michael@0 1118 for (size_t i = 0; i < regex_count; i++) {
michael@0 1119 int compilation_err = regcomp(&(mRegexes[i]), regexes_raw[i], REG_NOSUB);
michael@0 1120 if (compilation_err) {
michael@0 1121 OOM_LOG(ANDROID_LOG_ERROR, "Cannot compile regex \"%s\"\n", regexes_raw[i]);
michael@0 1122 return NS_OK;
michael@0 1123 }
michael@0 1124 }
michael@0 1125 }
michael@0 1126
michael@0 1127 #ifndef KLOG_SIZE_BUFFER
michael@0 1128 // Upstream bionic in commit
michael@0 1129 // e249b059637b49a285ed9f58a2a18bfd054e5d95
michael@0 1130 // deprecated the old klog defs.
michael@0 1131 // Our current bionic does not hit this
michael@0 1132 // change yet so handle the future change.
michael@0 1133 #define KLOG_SIZE_BUFFER KLOG_WRITE
michael@0 1134 #else
michael@0 1135 // Once the change hits our bionic this ifndef
michael@0 1136 // can be removed.
michael@0 1137 #warning "Please remove KLOG_UNREAD_SIZE compatability def"
michael@0 1138 #endif
michael@0 1139 // Retreive kernel log
michael@0 1140 int msg_buf_size = klogctl(KLOG_SIZE_BUFFER, NULL, 0);
michael@0 1141 ScopedFreePtr<char> msg_buf(static_cast<char *>(malloc(msg_buf_size + 1)));
michael@0 1142 int read_size = klogctl(KLOG_READ_ALL, msg_buf.rwget(), msg_buf_size);
michael@0 1143
michael@0 1144 // Turn buffer into cstring
michael@0 1145 read_size = read_size > msg_buf_size ? msg_buf_size : read_size;
michael@0 1146 msg_buf.rwget()[read_size] = '\0';
michael@0 1147
michael@0 1148 // Foreach line
michael@0 1149 char* line_end;
michael@0 1150 char* line_begin = msg_buf.rwget();
michael@0 1151 for (; (line_end = strchr(line_begin, '\n')); line_begin = line_end + 1) {
michael@0 1152 // make line into cstring
michael@0 1153 *line_end = '\0';
michael@0 1154
michael@0 1155 // Note: Kernel messages look like:
michael@0 1156 // <5>[63648.286409] sd 35:0:0:0: Attached scsi generic sg1 type 0
michael@0 1157 // 5 is the loging level
michael@0 1158 // [*] is the time timestamp, seconds since boot
michael@0 1159 // last comes the logged message
michael@0 1160
michael@0 1161 // Since the logging level can be a string we must
michael@0 1162 // skip it since scanf lacks wildcard matching
michael@0 1163 char* timestamp_begin = strchr(line_begin, '[');
michael@0 1164 char after_float;
michael@0 1165 double lineTimestamp = -1;
michael@0 1166 bool lineTimestampFound = false;
michael@0 1167 if (timestamp_begin &&
michael@0 1168 // Note: scanf treats a ' ' as [ ]*
michael@0 1169 // Note: scanf treats [ %lf] as [ %lf thus we must check
michael@0 1170 // for the closing bracket outselves.
michael@0 1171 2 == sscanf(timestamp_begin, "[ %lf%c", &lineTimestamp, &after_float) &&
michael@0 1172 after_float == ']') {
michael@0 1173 if (lineTimestamp <= mLastLineChecked) {
michael@0 1174 continue;
michael@0 1175 }
michael@0 1176
michael@0 1177 lineTimestampFound = true;
michael@0 1178 mLastLineChecked = lineTimestamp;
michael@0 1179 }
michael@0 1180
michael@0 1181
michael@0 1182 // Log interesting lines
michael@0 1183 for (size_t i = 0; i < regex_count; i++) {
michael@0 1184 int matching = !regexec(&(mRegexes[i]), line_begin, 0, NULL, 0);
michael@0 1185 if (matching) {
michael@0 1186 // Log content of kernel message. We try to skip the ], but if for
michael@0 1187 // some reason (most likely due to buffer overflow/wraparound), we
michael@0 1188 // can't find the ] then we just log the entire line.
michael@0 1189 char* endOfTimestamp = strchr(line_begin, ']');
michael@0 1190 if (endOfTimestamp && endOfTimestamp[1] == ' ') {
michael@0 1191 // skip the ] and the space that follows it
michael@0 1192 line_begin = endOfTimestamp + 2;
michael@0 1193 }
michael@0 1194 if (!lineTimestampFound) {
michael@0 1195 OOM_LOG(ANDROID_LOG_WARN, "following kill message may be a duplicate");
michael@0 1196 }
michael@0 1197 OOM_LOG(ANDROID_LOG_ERROR, "[Kill]: %s\n", line_begin);
michael@0 1198 break;
michael@0 1199 }
michael@0 1200 }
michael@0 1201 }
michael@0 1202
michael@0 1203 return NS_OK;
michael@0 1204 }
michael@0 1205
michael@0 1206 static void
michael@0 1207 EnsureKernelLowMemKillerParamsSet()
michael@0 1208 {
michael@0 1209 static bool kernelLowMemKillerParamsSet;
michael@0 1210 if (kernelLowMemKillerParamsSet) {
michael@0 1211 return;
michael@0 1212 }
michael@0 1213 kernelLowMemKillerParamsSet = true;
michael@0 1214
michael@0 1215 HAL_LOG(("Setting kernel's low-mem killer parameters."));
michael@0 1216
michael@0 1217 // Set /sys/module/lowmemorykiller/parameters/{adj,minfree,notify_trigger}
michael@0 1218 // according to our prefs. These files let us tune when the kernel kills
michael@0 1219 // processes when we're low on memory, and when it notifies us that we're
michael@0 1220 // running low on available memory.
michael@0 1221 //
michael@0 1222 // adj and minfree are both comma-separated lists of integers. If adj="A,B"
michael@0 1223 // and minfree="X,Y", then the kernel will kill processes with oom_adj
michael@0 1224 // A or higher once we have fewer than X pages of memory free, and will kill
michael@0 1225 // processes with oom_adj B or higher once we have fewer than Y pages of
michael@0 1226 // memory free.
michael@0 1227 //
michael@0 1228 // notify_trigger is a single integer. If we set notify_trigger=Z, then
michael@0 1229 // we'll get notified when there are fewer than Z pages of memory free. (See
michael@0 1230 // GonkMemoryPressureMonitoring.cpp.)
michael@0 1231
michael@0 1232 // Build the adj and minfree strings.
michael@0 1233 nsAutoCString adjParams;
michael@0 1234 nsAutoCString minfreeParams;
michael@0 1235
michael@0 1236 int32_t lowerBoundOfNextOomScoreAdj = OOM_SCORE_ADJ_MIN - 1;
michael@0 1237 int32_t lowerBoundOfNextKillUnderKB = 0;
michael@0 1238 int32_t countOfLowmemorykillerParametersSets = 0;
michael@0 1239
michael@0 1240 for (int i = NUM_PROCESS_PRIORITY - 1; i >= 0; i--) {
michael@0 1241 // The system doesn't function correctly if we're missing these prefs, so
michael@0 1242 // crash loudly.
michael@0 1243
michael@0 1244 ProcessPriority priority = static_cast<ProcessPriority>(i);
michael@0 1245
michael@0 1246 int32_t oomScoreAdj;
michael@0 1247 if (!NS_SUCCEEDED(Preferences::GetInt(
michael@0 1248 nsPrintfCString("hal.processPriorityManager.gonk.%s.OomScoreAdjust",
michael@0 1249 ProcessPriorityToString(priority)).get(),
michael@0 1250 &oomScoreAdj))) {
michael@0 1251 MOZ_CRASH();
michael@0 1252 }
michael@0 1253
michael@0 1254 int32_t killUnderKB;
michael@0 1255 if (!NS_SUCCEEDED(Preferences::GetInt(
michael@0 1256 nsPrintfCString("hal.processPriorityManager.gonk.%s.KillUnderKB",
michael@0 1257 ProcessPriorityToString(priority)).get(),
michael@0 1258 &killUnderKB))) {
michael@0 1259 // ProcessPriority values like PROCESS_PRIORITY_FOREGROUND_KEYBOARD,
michael@0 1260 // which has only OomScoreAdjust but lacks KillUnderMB value, will not
michael@0 1261 // create new LMK parameters.
michael@0 1262 continue;
michael@0 1263 }
michael@0 1264
michael@0 1265 // The LMK in kernel silently malfunctions if we assign the parameters
michael@0 1266 // in non-increasing order, so we add this assertion here. See bug 887192.
michael@0 1267 MOZ_ASSERT(oomScoreAdj > lowerBoundOfNextOomScoreAdj);
michael@0 1268 MOZ_ASSERT(killUnderKB > lowerBoundOfNextKillUnderKB);
michael@0 1269
michael@0 1270 // The LMK in kernel only accept 6 sets of LMK parameters. See bug 914728.
michael@0 1271 MOZ_ASSERT(countOfLowmemorykillerParametersSets < 6);
michael@0 1272
michael@0 1273 // adj is in oom_adj units.
michael@0 1274 adjParams.AppendPrintf("%d,", OomAdjOfOomScoreAdj(oomScoreAdj));
michael@0 1275
michael@0 1276 // minfree is in pages.
michael@0 1277 minfreeParams.AppendPrintf("%d,", killUnderKB * 1024 / PAGE_SIZE);
michael@0 1278
michael@0 1279 lowerBoundOfNextOomScoreAdj = oomScoreAdj;
michael@0 1280 lowerBoundOfNextKillUnderKB = killUnderKB;
michael@0 1281 countOfLowmemorykillerParametersSets++;
michael@0 1282 }
michael@0 1283
michael@0 1284 // Strip off trailing commas.
michael@0 1285 adjParams.Cut(adjParams.Length() - 1, 1);
michael@0 1286 minfreeParams.Cut(minfreeParams.Length() - 1, 1);
michael@0 1287 if (!adjParams.IsEmpty() && !minfreeParams.IsEmpty()) {
michael@0 1288 WriteToFile("/sys/module/lowmemorykiller/parameters/adj", adjParams.get());
michael@0 1289 WriteToFile("/sys/module/lowmemorykiller/parameters/minfree", minfreeParams.get());
michael@0 1290 }
michael@0 1291
michael@0 1292 // Set the low-memory-notification threshold.
michael@0 1293 int32_t lowMemNotifyThresholdKB;
michael@0 1294 if (NS_SUCCEEDED(Preferences::GetInt(
michael@0 1295 "hal.processPriorityManager.gonk.notifyLowMemUnderKB",
michael@0 1296 &lowMemNotifyThresholdKB))) {
michael@0 1297
michael@0 1298 // notify_trigger is in pages.
michael@0 1299 WriteToFile("/sys/module/lowmemorykiller/parameters/notify_trigger",
michael@0 1300 nsPrintfCString("%d", lowMemNotifyThresholdKB * 1024 / PAGE_SIZE).get());
michael@0 1301 }
michael@0 1302
michael@0 1303 // Ensure OOM events appear in logcat
michael@0 1304 nsRefPtr<OomVictimLogger> oomLogger = new OomVictimLogger();
michael@0 1305 nsCOMPtr<nsIObserverService> os = services::GetObserverService();
michael@0 1306 if (os) {
michael@0 1307 os->AddObserver(oomLogger, "ipc:content-shutdown", false);
michael@0 1308 }
michael@0 1309 }
michael@0 1310
michael@0 1311 static void
michael@0 1312 SetNiceForPid(int aPid, int aNice)
michael@0 1313 {
michael@0 1314 errno = 0;
michael@0 1315 int origProcPriority = getpriority(PRIO_PROCESS, aPid);
michael@0 1316 if (errno) {
michael@0 1317 LOG("Unable to get nice for pid=%d; error %d. SetNiceForPid bailing.",
michael@0 1318 aPid, errno);
michael@0 1319 return;
michael@0 1320 }
michael@0 1321
michael@0 1322 int rv = setpriority(PRIO_PROCESS, aPid, aNice);
michael@0 1323 if (rv) {
michael@0 1324 LOG("Unable to set nice for pid=%d; error %d. SetNiceForPid bailing.",
michael@0 1325 aPid, errno);
michael@0 1326 return;
michael@0 1327 }
michael@0 1328
michael@0 1329 // On Linux, setpriority(aPid) modifies the priority only of the main
michael@0 1330 // thread of that process. We have to modify the priorities of all of the
michael@0 1331 // process's threads as well, so iterate over all the threads and increase
michael@0 1332 // each of their priorites by aNice - origProcPriority (and also ensure that
michael@0 1333 // none of the tasks has a lower priority than the main thread).
michael@0 1334 //
michael@0 1335 // This is horribly racy.
michael@0 1336
michael@0 1337 DIR* tasksDir = opendir(nsPrintfCString("/proc/%d/task/", aPid).get());
michael@0 1338 if (!tasksDir) {
michael@0 1339 LOG("Unable to open /proc/%d/task. SetNiceForPid bailing.", aPid);
michael@0 1340 return;
michael@0 1341 }
michael@0 1342
michael@0 1343 // Be careful not to leak tasksDir; after this point, we must call closedir().
michael@0 1344
michael@0 1345 while (struct dirent* de = readdir(tasksDir)) {
michael@0 1346 char* endptr = nullptr;
michael@0 1347 long tidlong = strtol(de->d_name, &endptr, /* base */ 10);
michael@0 1348 if (*endptr || tidlong < 0 || tidlong > INT32_MAX || tidlong == aPid) {
michael@0 1349 // if dp->d_name was not an integer, was negative (?!) or too large, or
michael@0 1350 // was the same as aPid, we're not interested.
michael@0 1351 //
michael@0 1352 // (The |tidlong == aPid| check is very important; without it, we'll
michael@0 1353 // renice aPid twice, and the second renice will be relative to the
michael@0 1354 // priority set by the first renice.)
michael@0 1355 continue;
michael@0 1356 }
michael@0 1357
michael@0 1358 int tid = static_cast<int>(tidlong);
michael@0 1359
michael@0 1360 errno = 0;
michael@0 1361 // Get and set the task's new priority.
michael@0 1362 int origtaskpriority = getpriority(PRIO_PROCESS, tid);
michael@0 1363 if (errno) {
michael@0 1364 LOG("Unable to get nice for tid=%d (pid=%d); error %d. This isn't "
michael@0 1365 "necessarily a problem; it could be a benign race condition.",
michael@0 1366 tid, aPid, errno);
michael@0 1367 continue;
michael@0 1368 }
michael@0 1369
michael@0 1370 int newtaskpriority =
michael@0 1371 std::max(origtaskpriority - origProcPriority + aNice, aNice);
michael@0 1372 rv = setpriority(PRIO_PROCESS, tid, newtaskpriority);
michael@0 1373
michael@0 1374 if (rv) {
michael@0 1375 LOG("Unable to set nice for tid=%d (pid=%d); error %d. This isn't "
michael@0 1376 "necessarily a problem; it could be a benign race condition.",
michael@0 1377 tid, aPid, errno);
michael@0 1378 continue;
michael@0 1379 }
michael@0 1380 }
michael@0 1381
michael@0 1382 LOG("Changed nice for pid %d from %d to %d.",
michael@0 1383 aPid, origProcPriority, aNice);
michael@0 1384
michael@0 1385 closedir(tasksDir);
michael@0 1386 }
michael@0 1387
michael@0 1388 void
michael@0 1389 SetProcessPriority(int aPid,
michael@0 1390 ProcessPriority aPriority,
michael@0 1391 ProcessCPUPriority aCPUPriority,
michael@0 1392 uint32_t aBackgroundLRU)
michael@0 1393 {
michael@0 1394 HAL_LOG(("SetProcessPriority(pid=%d, priority=%d, cpuPriority=%d, LRU=%u)",
michael@0 1395 aPid, aPriority, aCPUPriority, aBackgroundLRU));
michael@0 1396
michael@0 1397 // If this is the first time SetProcessPriority was called, set the kernel's
michael@0 1398 // OOM parameters according to our prefs.
michael@0 1399 //
michael@0 1400 // We could/should do this on startup instead of waiting for the first
michael@0 1401 // SetProcessPriorityCall. But in practice, the master process needs to set
michael@0 1402 // its priority early in the game, so we can reasonably rely on
michael@0 1403 // SetProcessPriority being called early in startup.
michael@0 1404 EnsureKernelLowMemKillerParamsSet();
michael@0 1405
michael@0 1406 int32_t oomScoreAdj = 0;
michael@0 1407 nsresult rv = Preferences::GetInt(nsPrintfCString(
michael@0 1408 "hal.processPriorityManager.gonk.%s.OomScoreAdjust",
michael@0 1409 ProcessPriorityToString(aPriority)).get(), &oomScoreAdj);
michael@0 1410
michael@0 1411 RoundOomScoreAdjUpWithBackroundLRU(oomScoreAdj, aBackgroundLRU);
michael@0 1412
michael@0 1413 if (NS_SUCCEEDED(rv)) {
michael@0 1414 int clampedOomScoreAdj = clamped<int>(oomScoreAdj, OOM_SCORE_ADJ_MIN,
michael@0 1415 OOM_SCORE_ADJ_MAX);
michael@0 1416 if(clampedOomScoreAdj != oomScoreAdj) {
michael@0 1417 HAL_LOG(("Clamping OOM adjustment for pid %d to %d",
michael@0 1418 aPid, clampedOomScoreAdj));
michael@0 1419 } else {
michael@0 1420 HAL_LOG(("Setting OOM adjustment for pid %d to %d",
michael@0 1421 aPid, clampedOomScoreAdj));
michael@0 1422 }
michael@0 1423
michael@0 1424 // We try the newer interface first, and fall back to the older interface
michael@0 1425 // on failure.
michael@0 1426
michael@0 1427 if (!WriteToFile(nsPrintfCString("/proc/%d/oom_score_adj", aPid).get(),
michael@0 1428 nsPrintfCString("%d", clampedOomScoreAdj).get()))
michael@0 1429 {
michael@0 1430 int oomAdj = OomAdjOfOomScoreAdj(clampedOomScoreAdj);
michael@0 1431
michael@0 1432 WriteToFile(nsPrintfCString("/proc/%d/oom_adj", aPid).get(),
michael@0 1433 nsPrintfCString("%d", oomAdj).get());
michael@0 1434 }
michael@0 1435 } else {
michael@0 1436 LOG("Unable to read oom_score_adj pref for priority %s; "
michael@0 1437 "are the prefs messed up?",
michael@0 1438 ProcessPriorityToString(aPriority));
michael@0 1439 MOZ_ASSERT(false);
michael@0 1440 }
michael@0 1441
michael@0 1442 int32_t nice = 0;
michael@0 1443
michael@0 1444 if (aCPUPriority == PROCESS_CPU_PRIORITY_NORMAL) {
michael@0 1445 rv = Preferences::GetInt(
michael@0 1446 nsPrintfCString("hal.processPriorityManager.gonk.%s.Nice",
michael@0 1447 ProcessPriorityToString(aPriority)).get(),
michael@0 1448 &nice);
michael@0 1449 } else if (aCPUPriority == PROCESS_CPU_PRIORITY_LOW) {
michael@0 1450 rv = Preferences::GetInt("hal.processPriorityManager.gonk.LowCPUNice",
michael@0 1451 &nice);
michael@0 1452 } else {
michael@0 1453 LOG("Unable to read niceness pref for priority %s; "
michael@0 1454 "are the prefs messed up?",
michael@0 1455 ProcessPriorityToString(aPriority));
michael@0 1456 MOZ_ASSERT(false);
michael@0 1457 rv = NS_ERROR_FAILURE;
michael@0 1458 }
michael@0 1459
michael@0 1460 if (NS_SUCCEEDED(rv)) {
michael@0 1461 LOG("Setting nice for pid %d to %d", aPid, nice);
michael@0 1462 SetNiceForPid(aPid, nice);
michael@0 1463 }
michael@0 1464 }
michael@0 1465
michael@0 1466 void
michael@0 1467 FactoryReset()
michael@0 1468 {
michael@0 1469 nsCOMPtr<nsIRecoveryService> recoveryService =
michael@0 1470 do_GetService("@mozilla.org/recovery-service;1");
michael@0 1471 if (!recoveryService) {
michael@0 1472 NS_WARNING("Could not get recovery service!");
michael@0 1473 return;
michael@0 1474 }
michael@0 1475
michael@0 1476 recoveryService->FactoryReset();
michael@0 1477 }
michael@0 1478
michael@0 1479 } // hal_impl
michael@0 1480 } // mozilla

mercurial