1 /*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #define LOG_TAG "BatteryStatsService"
18 //#define LOG_NDEBUG 0
19
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <inttypes.h>
23 #include <semaphore.h>
24 #include <stddef.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <sys/stat.h>
28 #include <sys/types.h>
29 #include <unistd.h>
30 #include <climits>
31 #include <unordered_map>
32 #include <utility>
33
34 #include <android-base/thread_annotations.h>
35 #include <android/hardware/power/1.0/IPower.h>
36 #include <android/hardware/power/1.1/IPower.h>
37 #include <android/hardware/power/stats/1.0/IPowerStats.h>
38 #include <android/system/suspend/BnSuspendCallback.h>
39 #include <android/system/suspend/ISuspendControlService.h>
40 #include <android_runtime/AndroidRuntime.h>
41 #include <jni.h>
42
43 #include <nativehelper/ScopedLocalRef.h>
44 #include <nativehelper/ScopedPrimitiveArray.h>
45
46 #include <powermanager/PowerHalLoader.h>
47
48 #include <log/log.h>
49 #include <utils/misc.h>
50 #include <utils/Log.h>
51
52 #include <android-base/strings.h>
53
54 using android::hardware::hidl_vec;
55 using android::hardware::Return;
56 using android::hardware::Void;
57 using android::hardware::power::stats::V1_0::IPowerStats;
58 using android::system::suspend::BnSuspendCallback;
59 using android::system::suspend::ISuspendControlService;
60
61 namespace android
62 {
63
64 static bool wakeup_init = false;
65 static std::mutex mReasonsMutex;
66 static std::vector<std::string> mWakeupReasons;
67 static sem_t wakeup_sem;
68 extern sp<ISuspendControlService> getSuspendControl();
69
70 std::mutex gPowerStatsHalMutex;
71 sp<IPowerStats> gPowerStatsHalV1_0 = nullptr;
72
73 std::function<void(JNIEnv*, jobject)> gGetRailEnergyPowerStatsImpl = {};
74
75 // Cellular/Wifi power monitor rail information
76 static jmethodID jupdateRailData = NULL;
77 static jmethodID jsetRailStatsAvailability = NULL;
78
79 std::unordered_map<uint32_t, std::pair<std::string, std::string>> gPowerStatsHalRailNames = {};
80 static bool power_monitor_available = false;
81
deinitPowerStatsHalLocked()82 static void deinitPowerStatsHalLocked() EXCLUSIVE_LOCKS_REQUIRED(gPowerStatsHalMutex) {
83 gPowerStatsHalV1_0 = nullptr;
84 }
85
86 struct PowerHalDeathRecipient : virtual public hardware::hidl_death_recipient {
serviceDiedandroid::PowerHalDeathRecipient87 virtual void serviceDied(uint64_t cookie,
88 const wp<android::hidl::base::V1_0::IBase>& who) override {
89 // The HAL just died. Reset all handles to HAL services.
90 std::lock_guard<std::mutex> lock(gPowerStatsHalMutex);
91 deinitPowerStatsHalLocked();
92 }
93 };
94
95 sp<PowerHalDeathRecipient> gDeathRecipient = new PowerHalDeathRecipient();
96
97 class WakeupCallback : public BnSuspendCallback {
98 public:
notifyWakeup(bool success,const std::vector<std::string> & wakeupReasons)99 binder::Status notifyWakeup(bool success,
100 const std::vector<std::string>& wakeupReasons) override {
101 ALOGV("In wakeup_callback: %s", success ? "resumed from suspend" : "suspend aborted");
102 bool reasonsCaptured = false;
103 {
104 std::unique_lock<std::mutex> reasonsLock(mReasonsMutex, std::defer_lock);
105 if (reasonsLock.try_lock() && mWakeupReasons.empty()) {
106 mWakeupReasons = wakeupReasons;
107 reasonsCaptured = true;
108 }
109 }
110 if (!reasonsCaptured) {
111 ALOGE("Failed to write wakeup reasons. Reasons dropped:");
112 for (auto wakeupReason : wakeupReasons) {
113 ALOGE("\t%s", wakeupReason.c_str());
114 }
115 }
116
117 int ret = sem_post(&wakeup_sem);
118 if (ret < 0) {
119 char buf[80];
120 strerror_r(errno, buf, sizeof(buf));
121 ALOGE("Error posting wakeup sem: %s\n", buf);
122 }
123 return binder::Status::ok();
124 }
125 };
126
nativeWaitWakeup(JNIEnv * env,jobject clazz,jobject outBuf)127 static jint nativeWaitWakeup(JNIEnv *env, jobject clazz, jobject outBuf)
128 {
129 if (outBuf == NULL) {
130 jniThrowException(env, "java/lang/NullPointerException", "null argument");
131 return -1;
132 }
133
134 // Register our wakeup callback if not yet done.
135 if (!wakeup_init) {
136 wakeup_init = true;
137 ALOGV("Creating semaphore...");
138 int ret = sem_init(&wakeup_sem, 0, 0);
139 if (ret < 0) {
140 char buf[80];
141 strerror_r(errno, buf, sizeof(buf));
142 ALOGE("Error creating semaphore: %s\n", buf);
143 jniThrowException(env, "java/lang/IllegalStateException", buf);
144 return -1;
145 }
146 sp<ISuspendControlService> suspendControl = getSuspendControl();
147 bool isRegistered = false;
148 suspendControl->registerCallback(new WakeupCallback(), &isRegistered);
149 if (!isRegistered) {
150 ALOGE("Failed to register wakeup callback");
151 }
152 }
153
154 // Wait for wakeup.
155 ALOGV("Waiting for wakeup...");
156 int ret = sem_wait(&wakeup_sem);
157 if (ret < 0) {
158 char buf[80];
159 strerror_r(errno, buf, sizeof(buf));
160 ALOGE("Error waiting on semaphore: %s\n", buf);
161 // Return 0 here to let it continue looping but not return results.
162 return 0;
163 }
164
165 char* mergedreason = (char*)env->GetDirectBufferAddress(outBuf);
166 int remainreasonlen = (int)env->GetDirectBufferCapacity(outBuf);
167
168 ALOGV("Reading wakeup reasons");
169 std::vector<std::string> wakeupReasons;
170 {
171 std::unique_lock<std::mutex> reasonsLock(mReasonsMutex, std::defer_lock);
172 if (reasonsLock.try_lock() && !mWakeupReasons.empty()) {
173 wakeupReasons = std::move(mWakeupReasons);
174 mWakeupReasons.clear();
175 }
176 }
177
178 if (wakeupReasons.empty()) {
179 return 0;
180 }
181
182 std::string mergedReasonStr = ::android::base::Join(wakeupReasons, ":");
183 strncpy(mergedreason, mergedReasonStr.c_str(), remainreasonlen);
184 mergedreason[remainreasonlen - 1] = '\0';
185
186 ALOGV("Got %d reasons", (int)wakeupReasons.size());
187
188 return strlen(mergedreason);
189 }
190
checkPowerStatsHalResultLocked(const Return<void> & ret,const char * function)191 static bool checkPowerStatsHalResultLocked(const Return<void>& ret, const char* function)
192 EXCLUSIVE_LOCKS_REQUIRED(gPowerStatsHalMutex) {
193 if (!ret.isOk()) {
194 ALOGE("%s failed: requested HAL service not available. Description: %s",
195 function, ret.description().c_str());
196 if (ret.isDeadObject()) {
197 deinitPowerStatsHalLocked();
198 }
199 return false;
200 }
201 return true;
202 }
203
204 // gPowerStatsHalV1_0 must not be null
initializePowerStatsLocked()205 static bool initializePowerStatsLocked() EXCLUSIVE_LOCKS_REQUIRED(gPowerStatsHalMutex) {
206 using android::hardware::power::stats::V1_0::Status;
207
208 // Clear out previous content if we are re-initializing
209 gPowerStatsHalRailNames.clear();
210
211 Return<void> ret;
212
213 // Get Power monitor rails available
214 ret = gPowerStatsHalV1_0->getRailInfo([](auto rails, auto status) {
215 if (status != Status::SUCCESS) {
216 ALOGW("Rail information is not available");
217 power_monitor_available = false;
218 return;
219 }
220
221 // Fill out rail names/subsystems into gPowerStatsHalRailNames
222 for (auto rail : rails) {
223 gPowerStatsHalRailNames.emplace(rail.index,
224 std::make_pair(rail.railName, rail.subsysName));
225 }
226 if (!gPowerStatsHalRailNames.empty()) {
227 power_monitor_available = true;
228 }
229 });
230 if (!checkPowerStatsHalResultLocked(ret, __func__)) {
231 return false;
232 }
233
234 return true;
235 }
236
getPowerStatsHalLocked()237 static bool getPowerStatsHalLocked() EXCLUSIVE_LOCKS_REQUIRED(gPowerStatsHalMutex) {
238 if (gPowerStatsHalV1_0 == nullptr) {
239 gPowerStatsHalV1_0 = IPowerStats::getService();
240 if (gPowerStatsHalV1_0 == nullptr) {
241 ALOGE("Unable to get power.stats HAL service.");
242 return false;
243 }
244
245 // Link death recipient to power.stats service handle
246 hardware::Return<bool> linked = gPowerStatsHalV1_0->linkToDeath(gDeathRecipient, 0);
247 if (!linked.isOk()) {
248 ALOGE("Transaction error in linking to power.stats HAL death: %s",
249 linked.description().c_str());
250 deinitPowerStatsHalLocked();
251 return false;
252 } else if (!linked) {
253 ALOGW("Unable to link to power.stats HAL death notifications");
254 // We should still continue even though linking failed
255 }
256 return initializePowerStatsLocked();
257 }
258 return true;
259 }
260
getPowerStatsHalRailEnergyDataLocked(JNIEnv * env,jobject jrailStats)261 static void getPowerStatsHalRailEnergyDataLocked(JNIEnv* env, jobject jrailStats)
262 EXCLUSIVE_LOCKS_REQUIRED(gPowerStatsHalMutex) {
263 using android::hardware::power::stats::V1_0::Status;
264 using android::hardware::power::stats::V1_0::EnergyData;
265
266 if (!getPowerStatsHalLocked()) {
267 ALOGE("failed to get power stats");
268 return;
269 }
270
271 if (!power_monitor_available) {
272 env->CallVoidMethod(jrailStats, jsetRailStatsAvailability, false);
273 ALOGW("Rail energy data is not available");
274 return;
275 }
276
277 // Get power rail energySinceBoot data
278 Return<void> ret = gPowerStatsHalV1_0->getEnergyData({},
279 [&env, &jrailStats](auto energyData, auto status) {
280 if (status == Status::NOT_SUPPORTED) {
281 ALOGW("getEnergyData is not supported");
282 return;
283 }
284
285 for (auto data : energyData) {
286 if (!(data.timestamp > LLONG_MAX || data.energy > LLONG_MAX)) {
287 env->CallVoidMethod(jrailStats,
288 jupdateRailData,
289 data.index,
290 env->NewStringUTF(
291 gPowerStatsHalRailNames.at(data.index).first.c_str()),
292 env->NewStringUTF(
293 gPowerStatsHalRailNames.at(data.index).second.c_str()),
294 data.timestamp,
295 data.energy);
296 } else {
297 ALOGE("Java long overflow seen. Rail index %d not updated", data.index);
298 }
299 }
300 });
301 if (!checkPowerStatsHalResultLocked(ret, __func__)) {
302 ALOGE("getEnergyData failed");
303 }
304 }
305
setUpPowerStatsLocked()306 static void setUpPowerStatsLocked() EXCLUSIVE_LOCKS_REQUIRED(gPowerStatsHalMutex) {
307 // First see if power.stats HAL is available. Fall back to power HAL if
308 // power.stats HAL is unavailable.
309 if (IPowerStats::getService() != nullptr) {
310 ALOGI("Using power.stats HAL");
311 gGetRailEnergyPowerStatsImpl = getPowerStatsHalRailEnergyDataLocked;
312 } else {
313 gGetRailEnergyPowerStatsImpl = NULL;
314 }
315 }
316
getRailEnergyPowerStats(JNIEnv * env,jobject,jobject jrailStats)317 static void getRailEnergyPowerStats(JNIEnv* env, jobject /* clazz */, jobject jrailStats) {
318 if (jrailStats == NULL) {
319 jniThrowException(env, "java/lang/NullPointerException",
320 "The railstats jni input jobject jrailStats is null.");
321 return;
322 }
323 if (jupdateRailData == NULL) {
324 ALOGE("A railstats jni jmethodID is null.");
325 return;
326 }
327
328 std::lock_guard<std::mutex> lock(gPowerStatsHalMutex);
329
330 if (!gGetRailEnergyPowerStatsImpl) {
331 setUpPowerStatsLocked();
332 }
333
334 if (gGetRailEnergyPowerStatsImpl) {
335 gGetRailEnergyPowerStatsImpl(env, jrailStats);
336 return;
337 }
338
339 if (jsetRailStatsAvailability == NULL) {
340 ALOGE("setRailStatsAvailability jni jmethodID is null.");
341 return;
342 }
343 env->CallVoidMethod(jrailStats, jsetRailStatsAvailability, false);
344 ALOGE("Unable to load Power.Stats.HAL. Setting rail availability to false");
345 return;
346 }
347
348 static const JNINativeMethod method_table[] = {
349 { "nativeWaitWakeup", "(Ljava/nio/ByteBuffer;)I", (void*)nativeWaitWakeup },
350 { "getRailEnergyPowerStats", "(Lcom/android/internal/os/RailStats;)V",
351 (void*)getRailEnergyPowerStats },
352 };
353
register_android_server_BatteryStatsService(JNIEnv * env)354 int register_android_server_BatteryStatsService(JNIEnv *env)
355 {
356 // get java classes and methods
357 jclass clsRailStats = env->FindClass("com/android/internal/os/RailStats");
358 if (clsRailStats == NULL) {
359 ALOGE("A rpmstats jni jclass is null.");
360 } else {
361 jupdateRailData = env->GetMethodID(clsRailStats, "updateRailData",
362 "(JLjava/lang/String;Ljava/lang/String;JJ)V");
363 jsetRailStatsAvailability = env->GetMethodID(clsRailStats, "setRailStatsAvailability",
364 "(Z)V");
365 }
366
367 return jniRegisterNativeMethods(env, "com/android/server/am/BatteryStatsService",
368 method_table, NELEM(method_table));
369 }
370
371 };
372