From 620c35bd6bfdf71e2cd7c9f4bd69664a4f39a91e Mon Sep 17 00:00:00 2001 From: Steve Kondik Date: Mon, 2 Nov 2015 17:43:44 -0800 Subject: [PATCH] cmsdk: Refactor the PerformanceManager * Remove all code and configuration into CMSDK. * Deprecate the properties-based API Change-Id: Ib14ce5b8623cb368e6b545d1f82bc9c58580e13b --- Android.mk | 4 +- .../internal/PerformanceManagerService.java | 314 ++++++++++++++++++ cm/res/res/values/arrays.xml | 38 +++ cm/res/res/values/config.xml | 9 +- cm/res/res/values/strings.xml | 7 + cm/res/res/values/symbols.xml | 12 + .../cyanogenmod/app/CMContextConstants.java | 7 + .../power/IPerformanceManager.aidl | 29 ++ .../cyanogenmod/power/PerformanceManager.java | 181 ++++++++++ .../power/PerformanceManagerInternal.java | 29 ++ .../cyanogenmod/providers/CMSettings.java | 12 + 11 files changed, 639 insertions(+), 3 deletions(-) create mode 100644 cm/lib/main/java/org/cyanogenmod/platform/internal/PerformanceManagerService.java create mode 100644 cm/res/res/values/arrays.xml create mode 100644 src/java/cyanogenmod/power/IPerformanceManager.aidl create mode 100644 src/java/cyanogenmod/power/PerformanceManager.java create mode 100644 src/java/cyanogenmod/power/PerformanceManagerInternal.java diff --git a/Android.mk b/Android.mk index e6305248..6f32bb1e 100644 --- a/Android.mk +++ b/Android.mk @@ -193,7 +193,7 @@ LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:= build/tools/droiddoc/templates-sdk LOCAL_DROIDDOC_OPTIONS:= \ -stubs $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/cmsdk_stubs_current_intermediates/src \ - -stubpackages cyanogenmod.alarmclock:cyanogenmod.app:cyanogenmod.content:cyanogenmod.hardware:cyanogenmod.media:cyanogenmod.os:cyanogenmod.profiles:cyanogenmod.providers:cyanogenmod.platform:org.cyanogenmod.platform \ + -stubpackages cyanogenmod.alarmclock:cyanogenmod.app:cyanogenmod.content:cyanogenmod.hardware:cyanogenmod.media:cyanogenmod.os:cyanogenmod.profiles:cyanogenmod.providers:cyanogenmod.platform:cyanogenmod.power:org.cyanogenmod.platform \ -api $(INTERNAL_CM_PLATFORM_API_FILE) \ -removedApi $(INTERNAL_CM_PLATFORM_REMOVED_API_FILE) \ -nodocs \ @@ -222,7 +222,7 @@ LOCAL_MODULE := cm-system-api-stubs LOCAL_DROIDDOC_OPTIONS:=\ -stubs $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/cmsdk_system_stubs_current_intermediates/src \ - -stubpackages cyanogenmod.alarmclock:cyanogenmod.app:cyanogenmod.content:cyanogenmod.hardware:cyanogenmod.media:cyanogenmod.os:cyanogenmod.profiles:cyanogenmod.providers:cyanogenmod.platform:org.cyanogenmod.platform \ + -stubpackages cyanogenmod.alarmclock:cyanogenmod.app:cyanogenmod.content:cyanogenmod.hardware:cyanogenmod.media:cyanogenmod.os:cyanogenmod.profiles:cyanogenmod.providers:cyanogenmod.platform:cyanogenmod.power:org.cyanogenmod.platform \ -showAnnotation android.annotation.SystemApi \ -api $(INTERNAL_CM_PLATFORM_SYSTEM_API_FILE) \ -removedApi $(INTERNAL_CM_PLATFORM_SYSTEM_REMOVED_API_FILE) \ diff --git a/cm/lib/main/java/org/cyanogenmod/platform/internal/PerformanceManagerService.java b/cm/lib/main/java/org/cyanogenmod/platform/internal/PerformanceManagerService.java new file mode 100644 index 00000000..2851d89e --- /dev/null +++ b/cm/lib/main/java/org/cyanogenmod/platform/internal/PerformanceManagerService.java @@ -0,0 +1,314 @@ +/* + * Copyright (C) 2014 The CyanogenMod 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. + */ + +package org.cyanogenmod.platform.internal; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Handler; +import android.os.IBinder; +import android.os.PowerManager; +import android.os.PowerManagerInternal; +import android.os.Looper; +import android.os.Message; +import android.os.Process; +import android.os.SystemProperties; +import android.text.TextUtils; +import android.util.Log; +import android.util.Slog; + +import com.android.server.ServiceThread; +import com.android.server.SystemService; +import com.android.server.Watchdog; + +import cyanogenmod.app.CMContextConstants; +import cyanogenmod.power.IPerformanceManager; +import cyanogenmod.power.PerformanceManager; +import cyanogenmod.power.PerformanceManagerInternal; +import cyanogenmod.providers.CMSettings; + +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.regex.Pattern; + +public class PerformanceManagerService extends SystemService { + + private static final String TAG = "PerformanceManager"; + + private final Context mContext; + + private Pattern[] mPatterns = null; + private int[] mProfiles = null; + + private int mCurrentProfile = -1; + + private boolean mProfileSetByUser = false; + private int mNumProfiles = 0; + + private final ServiceThread mHandlerThread; + private final PerformanceManagerHandler mHandler; + + // keep in sync with hardware/libhardware/include/hardware/power.h + private final int POWER_HINT_CPU_BOOST = 0x00000010; + private final int POWER_HINT_LAUNCH_BOOST = 0x00000011; + private final int POWER_HINT_SET_PROFILE = 0x00000030; + + private final int POWER_FEATURE_SUPPORTED_PROFILES = 0x00001000; + + private PowerManagerInternal mPm; + private boolean mLowPowerModeEnabled = false; + + // Max time (microseconds) to allow a CPU boost for + private static final int MAX_CPU_BOOST_TIME = 5000000; + + public PerformanceManagerService(Context context) { + super(context); + + mContext = context; + + String[] activities = context.getResources().getStringArray( + R.array.config_auto_perf_activities); + if (activities != null && activities.length > 0) { + mPatterns = new Pattern[activities.length]; + mProfiles = new int[activities.length]; + for (int i = 0; i < activities.length; i++) { + String[] info = activities[i].split(","); + if (info.length == 2) { + mPatterns[i] = Pattern.compile(info[0]); + mProfiles[i] = Integer.valueOf(info[1]); + } + } + } + + // We need a high priority thread to handle these requests in front of + // everything else asynchronously + mHandlerThread = new ServiceThread(TAG, + Process.THREAD_PRIORITY_URGENT_DISPLAY + 1, false /*allowIo*/); + mHandlerThread.start(); + + mHandler = new PerformanceManagerHandler(mHandlerThread.getLooper()); + } + + @Override + public void onStart() { + publishBinderService(CMContextConstants.CM_PERFORMANCE_SERVICE, mBinder); + publishLocalService(PerformanceManagerInternal.class, new LocalService()); + } + + @Override + public void onBootPhase(int phase) { + if (phase == PHASE_SYSTEM_SERVICES_READY) { + synchronized (this) { + mPm = getLocalService(PowerManagerInternal.class); + mNumProfiles = mPm.getFeature(POWER_FEATURE_SUPPORTED_PROFILES); + Slog.d(TAG, "Supported profiles: " + mNumProfiles); + if (mNumProfiles > 0) { + int profile = CMSettings.Secure.getInt(mContext.getContentResolver(), + CMSettings.Secure.PERFORMANCE_PROFILE, + PerformanceManager.PROFILE_BALANCED); + if (profile == PerformanceManager.PROFILE_HIGH_PERFORMANCE) { + profile = PerformanceManager.PROFILE_BALANCED; + } + setPowerProfileInternal(profile, true); + mPm.registerLowPowerModeObserver(mLowPowerModeListener); + } + } + } + } + + private boolean hasAppProfiles() { + return mNumProfiles > 0 && mPatterns != null && + (CMSettings.Secure.getInt(mContext.getContentResolver(), + CMSettings.Secure.APP_PERFORMANCE_PROFILES_ENABLED, 1) == 1); + } + + private synchronized boolean setPowerProfileInternal(int profile, boolean fromUser) { + if (profile == mCurrentProfile) { + return false; + } + + if (profile < 0 || profile > mNumProfiles) { + Slog.e(TAG, "Invalid profile: " + profile); + return false; + } + + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null); + + long token = Binder.clearCallingIdentity(); + + if (fromUser) { + CMSettings.Secure.putInt(mContext.getContentResolver(), + CMSettings.Secure.PERFORMANCE_PROFILE, profile); + } + + mCurrentProfile = profile; + + mHandler.removeMessages(MSG_CPU_BOOST); + mHandler.removeMessages(MSG_LAUNCH_BOOST); + mHandler.sendMessage( + Message.obtain(mHandler, MSG_SET_PROFILE, profile, + (fromUser ? 1 : 0))); + + Binder.restoreCallingIdentity(token); + + return true; + } + + private int getProfileForActivity(String componentName) { + if (componentName != null) { + for (int i = 0; i < mPatterns.length; i++) { + if (mPatterns[i].matcher(componentName).matches()) { + return mProfiles[i]; + } + } + } + return PerformanceManager.PROFILE_BALANCED; + } + + private void cpuBoostInternal(int duration) { + if (duration > 0 && duration <= MAX_CPU_BOOST_TIME) { + // Don't send boosts if we're in another power profile + if (mCurrentProfile == PerformanceManager.PROFILE_POWER_SAVE || + mCurrentProfile == PerformanceManager.PROFILE_HIGH_PERFORMANCE) { + return; + } + mHandler.removeMessages(MSG_CPU_BOOST); + mHandler.sendMessage(Message.obtain(mHandler, MSG_CPU_BOOST, duration, 0)); + } else { + Slog.e(TAG, "Invalid boost duration: " + duration); + } + } + + private final IBinder mBinder = new IPerformanceManager.Stub() { + + @Override + public boolean setPowerProfile(int profile) { + return setPowerProfileInternal(profile, true); + } + + /** + * Boost the CPU + * + * @param duration Duration to boost the CPU for, in milliseconds. + * @hide + */ + @Override + public void cpuBoost(int duration) { + cpuBoostInternal(duration); + } + + @Override + public int getPowerProfile() { + return CMSettings.Secure.getInt(mContext.getContentResolver(), + CMSettings.Secure.PERFORMANCE_PROFILE, + PerformanceManager.PROFILE_BALANCED); + } + + @Override + public int getNumberOfProfiles() { + return mNumProfiles; + } + }; + + private final class LocalService implements PerformanceManagerInternal { + + @Override + public void cpuBoost(int duration) { + cpuBoostInternal(duration); + } + + @Override + public void launchBoost() { + // Don't send boosts if we're in another power profile + if (mCurrentProfile == PerformanceManager.PROFILE_POWER_SAVE || + mCurrentProfile == PerformanceManager.PROFILE_HIGH_PERFORMANCE) { + return; + } + mHandler.removeMessages(MSG_CPU_BOOST); + mHandler.removeMessages(MSG_LAUNCH_BOOST); + mHandler.sendEmptyMessage(MSG_LAUNCH_BOOST); + } + + @Override + public void activityResumed(Intent intent) { + if (!hasAppProfiles() || intent == null || mProfileSetByUser) { + return; + } + + final ComponentName cn = intent.getComponent(); + if (cn == null) { + return; + } + + int forApp = getProfileForActivity(cn.flattenToString()); + if (forApp == mCurrentProfile) { + return; + } + + setPowerProfileInternal(forApp, false); + } + } + + private static final int MSG_CPU_BOOST = 1; + private static final int MSG_LAUNCH_BOOST = 2; + private static final int MSG_SET_PROFILE = 3; + + /** + * Handler for asynchronous operations performed by the performance manager. + */ + private final class PerformanceManagerHandler extends Handler { + public PerformanceManagerHandler(Looper looper) { + super(looper, null, true /*async*/); + } + + @Override + public void handleMessage(Message msg) { + Slog.d(TAG, "handler what=" + msg.what + " arg=" + msg.arg1); + switch (msg.what) { + case MSG_CPU_BOOST: + mPm.powerHint(POWER_HINT_CPU_BOOST, msg.arg1); + break; + case MSG_LAUNCH_BOOST: + mPm.powerHint(POWER_HINT_LAUNCH_BOOST, 0); + break; + case MSG_SET_PROFILE: + mPm.powerHint(POWER_HINT_SET_PROFILE, msg.arg1); + break; + } + } + } + + private final PowerManagerInternal.LowPowerModeListener mLowPowerModeListener = new + PowerManagerInternal.LowPowerModeListener() { + + @Override + public void onLowPowerModeChanged(boolean enabled) { + + if (mNumProfiles < 1) { + return; + } + if (enabled == mLowPowerModeEnabled) { + return; + } + if (enabled && mCurrentProfile != PerformanceManager.PROFILE_POWER_SAVE) { + setPowerProfileInternal(PerformanceManager.PROFILE_POWER_SAVE, true); + } else if (!enabled && mCurrentProfile == PerformanceManager.PROFILE_POWER_SAVE) { + setPowerProfileInternal(PerformanceManager.PROFILE_BALANCED, true); + } + } + }; +} diff --git a/cm/res/res/values/arrays.xml b/cm/res/res/values/arrays.xml new file mode 100644 index 00000000..2b78750f --- /dev/null +++ b/cm/res/res/values/arrays.xml @@ -0,0 +1,38 @@ + + + + + + @string/perf_profile_pwrsv + @string/perf_profile_bias_power + @string/perf_profile_bal + @string/perf_profile_bias_perf + @string/perf_profile_perf + + + + + 0 + 3 + 1 + 4 + 2 + + diff --git a/cm/res/res/values/config.xml b/cm/res/res/values/config.xml index eb8982d4..58e7566f 100644 --- a/cm/res/res/values/config.xml +++ b/cm/res/res/values/config.xml @@ -38,4 +38,11 @@ com.cyanogen.app.suggest - \ No newline at end of file + + + + + + diff --git a/cm/res/res/values/strings.xml b/cm/res/res/values/strings.xml index 67e7228a..52606508 100644 --- a/cm/res/res/values/strings.xml +++ b/cm/res/res/values/strings.xml @@ -96,4 +96,11 @@ Other + + Power save + Balanced + Performance + Efficiency + Quick + diff --git a/cm/res/res/values/symbols.xml b/cm/res/res/values/symbols.xml index 79779390..552a22e7 100644 --- a/cm/res/res/values/symbols.xml +++ b/cm/res/res/values/symbols.xml @@ -29,4 +29,16 @@ + + + + + + + + + + + + diff --git a/src/java/cyanogenmod/app/CMContextConstants.java b/src/java/cyanogenmod/app/CMContextConstants.java index b2278b1c..6c5e39bd 100644 --- a/src/java/cyanogenmod/app/CMContextConstants.java +++ b/src/java/cyanogenmod/app/CMContextConstants.java @@ -90,4 +90,11 @@ public final class CMContextConstants { * @hide */ public static final String CM_APP_SUGGEST_SERVICE = "cmappsuggest"; + + /** + * Control device power profile and characteristics. + * + * @hide + */ + public static final String CM_PERFORMANCE_SERVICE = "cmperformance"; } diff --git a/src/java/cyanogenmod/power/IPerformanceManager.aidl b/src/java/cyanogenmod/power/IPerformanceManager.aidl new file mode 100644 index 00000000..106854f4 --- /dev/null +++ b/src/java/cyanogenmod/power/IPerformanceManager.aidl @@ -0,0 +1,29 @@ +/* + * Copyright 2015, The CyanogenMod 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. + */ + +package cyanogenmod.power; + +/** @hide */ +interface IPerformanceManager { + + oneway void cpuBoost(int duration); + + boolean setPowerProfile(int profile); + + int getPowerProfile(); + + int getNumberOfProfiles(); +} diff --git a/src/java/cyanogenmod/power/PerformanceManager.java b/src/java/cyanogenmod/power/PerformanceManager.java new file mode 100644 index 00000000..f91bc989 --- /dev/null +++ b/src/java/cyanogenmod/power/PerformanceManager.java @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2015 The CyanogenMod 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. + */ +package cyanogenmod.power; + +import android.content.Context; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.util.Log; + +import cyanogenmod.app.CMContextConstants; + +/** + * + */ +public class PerformanceManager { + + public static final String TAG = "PerformanceManager"; + + /** + * Power save profile + * + * This mode sacrifices performance for maximum power saving. + */ + public static final int PROFILE_POWER_SAVE = 0; + + /** + * Balanced power profile + * + * The default mode for balanced power savings and performance + */ + public static final int PROFILE_BALANCED = 1; + + /** + * High-performance profile + * + * This mode sacrifices power for maximum performance + */ + public static final int PROFILE_HIGH_PERFORMANCE = 2; + + /** + * Power save bias profile + * + * This mode decreases performance slightly to improve + * power savings. + */ + public static final int PROFILE_BIAS_POWER_SAVE = 3; + + /** + * Performance bias profile + * + * This mode improves performance at the cost of some power. + */ + public static final int PROFILE_BIAS_PERFORMANCE = 4; + + private int mNumberOfProfiles = 0; + + /** + * Broadcast sent when profile is changed + */ + public static final String POWER_PROFILE_CHANGED = "cyanogenmod.power.PROFILE_CHANGED"; + + private static IPerformanceManager sService; + private static PerformanceManager sInstance; + + private PerformanceManager(Context context) { + sService = getService(); + + try { + if (sService != null) { + mNumberOfProfiles = sService.getNumberOfProfiles(); + } + } catch (RemoteException e) { + } + } + + public static PerformanceManager getInstance(Context context) { + if (sInstance == null) { + sInstance = new PerformanceManager(context); + } + return sInstance; + } + + private static IPerformanceManager getService() { + if (sService != null) { + return sService; + } + IBinder b = ServiceManager.getService(CMContextConstants.CM_PERFORMANCE_SERVICE); + if (b != null) { + sService = IPerformanceManager.Stub.asInterface(b); + return sService; + } + return null; + } + + private boolean checkService() { + if (sService == null) { + Log.w(TAG, "not connected to PerformanceManagerService"); + return false; + } + return true; + } + + /** + * Boost the CPU. Boosts the cpu for the given duration in microseconds. + * Requires the {@link android.Manifest.permission#CPU_BOOST} permission. + * + * @param duration in microseconds to boost the CPU + * @hide + */ + public void cpuBoost(int duration) + { + try { + if (checkService()) { + sService.cpuBoost(duration); + } + } catch (RemoteException e) { + } + } + + /** + * Returns the number of supported profiles, zero if unsupported + * This is queried via the PowerHAL. + */ + public int getNumberOfProfiles() { + return mNumberOfProfiles; + } + + /** + * Set the system power profile + * + * @throws IllegalArgumentException if invalid + */ + public boolean setPowerProfile(int profile) { + if (mNumberOfProfiles < 1) { + throw new IllegalArgumentException("Power profiles not enabled on this system!"); + } + + boolean changed = false; + try { + if (checkService()) { + changed = sService.setPowerProfile(profile); + } + } catch (RemoteException e) { + throw new IllegalArgumentException(e); + } + return changed; + } + + /** + * Gets the current power profile + * + * Returns null if power profiles are not enabled + */ + public int getPowerProfile() { + int ret = -1; + if (mNumberOfProfiles > 0) { + try { + if (checkService()) { + ret = sService.getPowerProfile(); + } + } catch (RemoteException e) { + // nothing + } + } + return ret; + } +} diff --git a/src/java/cyanogenmod/power/PerformanceManagerInternal.java b/src/java/cyanogenmod/power/PerformanceManagerInternal.java new file mode 100644 index 00000000..67f158af --- /dev/null +++ b/src/java/cyanogenmod/power/PerformanceManagerInternal.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2015 The CyanogenMod 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. + */ + +package cyanogenmod.power; + +import android.content.Intent; + +/** {@hide} */ +public interface PerformanceManagerInternal { + + void activityResumed(Intent intent); + + void cpuBoost(int duration); + + void launchBoost(); +} diff --git a/src/java/cyanogenmod/providers/CMSettings.java b/src/java/cyanogenmod/providers/CMSettings.java index 2785b194..7fd751bc 100644 --- a/src/java/cyanogenmod/providers/CMSettings.java +++ b/src/java/cyanogenmod/providers/CMSettings.java @@ -1178,6 +1178,18 @@ public final class CMSettings { @Deprecated public static final String BUGREPORT_IN_POWER_MENU = "bugreport_in_power_menu"; + /** + * Performance profile + * @hide + */ + public static final String PERFORMANCE_PROFILE = "performance_profile"; + + /** + * App-based performance profile selection + * @hide + */ + public static final String APP_PERFORMANCE_PROFILES_ENABLED = "app_perf_profiles_enabled"; + // endregion }