/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set sw=2 ts=8 et ft=cpp : */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at: * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Code. * * The Initial Developer of the Original Code is * The Mozilla Foundation * Portions created by the Initial Developer are Copyright (C) 2011 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Chris Jones * Michael Wu * Justin Lebar * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "hardware_legacy/uevent.h" #include "Hal.h" #include "mozilla/dom/battery/Constants.h" #include "mozilla/FileUtils.h" #include "nsAlgorithm.h" #include "nsThreadUtils.h" #include "mozilla/Monitor.h" #include "mozilla/Services.h" #include "mozilla/FileUtils.h" #include "nsThreadUtils.h" #include "nsIRunnable.h" #include "nsIThread.h" #include "nsIObserver.h" #include "nsIObserverService.h" #include "hardware_legacy/vibrator.h" #include #include #include #include using mozilla::hal::WindowIdentifier; namespace mozilla { namespace hal_impl { namespace { /** * This runnable runs for the lifetime of the program, once started. It's * responsible for "playing" vibration patterns. */ class VibratorRunnable : public nsIRunnable , public nsIObserver { public: VibratorRunnable() : mMonitor("VibratorRunnable") , mIndex(0) , mShuttingDown(false) { nsCOMPtr os = services::GetObserverService(); if (!os) { NS_WARNING("Could not get observer service!"); return; } os->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, /* weak ref */ true); } NS_DECL_ISUPPORTS NS_DECL_NSIRUNNABLE NS_DECL_NSIOBSERVER // Run on the main thread, not the vibrator thread. void Vibrate(const nsTArray &pattern); void CancelVibrate(); private: Monitor mMonitor; // The currently-playing pattern. nsTArray mPattern; // The index we're at in the currently-playing pattern. If mIndex >= // mPattern.Length(), then we're not currently playing anything. uint32 mIndex; // Set to true in our shutdown observer. When this is true, we kill the // vibrator thread. bool mShuttingDown; }; NS_IMPL_ISUPPORTS2(VibratorRunnable, nsIRunnable, nsIObserver); NS_IMETHODIMP VibratorRunnable::Run() { MonitorAutoLock lock(mMonitor); // We currently assume that mMonitor.Wait(X) waits for X milliseconds. But in // reality, the kernel might not switch to this thread for some time after the // wait expires. So there's potential for some inaccuracy here. // // This doesn't worry me too much. Note that we don't even start vibrating // immediately when VibratorRunnable::Vibrate is called -- we go through a // condvar onto another thread. Better just to be chill about small errors in // the timing here. while (!mShuttingDown) { if (mIndex < mPattern.Length()) { uint32 duration = mPattern[mIndex]; if (mIndex % 2 == 0) { vibrator_on(duration); } mIndex++; mMonitor.Wait(PR_MillisecondsToInterval(duration)); } else { mMonitor.Wait(); } } return NS_OK; } NS_IMETHODIMP VibratorRunnable::Observe(nsISupports *subject, const char *topic, const PRUnichar *data) { MOZ_ASSERT(strcmp(topic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0); MonitorAutoLock lock(mMonitor); mShuttingDown = true; mMonitor.Notify(); return NS_OK; } void VibratorRunnable::Vibrate(const nsTArray &pattern) { MonitorAutoLock lock(mMonitor); mPattern = pattern; mIndex = 0; mMonitor.Notify(); } void VibratorRunnable::CancelVibrate() { MonitorAutoLock lock(mMonitor); mPattern.Clear(); mPattern.AppendElement(0); mIndex = 0; mMonitor.Notify(); } VibratorRunnable *sVibratorRunnable = NULL; void EnsureVibratorThreadInitialized() { if (sVibratorRunnable) { return; } nsRefPtr runnable = new VibratorRunnable(); sVibratorRunnable = runnable; nsCOMPtr thread; NS_NewThread(getter_AddRefs(thread), sVibratorRunnable); } } // anonymous namespace void Vibrate(const nsTArray &pattern, const hal::WindowIdentifier &) { EnsureVibratorThreadInitialized(); sVibratorRunnable->Vibrate(pattern); } void CancelVibrate(const hal::WindowIdentifier &) { EnsureVibratorThreadInitialized(); sVibratorRunnable->CancelVibrate(); } namespace { class BatteryUpdater : public nsRunnable { public: NS_IMETHOD Run() { hal::BatteryInformation info; hal_impl::GetCurrentBatteryInformation(&info); hal::NotifyBatteryChange(info); return NS_OK; } }; class UEventWatcher : public nsRunnable { public: UEventWatcher() : mUpdater(new BatteryUpdater()) , mRunning(false) { } NS_IMETHOD Run() { while (mRunning) { char buf[1024]; int count = uevent_next_event(buf, sizeof(buf) - 1); if (!count) { NS_WARNING("uevent_next_event() returned 0!"); continue; } buf[sizeof(buf) - 1] = 0; if (strstr(buf, "battery")) NS_DispatchToMainThread(mUpdater); } return NS_OK; } bool mRunning; private: nsRefPtr mUpdater; }; } // anonymous namespace static bool sUEventInitialized = false; static UEventWatcher *sWatcher = NULL; static nsIThread *sWatcherThread = NULL; void EnableBatteryNotifications() { if (!sUEventInitialized) sUEventInitialized = uevent_init(); if (!sUEventInitialized) { NS_WARNING("uevent_init() failed!"); return; } if (!sWatcher) sWatcher = new UEventWatcher(); NS_ADDREF(sWatcher); sWatcher->mRunning = true; nsresult rv = NS_NewThread(&sWatcherThread, sWatcher); if (NS_FAILED(rv)) NS_WARNING("Failed to get new thread for uevent watching"); } void DisableBatteryNotifications() { sWatcher->mRunning = false; sWatcherThread->Shutdown(); NS_IF_RELEASE(sWatcherThread); delete sWatcher; } void GetCurrentBatteryInformation(hal::BatteryInformation *aBatteryInfo) { FILE *capacityFile = fopen("/sys/class/power_supply/battery/capacity", "r"); double capacity = dom::battery::kDefaultLevel * 100; if (capacityFile) { fscanf(capacityFile, "%lf", &capacity); fclose(capacityFile); } FILE *chargingFile = fopen("/sys/class/power_supply/battery/charging_source", "r"); int chargingSrc = 1; if (chargingFile) { fscanf(chargingFile, "%d", &chargingSrc); fclose(chargingFile); } aBatteryInfo->level() = capacity / 100; aBatteryInfo->charging() = chargingSrc == 1; aBatteryInfo->remainingTime() = dom::battery::kUnknownRemainingTime; } namespace { /** * RAII class to help us remember to close file descriptors. */ const char *screenEnabledFilename = "/sys/power/state"; const char *screenBrightnessFilename = "/sys/class/backlight/pwm-backlight/brightness"; template bool ReadFromFile(const char *filename, char (&buf)[n]) { int fd = open(filename, O_RDONLY); ScopedClose autoClose(fd); if (fd < 0) { HAL_LOG(("Unable to open file %s.", filename)); return false; } ssize_t numRead = read(fd, buf, n); if (numRead < 0) { HAL_LOG(("Error reading from file %s.", filename)); return false; } buf[PR_MIN(numRead, n - 1)] = '\0'; return true; } void WriteToFile(const char *filename, const char *toWrite) { int fd = open(filename, O_WRONLY); ScopedClose autoClose(fd); if (fd < 0) { HAL_LOG(("Unable to open file %s.", filename)); return; } if (write(fd, toWrite, strlen(toWrite)) < 0) { HAL_LOG(("Unable to write to file %s.", filename)); return; } } // We can write to screenEnabledFilename to enable/disable the screen, but when // we read, we always get "mem"! So we have to keep track ourselves whether // the screen is on or not. bool sScreenEnabled = true; } // anonymous namespace bool GetScreenEnabled() { return sScreenEnabled; } void SetScreenEnabled(bool enabled) { WriteToFile(screenEnabledFilename, enabled ? "on" : "mem"); sScreenEnabled = enabled; } double GetScreenBrightness() { char buf[32]; ReadFromFile(screenBrightnessFilename, buf); errno = 0; unsigned long val = strtoul(buf, NULL, 10); if (errno) { HAL_LOG(("Cannot parse contents of %s; expected an unsigned " "int, but contains \"%s\".", screenBrightnessFilename, buf)); return 1; } if (val > 255) { HAL_LOG(("Got out-of-range brightness %d, truncating to 1.0", val)); val = 255; } return val / 255.0; } void SetScreenBrightness(double brightness) { // Don't use De Morgan's law to push the ! into this expression; we want to // catch NaN too. if (!(0 <= brightness && brightness <= 1)) { HAL_LOG(("SetScreenBrightness: Dropping illegal brightness %f.", brightness)); return; } // Convert the value in [0, 1] to an int between 0 and 255, then write to a // string. int val = static_cast(round(brightness * 255)); char str[4]; DebugOnly numChars = snprintf(str, sizeof(str), "%d", val); MOZ_ASSERT(numChars < static_cast(sizeof(str))); WriteToFile(screenBrightnessFilename, str); } } // hal_impl } // mozilla