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