LCOV - code coverage report
Current view: directory - hal/linux - UPowerClient.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 119 0 0.0 %
Date: 2012-06-02 Functions: 20 0 0.0 %

       1                 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2                 : /* ***** BEGIN LICENSE BLOCK *****
       3                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       4                 :  *
       5                 :  * The contents of this file are subject to the Mozilla Public License Version
       6                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       7                 :  * the License. You may obtain a copy of the License at
       8                 :  * http://www.mozilla.org/MPL/
       9                 :  *
      10                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      11                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      12                 :  * for the specific language governing rights and limitations under the
      13                 :  * License.
      14                 :  *
      15                 :  * The Original Code is mozilla.org code.
      16                 :  *
      17                 :  * The Initial Developer of the Original Code is Mozilla Foundation
      18                 :  * Portions created by the Initial Developer are Copyright (C) 2011
      19                 :  * the Initial Developer. All Rights Reserved.
      20                 :  *
      21                 :  * Contributor(s):
      22                 :  *   Mounir Lamouri <mounir.lamouri@mozilla.com> (Original Author)
      23                 :  *
      24                 :  * Alternatively, the contents of this file may be used under the terms of
      25                 :  * either of the GNU General Public License Version 2 or later (the "GPL"),
      26                 :  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      27                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      28                 :  * of those above. If you wish to allow use of your version of this file only
      29                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      30                 :  * use your version of this file under the terms of the MPL, indicate your
      31                 :  * decision by deleting the provisions above and replace them with the notice
      32                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      33                 :  * the provisions above, a recipient may use your version of this file under
      34                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      35                 :  *
      36                 :  * ***** END LICENSE BLOCK ***** */
      37                 : 
      38                 : #include <mozilla/Hal.h>
      39                 : #include <dbus/dbus-glib.h>
      40                 : #include <dbus/dbus-glib-lowlevel.h>
      41                 : #include <mozilla/dom/battery/Constants.h>
      42                 : #include "nsAutoRef.h"
      43                 : 
      44                 : /*
      45                 :  * Helper that manages the destruction of glib objects as soon as they leave
      46                 :  * the current scope.
      47                 :  *
      48                 :  * We are specializing nsAutoRef class.
      49                 :  */
      50                 : 
      51                 : template <>
      52                 : class nsAutoRefTraits<DBusGProxy> : public nsPointerRefTraits<DBusGProxy>
      53               0 : {
      54                 : public:
      55               0 :   static void Release(DBusGProxy* ptr) { g_object_unref(ptr); }
      56                 : };
      57                 : 
      58                 : template <>
      59                 : class nsAutoRefTraits<GHashTable> : public nsPointerRefTraits<GHashTable>
      60               0 : {
      61                 : public:
      62               0 :   static void Release(GHashTable* ptr) { g_hash_table_unref(ptr); }
      63                 : };
      64                 : 
      65                 : using namespace mozilla::dom::battery;
      66                 : 
      67                 : namespace mozilla {
      68                 : namespace hal_impl {
      69                 : 
      70                 : /**
      71                 :  * This is the declaration of UPowerClient class. This class is listening and
      72                 :  * communicating to upower daemon through DBus.
      73                 :  * There is no header file because this class shouldn't be public.
      74                 :  */
      75                 : class UPowerClient
      76                 : {
      77                 : public:
      78                 :   static UPowerClient* GetInstance();
      79                 : 
      80                 :   void BeginListening();
      81                 :   void StopListening();
      82                 : 
      83                 :   double GetLevel();
      84                 :   bool   IsCharging();
      85                 :   double GetRemainingTime();
      86                 : 
      87                 :   ~UPowerClient();
      88                 : 
      89                 : private:
      90                 :   UPowerClient();
      91                 : 
      92                 :   enum States {
      93                 :     eState_Unknown = 0,
      94                 :     eState_Charging,
      95                 :     eState_Discharging,
      96                 :     eState_Empty,
      97                 :     eState_FullyCharged,
      98                 :     eState_PendingCharge,
      99                 :     eState_PendingDischarge
     100                 :   };
     101                 : 
     102                 :   /**
     103                 :    * Update the currently tracked device.
     104                 :    * @return whether everything went ok.
     105                 :    */
     106                 :   void UpdateTrackedDevice();
     107                 : 
     108                 :   /**
     109                 :    * Returns a hash table with the properties of aDevice.
     110                 :    * Note: the caller has to unref the hash table.
     111                 :    */
     112                 :   GHashTable* GetDeviceProperties(const gchar* aDevice);
     113                 : 
     114                 :   /**
     115                 :    * Using the device properties (aHashTable), this method updates the member
     116                 :    * variable storing the values we care about.
     117                 :    */
     118                 :   void UpdateSavedInfo(GHashTable* aHashTable);
     119                 : 
     120                 :   /**
     121                 :    * Callback used by 'DeviceChanged' signal.
     122                 :    */
     123                 :   static void DeviceChanged(DBusGProxy* aProxy, const gchar* aObjectPath,
     124                 :                             UPowerClient* aListener);
     125                 : 
     126                 :   /**
     127                 :    * Callback called when mDBusConnection gets a signal.
     128                 :    */
     129                 :   static DBusHandlerResult ConnectionSignalFilter(DBusConnection* aConnection,
     130                 :                                                   DBusMessage* aMessage,
     131                 :                                                   void* aData);
     132                 : 
     133                 :   // The DBus connection object.
     134                 :   DBusGConnection* mDBusConnection;
     135                 : 
     136                 :   // The DBus proxy object to upower.
     137                 :   DBusGProxy* mUPowerProxy;
     138                 : 
     139                 :   // The path of the tracked device.
     140                 :   gchar* mTrackedDevice;
     141                 : 
     142                 :   double mLevel;
     143                 :   bool mCharging;
     144                 :   double mRemainingTime;
     145                 : 
     146                 :   static UPowerClient* sInstance;
     147                 : 
     148                 :   static const guint sDeviceTypeBattery = 2;
     149                 :   static const guint64 kUPowerUnknownRemainingTime = 0;
     150                 : };
     151                 : 
     152                 : /*
     153                 :  * Implementation of mozilla::hal_impl::EnableBatteryNotifications,
     154                 :  *                   mozilla::hal_impl::DisableBatteryNotifications,
     155                 :  *               and mozilla::hal_impl::GetCurrentBatteryInformation.
     156                 :  */
     157                 : 
     158                 : void
     159               0 : EnableBatteryNotifications()
     160                 : {
     161               0 :   UPowerClient::GetInstance()->BeginListening();
     162               0 : }
     163                 : 
     164                 : void
     165               0 : DisableBatteryNotifications()
     166                 : {
     167               0 :   UPowerClient::GetInstance()->StopListening();
     168               0 : }
     169                 : 
     170                 : void
     171               0 : GetCurrentBatteryInformation(hal::BatteryInformation* aBatteryInfo)
     172                 : {
     173               0 :   UPowerClient* upowerClient = UPowerClient::GetInstance();
     174                 : 
     175               0 :   aBatteryInfo->level() = upowerClient->GetLevel();
     176               0 :   aBatteryInfo->charging() = upowerClient->IsCharging();
     177               0 :   aBatteryInfo->remainingTime() = upowerClient->GetRemainingTime();
     178               0 : }
     179                 : 
     180                 : /*
     181                 :  * Following is the implementation of UPowerClient.
     182                 :  */
     183                 : 
     184                 : UPowerClient* UPowerClient::sInstance = nsnull;
     185                 : 
     186                 : /* static */ UPowerClient*
     187               0 : UPowerClient::GetInstance()
     188                 : {
     189               0 :   if (!sInstance) {
     190               0 :     sInstance = new UPowerClient();
     191                 :   }
     192                 : 
     193               0 :   return sInstance;
     194                 : }
     195                 : 
     196               0 : UPowerClient::UPowerClient()
     197                 :   : mDBusConnection(nsnull)
     198                 :   , mUPowerProxy(nsnull)
     199                 :   , mTrackedDevice(nsnull)
     200                 :   , mLevel(kDefaultLevel)
     201                 :   , mCharging(kDefaultCharging)
     202               0 :   , mRemainingTime(kDefaultRemainingTime)
     203                 : {
     204               0 : }
     205                 : 
     206               0 : UPowerClient::~UPowerClient()
     207                 : {
     208               0 :   NS_ASSERTION(!mDBusConnection && !mUPowerProxy && !mTrackedDevice,
     209                 :                "The observers have not been correctly removed! "
     210                 :                "(StopListening should have been called)");
     211               0 : }
     212                 : 
     213                 : void
     214               0 : UPowerClient::BeginListening()
     215                 : {
     216               0 :   GError* error = nsnull;
     217               0 :   mDBusConnection = dbus_g_bus_get(DBUS_BUS_SYSTEM, &error);
     218                 : 
     219               0 :   if (!mDBusConnection) {
     220               0 :     g_printerr("Failed to open connection to bus: %s\n", error->message);
     221               0 :     g_error_free(error);
     222               0 :     return;
     223                 :   }
     224                 : 
     225                 :   DBusConnection* dbusConnection =
     226               0 :     dbus_g_connection_get_connection(mDBusConnection);
     227                 : 
     228                 :   // Make sure we do not exit the entire program if DBus connection get lost.
     229               0 :   dbus_connection_set_exit_on_disconnect(dbusConnection, false);
     230                 : 
     231                 :   // Listening to signals the DBus connection is going to get so we will know
     232                 :   // when it is lost and we will be able to disconnect cleanly.
     233                 :   dbus_connection_add_filter(dbusConnection, ConnectionSignalFilter, this,
     234               0 :                              nsnull);
     235                 : 
     236                 :   mUPowerProxy = dbus_g_proxy_new_for_name(mDBusConnection,
     237                 :                                            "org.freedesktop.UPower",
     238                 :                                            "/org/freedesktop/UPower",
     239               0 :                                            "org.freedesktop.UPower");
     240                 : 
     241               0 :   UpdateTrackedDevice();
     242                 : 
     243                 :   /*
     244                 :    * TODO: we should probably listen to DeviceAdded and DeviceRemoved signals.
     245                 :    * If we do that, we would have to disconnect from those in StopListening.
     246                 :    * It's not yet implemented because it requires testing hot plugging and
     247                 :    * removal of a battery.
     248                 :    */
     249                 :   dbus_g_proxy_add_signal(mUPowerProxy, "DeviceChanged", G_TYPE_STRING,
     250               0 :                           G_TYPE_INVALID);
     251                 :   dbus_g_proxy_connect_signal(mUPowerProxy, "DeviceChanged",
     252               0 :                               G_CALLBACK (DeviceChanged), this, nsnull);
     253                 : }
     254                 : 
     255                 : void
     256               0 : UPowerClient::StopListening()
     257                 : {
     258                 :   // If mDBusConnection isn't initialized, that means we are not really listening.
     259               0 :   if (!mDBusConnection) {
     260               0 :     return;
     261                 :   }
     262                 : 
     263                 :   dbus_connection_remove_filter(
     264                 :       dbus_g_connection_get_connection(mDBusConnection),
     265               0 :       ConnectionSignalFilter, this);
     266                 : 
     267                 :   dbus_g_proxy_disconnect_signal(mUPowerProxy, "DeviceChanged",
     268               0 :                                  G_CALLBACK (DeviceChanged), this);
     269                 : 
     270               0 :   g_free(mTrackedDevice);
     271               0 :   mTrackedDevice = nsnull;
     272                 : 
     273               0 :   g_object_unref(mUPowerProxy);
     274               0 :   mUPowerProxy = nsnull;
     275                 : 
     276               0 :   dbus_g_connection_unref(mDBusConnection);
     277               0 :   mDBusConnection = nsnull;
     278                 : 
     279                 :   // We should now show the default values, not the latest we got.
     280               0 :   mLevel = kDefaultLevel;
     281               0 :   mCharging = kDefaultCharging;
     282               0 :   mRemainingTime = kDefaultRemainingTime;
     283                 : }
     284                 : 
     285                 : void
     286               0 : UPowerClient::UpdateTrackedDevice()
     287                 : {
     288                 :   GType typeGPtrArray = dbus_g_type_get_collection("GPtrArray",
     289               0 :                                                    DBUS_TYPE_G_OBJECT_PATH);
     290               0 :   GPtrArray* devices = nsnull;
     291               0 :   GError* error = nsnull;
     292                 : 
     293                 :   // If that fails, that likely means upower isn't installed.
     294               0 :   if (!dbus_g_proxy_call(mUPowerProxy, "EnumerateDevices", &error, G_TYPE_INVALID,
     295               0 :                          typeGPtrArray, &devices, G_TYPE_INVALID)) {
     296               0 :     g_printerr ("Error: %s\n", error->message);
     297                 : 
     298               0 :     mTrackedDevice = nsnull;
     299               0 :     g_error_free(error);
     300               0 :     return;
     301                 :   }
     302                 : 
     303                 :   /*
     304                 :    * We are looking for the first device that is a battery.
     305                 :    * TODO: we could try to combine more than one battery.
     306                 :    */
     307               0 :   for (guint i=0; i<devices->len; ++i) {
     308               0 :     gchar* devicePath = static_cast<gchar*>(g_ptr_array_index(devices, i));
     309               0 :     nsAutoRef<GHashTable> hashTable(GetDeviceProperties(devicePath));
     310                 : 
     311               0 :     if (g_value_get_uint(static_cast<const GValue*>(g_hash_table_lookup(hashTable, "Type"))) == sDeviceTypeBattery) {
     312               0 :       UpdateSavedInfo(hashTable);
     313               0 :       mTrackedDevice = devicePath;
     314                 :       break;
     315                 :     }
     316                 : 
     317               0 :     g_free(devicePath);
     318                 :   }
     319                 : 
     320                 : #if GLIB_MAJOR_VERSION >= 2 && GLIB_MINOR_VERSION >= 22
     321                 :     g_ptr_array_unref(devices);
     322                 : #else
     323               0 :     g_ptr_array_free(devices, true);
     324                 : #endif
     325                 : }
     326                 : 
     327                 : /* static */ void
     328               0 : UPowerClient::DeviceChanged(DBusGProxy* aProxy, const gchar* aObjectPath, UPowerClient* aListener)
     329                 : {
     330                 : #if GLIB_MAJOR_VERSION >= 2 && GLIB_MINOR_VERSION >= 16
     331                 :   if (g_strcmp0(aObjectPath, aListener->mTrackedDevice)) {
     332                 : #else
     333               0 :   if (g_ascii_strcasecmp(aObjectPath, aListener->mTrackedDevice)) {
     334                 : #endif
     335               0 :     return;
     336                 :   }
     337                 : 
     338               0 :   nsAutoRef<GHashTable> hashTable(aListener->GetDeviceProperties(aObjectPath));
     339               0 :   aListener->UpdateSavedInfo(hashTable);
     340                 : 
     341                 :   hal::NotifyBatteryChange(hal::BatteryInformation(aListener->mLevel,
     342                 :                                                    aListener->mCharging,
     343               0 :                                                    aListener->mRemainingTime));
     344                 : }
     345                 : 
     346                 : /* static */ DBusHandlerResult
     347               0 : UPowerClient::ConnectionSignalFilter(DBusConnection* aConnection,
     348                 :                                      DBusMessage* aMessage, void* aData)
     349                 : {
     350               0 :   if (dbus_message_is_signal(aMessage, DBUS_INTERFACE_LOCAL, "Disconnected")) {
     351               0 :     static_cast<UPowerClient*>(aData)->StopListening();
     352                 :     // We do not return DBUS_HANDLER_RESULT_HANDLED here because the connection
     353                 :     // might be shared and some other filters might want to do something.
     354                 :   }
     355                 : 
     356               0 :   return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
     357                 : }
     358                 : 
     359                 : GHashTable*
     360               0 : UPowerClient::GetDeviceProperties(const gchar* aDevice)
     361                 : {
     362                 :   nsAutoRef<DBusGProxy> proxy(dbus_g_proxy_new_for_name(mDBusConnection,
     363                 :                                                         "org.freedesktop.UPower",
     364                 :                                                         aDevice,
     365               0 :                                                         "org.freedesktop.DBus.Properties"));
     366                 : 
     367               0 :   GError* error = nsnull;
     368               0 :   GHashTable* hashTable = nsnull;
     369                 :   GType typeGHashTable = dbus_g_type_get_map("GHashTable", G_TYPE_STRING,
     370               0 :                                             G_TYPE_VALUE);
     371               0 :   if (!dbus_g_proxy_call(proxy, "GetAll", &error, G_TYPE_STRING,
     372                 :                          "org.freedesktop.UPower.Device", G_TYPE_INVALID,
     373               0 :                          typeGHashTable, &hashTable, G_TYPE_INVALID)) {
     374               0 :     g_printerr("Error: %s\n", error->message);
     375               0 :     g_error_free(error);
     376               0 :     return nsnull;
     377                 :   }
     378                 : 
     379               0 :   return hashTable;
     380                 : }
     381                 : 
     382                 : void
     383               0 : UPowerClient::UpdateSavedInfo(GHashTable* aHashTable)
     384                 : {
     385               0 :   bool isFull = false;
     386                 : 
     387                 :   /*
     388                 :    * State values are confusing...
     389                 :    * First of all, after looking at upower sources (0.9.13), it seems that
     390                 :    * PendingDischarge and PendingCharge are not used.
     391                 :    * In addition, FullyCharged and Empty states are not clear because we do not
     392                 :    * know if the battery is actually charging or not. Those values come directly
     393                 :    * from sysfs (in the Linux kernel) which have four states: "Empty", "Full",
     394                 :    * "Charging" and "Discharging". In sysfs, "Empty" and "Full" are also only
     395                 :    * related to the level, not to the charging state.
     396                 :    * In this code, we are going to assume that Full means charging and Empty
     397                 :    * means discharging because if that is not the case, the state should not
     398                 :    * last a long time (actually, it should disappear at the following update).
     399                 :    * It might be even very hard to see real cases where the state is Empty and
     400                 :    * the battery is charging or the state is Full and the battery is discharging
     401                 :    * given that plugging/unplugging the battery should have an impact on the
     402                 :    * level.
     403                 :    */
     404               0 :   switch (g_value_get_uint(static_cast<const GValue*>(g_hash_table_lookup(aHashTable, "State")))) {
     405                 :     case eState_Unknown:
     406               0 :       mCharging = kDefaultCharging;
     407               0 :       break;
     408                 :     case eState_FullyCharged:
     409               0 :       isFull = true;
     410                 :     case eState_Charging:
     411                 :     case eState_PendingCharge:
     412               0 :       mCharging = true;
     413               0 :       break;
     414                 :     case eState_Discharging:
     415                 :     case eState_Empty:
     416                 :     case eState_PendingDischarge:
     417               0 :       mCharging = false;
     418               0 :       break;
     419                 :   }
     420                 : 
     421                 :   /*
     422                 :    * The battery level might be very close to 100% (like 99.xxxx%) without
     423                 :    * increasing. It seems that upower sets the battery state as 'full' in that
     424                 :    * case so we should trust it and not even try to get the value.
     425                 :    */
     426               0 :   if (isFull) {
     427               0 :     mLevel = 1.0;
     428                 :   } else {
     429               0 :     mLevel = g_value_get_double(static_cast<const GValue*>(g_hash_table_lookup(aHashTable, "Percentage")))*0.01;
     430                 :   }
     431                 : 
     432               0 :   if (isFull) {
     433               0 :     mRemainingTime = 0;
     434                 :   } else {
     435               0 :     mRemainingTime = mCharging ? g_value_get_int64(static_cast<const GValue*>(g_hash_table_lookup(aHashTable, "TimeToFull")))
     436               0 :                                : g_value_get_int64(static_cast<const GValue*>(g_hash_table_lookup(aHashTable, "TimeToEmpty")));
     437                 : 
     438               0 :     if (mRemainingTime == kUPowerUnknownRemainingTime) {
     439               0 :       mRemainingTime = kUnknownRemainingTime;
     440                 :     }
     441                 :   }
     442               0 : }
     443                 : 
     444                 : double
     445               0 : UPowerClient::GetLevel()
     446                 : {
     447               0 :   return mLevel;
     448                 : }
     449                 : 
     450                 : bool
     451               0 : UPowerClient::IsCharging()
     452                 : {
     453               0 :   return mCharging;
     454                 : }
     455                 : 
     456                 : double
     457               0 : UPowerClient::GetRemainingTime()
     458                 : {
     459               0 :   return mRemainingTime;
     460                 : }
     461                 : 
     462                 : } // namespace hal_impl
     463                 : } // namespace mozilla

Generated by: LCOV version 1.7