| /* |
| * Copyright (C) 2010 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #define LOG_TAG "PowerManagerService-JNI" |
| |
| //#define LOG_NDEBUG 0 |
| |
| #include <android/hardware/power/1.1/IPower.h> |
| #include <android/hardware/power/Boost.h> |
| #include <android/hardware/power/IPower.h> |
| #include <android/hardware/power/Mode.h> |
| #include <android/system/suspend/1.0/ISystemSuspend.h> |
| #include <android/system/suspend/ISuspendControlService.h> |
| #include <nativehelper/JNIHelp.h> |
| #include "jni.h" |
| |
| #include <nativehelper/ScopedUtfChars.h> |
| |
| #include <limits.h> |
| |
| #include <android-base/chrono_utils.h> |
| #include <android_runtime/AndroidRuntime.h> |
| #include <android_runtime/Log.h> |
| #include <binder/IServiceManager.h> |
| #include <gui/SurfaceComposerClient.h> |
| #include <hardware/power.h> |
| #include <hardware_legacy/power.h> |
| #include <hidl/ServiceManagement.h> |
| #include <utils/Timers.h> |
| #include <utils/misc.h> |
| #include <utils/String8.h> |
| #include <utils/Log.h> |
| |
| #include "com_android_server_power_PowerManagerService.h" |
| |
| using android::hardware::Return; |
| using android::hardware::Void; |
| using android::hardware::power::Boost; |
| using android::hardware::power::Mode; |
| using android::hardware::power::V1_0::PowerHint; |
| using android::hardware::power::V1_0::Feature; |
| using android::String8; |
| using android::system::suspend::V1_0::ISystemSuspend; |
| using android::system::suspend::V1_0::IWakeLock; |
| using android::system::suspend::V1_0::WakeLockType; |
| using android::system::suspend::ISuspendControlService; |
| using IPowerV1_1 = android::hardware::power::V1_1::IPower; |
| using IPowerV1_0 = android::hardware::power::V1_0::IPower; |
| using IPowerAidl = android::hardware::power::IPower; |
| |
| namespace android { |
| |
| // ---------------------------------------------------------------------------- |
| |
| static struct { |
| jmethodID userActivityFromNative; |
| } gPowerManagerServiceClassInfo; |
| |
| // ---------------------------------------------------------------------------- |
| |
| static jobject gPowerManagerServiceObj; |
| static sp<IPowerV1_0> gPowerHalHidlV1_0_ = nullptr; |
| static sp<IPowerV1_1> gPowerHalHidlV1_1_ = nullptr; |
| static sp<IPowerAidl> gPowerHalAidl_ = nullptr; |
| static std::mutex gPowerHalMutex; |
| |
| enum class HalVersion { |
| NONE, |
| HIDL_1_0, |
| HIDL_1_1, |
| AIDL, |
| }; |
| |
| static nsecs_t gLastEventTime[USER_ACTIVITY_EVENT_LAST + 1]; |
| |
| // Throttling interval for user activity calls. |
| static const nsecs_t MIN_TIME_BETWEEN_USERACTIVITIES = 100 * 1000000L; // 100ms |
| |
| // ---------------------------------------------------------------------------- |
| |
| static bool checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) { |
| if (env->ExceptionCheck()) { |
| ALOGE("An exception was thrown by callback '%s'.", methodName); |
| LOGE_EX(env); |
| env->ExceptionClear(); |
| return true; |
| } |
| return false; |
| } |
| |
| // Check validity of current handle to the power HAL service, and connect to it if necessary. |
| // The caller must be holding gPowerHalMutex. |
| static HalVersion connectPowerHalLocked() { |
| static bool gPowerHalHidlExists = true; |
| static bool gPowerHalAidlExists = true; |
| if (!gPowerHalHidlExists && !gPowerHalAidlExists) { |
| return HalVersion::NONE; |
| } |
| if (gPowerHalAidlExists) { |
| if (!gPowerHalAidl_) { |
| gPowerHalAidl_ = waitForVintfService<IPowerAidl>(); |
| } |
| if (gPowerHalAidl_) { |
| ALOGI("Successfully connected to Power HAL AIDL service."); |
| return HalVersion::AIDL; |
| } else { |
| gPowerHalAidlExists = false; |
| } |
| } |
| if (gPowerHalHidlExists && gPowerHalHidlV1_0_ == nullptr) { |
| gPowerHalHidlV1_0_ = IPowerV1_0::getService(); |
| if (gPowerHalHidlV1_0_) { |
| ALOGI("Successfully connected to Power HAL HIDL 1.0 service."); |
| // Try cast to powerHAL HIDL V1_1 |
| gPowerHalHidlV1_1_ = IPowerV1_1::castFrom(gPowerHalHidlV1_0_); |
| if (gPowerHalHidlV1_1_) { |
| ALOGI("Successfully connected to Power HAL HIDL 1.1 service."); |
| } |
| } else { |
| ALOGI("Couldn't load power HAL HIDL service"); |
| gPowerHalHidlExists = false; |
| return HalVersion::NONE; |
| } |
| } |
| if (gPowerHalHidlV1_1_) { |
| return HalVersion::HIDL_1_1; |
| } else if (gPowerHalHidlV1_0_) { |
| return HalVersion::HIDL_1_0; |
| } |
| return HalVersion::NONE; |
| } |
| |
| // Retrieve a copy of PowerHAL HIDL V1_0 |
| sp<IPowerV1_0> getPowerHalHidlV1_0() { |
| std::lock_guard<std::mutex> lock(gPowerHalMutex); |
| HalVersion halVersion = connectPowerHalLocked(); |
| if (halVersion == HalVersion::HIDL_1_0 || halVersion == HalVersion::HIDL_1_1) { |
| return gPowerHalHidlV1_0_; |
| } |
| |
| return nullptr; |
| } |
| |
| // Retrieve a copy of PowerHAL HIDL V1_1 |
| sp<IPowerV1_1> getPowerHalHidlV1_1() { |
| std::lock_guard<std::mutex> lock(gPowerHalMutex); |
| if (connectPowerHalLocked() == HalVersion::HIDL_1_1) { |
| return gPowerHalHidlV1_1_; |
| } |
| |
| return nullptr; |
| } |
| |
| // Check if a call to a power HAL function failed; if so, log the failure and invalidate the |
| // current handle to the power HAL service. |
| bool processPowerHalReturn(bool isOk, const char* functionName) { |
| if (!isOk) { |
| ALOGE("%s() failed: power HAL service not available.", functionName); |
| gPowerHalMutex.lock(); |
| gPowerHalHidlV1_0_ = nullptr; |
| gPowerHalHidlV1_1_ = nullptr; |
| gPowerHalAidl_ = nullptr; |
| gPowerHalMutex.unlock(); |
| } |
| return isOk; |
| } |
| |
| enum class HalSupport { |
| UNKNOWN = 0, |
| ON, |
| OFF, |
| }; |
| |
| static void setPowerBoostWithHandle(sp<IPowerAidl> handle, Boost boost, int32_t durationMs) { |
| // Android framework only sends boost upto DISPLAY_UPDATE_IMMINENT. |
| // Need to increase the array size if more boost supported. |
| static std::array<std::atomic<HalSupport>, |
| static_cast<int32_t>(Boost::DISPLAY_UPDATE_IMMINENT) + 1> |
| boostSupportedArray = {HalSupport::UNKNOWN}; |
| |
| // Quick return if boost is not supported by HAL |
| if (boost > Boost::DISPLAY_UPDATE_IMMINENT || |
| boostSupportedArray[static_cast<int32_t>(boost)] == HalSupport::OFF) { |
| ALOGV("Skipped setPowerBoost %s because HAL doesn't support it", toString(boost).c_str()); |
| return; |
| } |
| |
| if (boostSupportedArray[static_cast<int32_t>(boost)] == HalSupport::UNKNOWN) { |
| bool isSupported = false; |
| handle->isBoostSupported(boost, &isSupported); |
| boostSupportedArray[static_cast<int32_t>(boost)] = |
| isSupported ? HalSupport::ON : HalSupport::OFF; |
| if (!isSupported) { |
| ALOGV("Skipped setPowerBoost %s because HAL doesn't support it", |
| toString(boost).c_str()); |
| return; |
| } |
| } |
| |
| auto ret = handle->setBoost(boost, durationMs); |
| processPowerHalReturn(ret.isOk(), "setPowerBoost"); |
| } |
| |
| static void setPowerBoost(Boost boost, int32_t durationMs) { |
| std::unique_lock<std::mutex> lock(gPowerHalMutex); |
| if (connectPowerHalLocked() != HalVersion::AIDL) { |
| ALOGV("Power HAL AIDL not available"); |
| return; |
| } |
| sp<IPowerAidl> handle = gPowerHalAidl_; |
| lock.unlock(); |
| setPowerBoostWithHandle(handle, boost, durationMs); |
| } |
| |
| static bool setPowerModeWithHandle(sp<IPowerAidl> handle, Mode mode, bool enabled) { |
| // Android framework only sends mode upto DISPLAY_INACTIVE. |
| // Need to increase the array if more mode supported. |
| static std::array<std::atomic<HalSupport>, static_cast<int32_t>(Mode::DISPLAY_INACTIVE) + 1> |
| modeSupportedArray = {HalSupport::UNKNOWN}; |
| |
| // Quick return if mode is not supported by HAL |
| if (mode > Mode::DISPLAY_INACTIVE || |
| modeSupportedArray[static_cast<int32_t>(mode)] == HalSupport::OFF) { |
| ALOGV("Skipped setPowerMode %s because HAL doesn't support it", toString(mode).c_str()); |
| return false; |
| } |
| |
| if (modeSupportedArray[static_cast<int32_t>(mode)] == HalSupport::UNKNOWN) { |
| bool isSupported = false; |
| handle->isModeSupported(mode, &isSupported); |
| modeSupportedArray[static_cast<int32_t>(mode)] = |
| isSupported ? HalSupport::ON : HalSupport::OFF; |
| if (!isSupported) { |
| ALOGV("Skipped setPowerMode %s because HAL doesn't support it", toString(mode).c_str()); |
| return false; |
| } |
| } |
| |
| auto ret = handle->setMode(mode, enabled); |
| processPowerHalReturn(ret.isOk(), "setPowerMode"); |
| return ret.isOk(); |
| } |
| |
| static bool setPowerMode(Mode mode, bool enabled) { |
| std::unique_lock<std::mutex> lock(gPowerHalMutex); |
| if (connectPowerHalLocked() != HalVersion::AIDL) { |
| ALOGV("Power HAL AIDL not available"); |
| return false; |
| } |
| sp<IPowerAidl> handle = gPowerHalAidl_; |
| lock.unlock(); |
| return setPowerModeWithHandle(handle, mode, enabled); |
| } |
| |
| static void sendPowerHint(PowerHint hintId, uint32_t data) { |
| std::unique_lock<std::mutex> lock(gPowerHalMutex); |
| switch (connectPowerHalLocked()) { |
| case HalVersion::NONE: |
| return; |
| case HalVersion::HIDL_1_0: { |
| sp<IPowerV1_0> handle = gPowerHalHidlV1_0_; |
| lock.unlock(); |
| auto ret = handle->powerHint(hintId, data); |
| processPowerHalReturn(ret.isOk(), "powerHint"); |
| break; |
| } |
| case HalVersion::HIDL_1_1: { |
| sp<IPowerV1_1> handle = gPowerHalHidlV1_1_; |
| lock.unlock(); |
| auto ret = handle->powerHintAsync(hintId, data); |
| processPowerHalReturn(ret.isOk(), "powerHintAsync"); |
| break; |
| } |
| case HalVersion::AIDL: { |
| if (hintId == PowerHint::INTERACTION) { |
| sp<IPowerAidl> handle = gPowerHalAidl_; |
| lock.unlock(); |
| setPowerBoostWithHandle(handle, Boost::INTERACTION, data); |
| break; |
| } else if (hintId == PowerHint::LAUNCH) { |
| sp<IPowerAidl> handle = gPowerHalAidl_; |
| lock.unlock(); |
| setPowerModeWithHandle(handle, Mode::LAUNCH, static_cast<bool>(data)); |
| break; |
| } else if (hintId == PowerHint::LOW_POWER) { |
| sp<IPowerAidl> handle = gPowerHalAidl_; |
| lock.unlock(); |
| setPowerModeWithHandle(handle, Mode::LOW_POWER, static_cast<bool>(data)); |
| break; |
| } else if (hintId == PowerHint::SUSTAINED_PERFORMANCE) { |
| sp<IPowerAidl> handle = gPowerHalAidl_; |
| lock.unlock(); |
| setPowerModeWithHandle(handle, Mode::SUSTAINED_PERFORMANCE, |
| static_cast<bool>(data)); |
| break; |
| } else if (hintId == PowerHint::VR_MODE) { |
| sp<IPowerAidl> handle = gPowerHalAidl_; |
| lock.unlock(); |
| setPowerModeWithHandle(handle, Mode::VR, static_cast<bool>(data)); |
| break; |
| } else { |
| ALOGE("Unsupported power hint: %s.", toString(hintId).c_str()); |
| return; |
| } |
| } |
| default: { |
| ALOGE("Unknown power HAL state"); |
| return; |
| } |
| } |
| SurfaceComposerClient::notifyPowerHint(static_cast<int32_t>(hintId)); |
| } |
| |
| void android_server_PowerManagerService_userActivity(nsecs_t eventTime, int32_t eventType) { |
| if (gPowerManagerServiceObj) { |
| // Throttle calls into user activity by event type. |
| // We're a little conservative about argument checking here in case the caller |
| // passes in bad data which could corrupt system state. |
| if (eventType >= 0 && eventType <= USER_ACTIVITY_EVENT_LAST) { |
| nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); |
| if (eventTime > now) { |
| eventTime = now; |
| } |
| |
| if (gLastEventTime[eventType] + MIN_TIME_BETWEEN_USERACTIVITIES > eventTime) { |
| return; |
| } |
| gLastEventTime[eventType] = eventTime; |
| |
| // Tell the power HAL when user activity occurs. |
| sendPowerHint(PowerHint::INTERACTION, 0); |
| } |
| |
| JNIEnv* env = AndroidRuntime::getJNIEnv(); |
| |
| env->CallVoidMethod(gPowerManagerServiceObj, |
| gPowerManagerServiceClassInfo.userActivityFromNative, |
| nanoseconds_to_milliseconds(eventTime), eventType, 0); |
| checkAndClearExceptionFromCallback(env, "userActivityFromNative"); |
| } |
| } |
| |
| static sp<ISystemSuspend> gSuspendHal = nullptr; |
| static sp<ISuspendControlService> gSuspendControl = nullptr; |
| static sp<IWakeLock> gSuspendBlocker = nullptr; |
| static std::mutex gSuspendMutex; |
| |
| // Assume SystemSuspend HAL is always alive. |
| // TODO: Force device to restart if SystemSuspend HAL dies. |
| sp<ISystemSuspend> getSuspendHal() { |
| static std::once_flag suspendHalFlag; |
| std::call_once(suspendHalFlag, [](){ |
| ::android::hardware::details::waitForHwService(ISystemSuspend::descriptor, "default"); |
| gSuspendHal = ISystemSuspend::getService(); |
| assert(gSuspendHal != nullptr); |
| }); |
| return gSuspendHal; |
| } |
| |
| sp<ISuspendControlService> getSuspendControl() { |
| static std::once_flag suspendControlFlag; |
| std::call_once(suspendControlFlag, [](){ |
| gSuspendControl = waitForService<ISuspendControlService>(String16("suspend_control")); |
| LOG_ALWAYS_FATAL_IF(gSuspendControl == nullptr); |
| }); |
| return gSuspendControl; |
| } |
| |
| void enableAutoSuspend() { |
| static bool enabled = false; |
| if (!enabled) { |
| sp<ISuspendControlService> suspendControl = getSuspendControl(); |
| suspendControl->enableAutosuspend(&enabled); |
| } |
| |
| { |
| std::lock_guard<std::mutex> lock(gSuspendMutex); |
| if (gSuspendBlocker) { |
| gSuspendBlocker->release(); |
| gSuspendBlocker.clear(); |
| } |
| } |
| } |
| |
| void disableAutoSuspend() { |
| std::lock_guard<std::mutex> lock(gSuspendMutex); |
| if (!gSuspendBlocker) { |
| sp<ISystemSuspend> suspendHal = getSuspendHal(); |
| gSuspendBlocker = suspendHal->acquireWakeLock(WakeLockType::PARTIAL, |
| "PowerManager.SuspendLockout"); |
| } |
| } |
| |
| // ---------------------------------------------------------------------------- |
| |
| static void nativeInit(JNIEnv* env, jobject obj) { |
| gPowerManagerServiceObj = env->NewGlobalRef(obj); |
| |
| gPowerHalMutex.lock(); |
| connectPowerHalLocked(); |
| gPowerHalMutex.unlock(); |
| } |
| |
| static void nativeAcquireSuspendBlocker(JNIEnv *env, jclass /* clazz */, jstring nameStr) { |
| ScopedUtfChars name(env, nameStr); |
| acquire_wake_lock(PARTIAL_WAKE_LOCK, name.c_str()); |
| } |
| |
| static void nativeReleaseSuspendBlocker(JNIEnv *env, jclass /* clazz */, jstring nameStr) { |
| ScopedUtfChars name(env, nameStr); |
| release_wake_lock(name.c_str()); |
| } |
| |
| static void nativeSetInteractive(JNIEnv* /* env */, jclass /* clazz */, jboolean enable) { |
| std::unique_lock<std::mutex> lock(gPowerHalMutex); |
| switch (connectPowerHalLocked()) { |
| case HalVersion::NONE: |
| return; |
| case HalVersion::HIDL_1_0: |
| FALLTHROUGH_INTENDED; |
| case HalVersion::HIDL_1_1: { |
| android::base::Timer t; |
| sp<IPowerV1_0> handle = gPowerHalHidlV1_0_; |
| lock.unlock(); |
| auto ret = handle->setInteractive(enable); |
| processPowerHalReturn(ret.isOk(), "setInteractive"); |
| if (t.duration() > 20ms) { |
| ALOGD("Excessive delay in setInteractive(%s) while turning screen %s", |
| enable ? "true" : "false", enable ? "on" : "off"); |
| } |
| return; |
| } |
| case HalVersion::AIDL: { |
| sp<IPowerAidl> handle = gPowerHalAidl_; |
| lock.unlock(); |
| setPowerModeWithHandle(handle, Mode::INTERACTIVE, enable); |
| return; |
| } |
| default: { |
| ALOGE("Unknown power HAL state"); |
| return; |
| } |
| } |
| } |
| |
| static void nativeSetAutoSuspend(JNIEnv* /* env */, jclass /* clazz */, jboolean enable) { |
| if (enable) { |
| android::base::Timer t; |
| enableAutoSuspend(); |
| if (t.duration() > 100ms) { |
| ALOGD("Excessive delay in autosuspend_enable() while turning screen off"); |
| } |
| } else { |
| android::base::Timer t; |
| disableAutoSuspend(); |
| if (t.duration() > 100ms) { |
| ALOGD("Excessive delay in autosuspend_disable() while turning screen on"); |
| } |
| } |
| } |
| |
| static void nativeSendPowerHint(JNIEnv* /* env */, jclass /* clazz */, jint hintId, jint data) { |
| sendPowerHint(static_cast<PowerHint>(hintId), data); |
| } |
| |
| static void nativeSetPowerBoost(JNIEnv* /* env */, jclass /* clazz */, jint boost, |
| jint durationMs) { |
| setPowerBoost(static_cast<Boost>(boost), durationMs); |
| } |
| |
| static jboolean nativeSetPowerMode(JNIEnv* /* env */, jclass /* clazz */, jint mode, |
| jboolean enabled) { |
| return setPowerMode(static_cast<Mode>(mode), enabled); |
| } |
| |
| static void nativeSetFeature(JNIEnv* /* env */, jclass /* clazz */, jint featureId, jint data) { |
| std::unique_lock<std::mutex> lock(gPowerHalMutex); |
| switch (connectPowerHalLocked()) { |
| case HalVersion::NONE: |
| return; |
| case HalVersion::HIDL_1_0: |
| FALLTHROUGH_INTENDED; |
| case HalVersion::HIDL_1_1: { |
| sp<IPowerV1_0> handle = gPowerHalHidlV1_0_; |
| lock.unlock(); |
| auto ret = handle->setFeature(static_cast<Feature>(featureId), static_cast<bool>(data)); |
| processPowerHalReturn(ret.isOk(), "setFeature"); |
| return; |
| } |
| case HalVersion::AIDL: { |
| sp<IPowerAidl> handle = gPowerHalAidl_; |
| lock.unlock(); |
| setPowerModeWithHandle(handle, Mode::DOUBLE_TAP_TO_WAKE, static_cast<bool>(data)); |
| return; |
| } |
| default: { |
| ALOGE("Unknown power HAL state"); |
| return; |
| } |
| } |
| } |
| |
| static bool nativeForceSuspend(JNIEnv* /* env */, jclass /* clazz */) { |
| bool retval = false; |
| getSuspendControl()->forceSuspend(&retval); |
| return retval; |
| } |
| |
| // ---------------------------------------------------------------------------- |
| |
| static const JNINativeMethod gPowerManagerServiceMethods[] = { |
| /* name, signature, funcPtr */ |
| {"nativeInit", "()V", (void*)nativeInit}, |
| {"nativeAcquireSuspendBlocker", "(Ljava/lang/String;)V", |
| (void*)nativeAcquireSuspendBlocker}, |
| {"nativeForceSuspend", "()Z", (void*)nativeForceSuspend}, |
| {"nativeReleaseSuspendBlocker", "(Ljava/lang/String;)V", |
| (void*)nativeReleaseSuspendBlocker}, |
| {"nativeSetInteractive", "(Z)V", (void*)nativeSetInteractive}, |
| {"nativeSetAutoSuspend", "(Z)V", (void*)nativeSetAutoSuspend}, |
| {"nativeSendPowerHint", "(II)V", (void*)nativeSendPowerHint}, |
| {"nativeSetPowerBoost", "(II)V", (void*)nativeSetPowerBoost}, |
| {"nativeSetPowerMode", "(IZ)Z", (void*)nativeSetPowerMode}, |
| {"nativeSetFeature", "(II)V", (void*)nativeSetFeature}, |
| }; |
| |
| #define FIND_CLASS(var, className) \ |
| var = env->FindClass(className); \ |
| LOG_FATAL_IF(! (var), "Unable to find class " className); |
| |
| #define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \ |
| var = env->GetMethodID(clazz, methodName, methodDescriptor); \ |
| LOG_FATAL_IF(! (var), "Unable to find method " methodName); |
| |
| #define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \ |
| var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \ |
| LOG_FATAL_IF(! (var), "Unable to find field " fieldName); |
| |
| int register_android_server_PowerManagerService(JNIEnv* env) { |
| int res = jniRegisterNativeMethods(env, "com/android/server/power/PowerManagerService", |
| gPowerManagerServiceMethods, NELEM(gPowerManagerServiceMethods)); |
| (void) res; // Faked use when LOG_NDEBUG. |
| LOG_FATAL_IF(res < 0, "Unable to register native methods."); |
| |
| // Callbacks |
| |
| jclass clazz; |
| FIND_CLASS(clazz, "com/android/server/power/PowerManagerService"); |
| |
| GET_METHOD_ID(gPowerManagerServiceClassInfo.userActivityFromNative, clazz, |
| "userActivityFromNative", "(JII)V"); |
| |
| // Initialize |
| for (int i = 0; i <= USER_ACTIVITY_EVENT_LAST; i++) { |
| gLastEventTime[i] = LLONG_MIN; |
| } |
| gPowerManagerServiceObj = NULL; |
| return 0; |
| } |
| |
| } /* namespace android */ |