summaryrefslogtreecommitdiff
path: root/hal/linux/LinuxPower.cpp
blob: 70ab4a3f6e01db63faf69bb6e34f7bee0fec1bd5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "Hal.h"
#include "HalLog.h"

#include <unistd.h>
#include <sys/reboot.h>
#include "nsIObserverService.h"
#include "mozilla/Services.h"
#include "MainThreadUtils.h"

#if defined(MOZ_WIDGET_GONK)
#include "cutils/android_reboot.h"
#include "cutils/properties.h"
#endif

namespace mozilla {
namespace hal_impl {

#if (defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 19)
static void
PowerCtl(const char* aValue, int aCmd)
{
  // this invokes init's powerctl builtin via /init.rc
  property_set("sys.powerctl", aValue);
  // device should reboot in few moments, but if it doesn't - call
  // android_reboot() to make sure that init isn't stuck somewhere
  sleep(10);
  HAL_LOG("Powerctl call takes too long, forcing %s.", aValue);
  android_reboot(aCmd, 0, nullptr);
}
#endif

void
Reboot()
{
  if (NS_IsMainThread()) {
    nsCOMPtr<nsIObserverService> obsServ = services::GetObserverService();
    if (obsServ) {
      obsServ->NotifyObservers(nullptr, "system-reboot", nullptr);
    }
  }

#if !defined(MOZ_WIDGET_GONK)
  sync();
  reboot(RB_AUTOBOOT);
#elif (ANDROID_VERSION < 19)
  android_reboot(ANDROID_RB_RESTART, 0, nullptr);
#else
  PowerCtl("reboot", ANDROID_RB_RESTART);
#endif
}

void
PowerOff()
{
  if (NS_IsMainThread()) {
    nsCOMPtr<nsIObserverService> obsServ = services::GetObserverService();
    if (obsServ) {
      obsServ->NotifyObservers(nullptr, "system-power-off", nullptr);
    }
  }

#if !defined(MOZ_WIDGET_GONK)
  sync();
  reboot(RB_POWER_OFF);
#elif (ANDROID_VERSION < 19)
  android_reboot(ANDROID_RB_POWEROFF, 0, nullptr);
#else
  PowerCtl("shutdown", ANDROID_RB_POWEROFF);
#endif
}

// Structure to specify how watchdog pthread is going to work.
typedef struct watchdogParam
{
  hal::ShutdownMode mode; // Specify how to shutdown the system.
  int32_t timeoutSecs;    // Specify the delayed seconds to shutdown the system.

  watchdogParam(hal::ShutdownMode aMode, int32_t aTimeoutSecs)
    : mode(aMode), timeoutSecs(aTimeoutSecs) {}
} watchdogParam_t;

// Function to complusively shut down the system with a given mode.
static void
QuitHard(hal::ShutdownMode aMode)
{
  switch (aMode)
  {
    case hal::eHalShutdownMode_PowerOff:
      PowerOff();
      break;
    case hal::eHalShutdownMode_Reboot:
      Reboot();
      break;
    case hal::eHalShutdownMode_Restart:
      // Don't let signal handlers affect forced shutdown.
      kill(0, SIGKILL);
      // If we can't SIGKILL our process group, something is badly
      // wrong.  Trying to deliver a catch-able signal to ourselves can
      // invoke signal handlers and might cause problems.  So try
      // _exit() and hope we go away.
      _exit(1);
      break;
    default:
      MOZ_CRASH();
  }
}

// Function to complusively shut down the system with a given mode when timeout.
static void*
ForceQuitWatchdog(void* aParamPtr)
{
  watchdogParam_t* paramPtr = reinterpret_cast<watchdogParam_t*>(aParamPtr);
  if (paramPtr->timeoutSecs > 0 && paramPtr->timeoutSecs <= 30) {
    // If we shut down normally before the timeout, this thread will
    // be harmlessly reaped by the OS.
    TimeStamp deadline =
      (TimeStamp::Now() + TimeDuration::FromSeconds(paramPtr->timeoutSecs));
    while (true) {
      TimeDuration remaining = (deadline - TimeStamp::Now());
      int sleepSeconds = int(remaining.ToSeconds());
      if (sleepSeconds <= 0) {
        break;
      }
      sleep(sleepSeconds);
    }
  }
  hal::ShutdownMode mode = paramPtr->mode;
  delete paramPtr;
  QuitHard(mode);
  return nullptr;
}

void
StartForceQuitWatchdog(hal::ShutdownMode aMode, int32_t aTimeoutSecs)
{
  // Force-quits are intepreted a little more ferociously on Gonk,
  // because while Gecko is in the process of shutting down, the user
  // can't call 911, for example.  And if we hang on shutdown, bad
  // things happen.  So, make sure that doesn't happen.
  if (aTimeoutSecs <= 0) {
    return;
  }

  // Use a raw pthread here to insulate ourselves from bugs in other
  // Gecko code that we're trying to protect!
  // 
  // Note that we let the watchdog in charge of releasing |paramPtr|
  // if the pthread is successfully created.
  watchdogParam_t* paramPtr = new watchdogParam_t(aMode, aTimeoutSecs);
  pthread_t watchdog;
  if (pthread_create(&watchdog, nullptr,
                     ForceQuitWatchdog,
                     reinterpret_cast<void*>(paramPtr))) {
    // Better safe than sorry.
    delete paramPtr;
    QuitHard(aMode);
  }
  // The watchdog thread is off and running now.
}

} // hal_impl
} // mozilla