From cd98e0b30b07d3a77137c5e5a81720514bf15969 Mon Sep 17 00:00:00 2001 From: Steve Kondik Date: Wed, 12 Oct 2016 04:06:33 -0700 Subject: [PATCH] cmsdk: Rework the PerformanceManager * Added a proper API for this thing * Pass objects around with necessary metadata, pre-filtered and sorted * Added event log and service dump * Simplified locking and adjusted for performance / correctness * Clarified/beautified code where possible * TODO: - Write profile descriptions - Finish the UI - Clean up javadocs - Do something with per-app profiles, maybe Change-Id: Ie2ef84031e81a18e95dcd62392c7e6939860d933 --- api/cm_current.txt | 17 + .../internal/PerformanceManagerService.java | 473 +++++++++++++----- cm/res/res/values/arrays.xml | 20 + cm/res/res/values/strings.xml | 8 + cm/res/res/values/symbols.xml | 7 + .../power/IPerformanceManager.aidl | 8 +- .../cyanogenmod/power/PerformanceManager.java | 89 +++- .../cyanogenmod/power/PerformanceProfile.aidl | 19 + .../cyanogenmod/power/PerformanceProfile.java | 176 +++++++ .../power/unit/PerfomanceManagerTest.java | 8 +- 10 files changed, 690 insertions(+), 135 deletions(-) create mode 100644 sdk/src/java/cyanogenmod/power/PerformanceProfile.aidl create mode 100644 sdk/src/java/cyanogenmod/power/PerformanceProfile.java diff --git a/api/cm_current.txt b/api/cm_current.txt index 64ce7422..17e0d86f 100644 --- a/api/cm_current.txt +++ b/api/cm_current.txt @@ -775,11 +775,15 @@ package cyanogenmod.platform { package cyanogenmod.power { public class PerformanceManager { + method public cyanogenmod.power.PerformanceProfile getActivePowerProfile(); method public static cyanogenmod.power.PerformanceManager getInstance(android.content.Context); method public int getNumberOfProfiles(); method public int getPowerProfile(); + method public cyanogenmod.power.PerformanceProfile getPowerProfile(int); + method public java.util.SortedSet getPowerProfiles(); method public boolean getProfileHasAppProfiles(int); method public boolean setPowerProfile(int); + method public boolean setPowerProfile(cyanogenmod.power.PerformanceProfile); field public static final java.lang.String POWER_PROFILE_CHANGED = "cyanogenmod.power.PROFILE_CHANGED"; field public static final int PROFILE_BALANCED = 1; // 0x1 field public static final int PROFILE_BIAS_PERFORMANCE = 4; // 0x4 @@ -789,6 +793,19 @@ package cyanogenmod.power { field public static final java.lang.String TAG = "PerformanceManager"; } + public class PerformanceProfile implements java.lang.Comparable android.os.Parcelable { + ctor public PerformanceProfile(int, float, java.lang.String, java.lang.String, boolean); + method public int compareTo(cyanogenmod.power.PerformanceProfile); + method public int describeContents(); + method public java.lang.String getDescription(); + method public int getId(); + method public java.lang.String getName(); + method public float getWeight(); + method public boolean isBoostEnabled(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator CREATOR; + } + } package cyanogenmod.preference { diff --git a/cm/lib/main/java/org/cyanogenmod/platform/internal/PerformanceManagerService.java b/cm/lib/main/java/org/cyanogenmod/platform/internal/PerformanceManagerService.java index 57b01418..c422178d 100644 --- a/cm/lib/main/java/org/cyanogenmod/platform/internal/PerformanceManagerService.java +++ b/cm/lib/main/java/org/cyanogenmod/platform/internal/PerformanceManagerService.java @@ -16,9 +16,15 @@ package org.cyanogenmod.platform.internal; +import android.content.BroadcastReceiver; import android.content.ComponentName; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; +import android.content.res.Resources; +import android.database.ContentObserver; +import android.net.Uri; import android.os.Binder; import android.os.Handler; import android.os.IBinder; @@ -26,36 +32,53 @@ import android.os.Looper; import android.os.Message; import android.os.PowerManagerInternal; import android.os.Process; -import android.util.Log; +import android.os.RemoteException; +import android.util.ArrayMap; import android.util.Slog; import com.android.server.ServiceThread; -import com.android.server.SystemService; +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.ArrayDeque; +import java.util.Date; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Objects; import java.util.regex.Pattern; import cyanogenmod.app.CMContextConstants; import cyanogenmod.power.IPerformanceManager; -import cyanogenmod.power.PerformanceManager; import cyanogenmod.power.PerformanceManagerInternal; -import cyanogenmod.providers.CMSettings; +import cyanogenmod.power.PerformanceProfile; -/** @hide */ +import static cyanogenmod.power.PerformanceManager.PROFILE_BALANCED; +import static cyanogenmod.power.PerformanceManager.PROFILE_HIGH_PERFORMANCE; +import static cyanogenmod.power.PerformanceManager.PROFILE_POWER_SAVE; +import static cyanogenmod.providers.CMSettings.Secure.APP_PERFORMANCE_PROFILES_ENABLED; +import static cyanogenmod.providers.CMSettings.Secure.PERFORMANCE_PROFILE; +import static cyanogenmod.providers.CMSettings.Secure.getInt; +import static cyanogenmod.providers.CMSettings.Secure.getUriFor; +import static cyanogenmod.providers.CMSettings.Secure.putInt; + +/** + * @hide + */ public class PerformanceManagerService extends CMSystemService { private static final String TAG = "PerformanceManager"; + private static final boolean DEBUG = false; + private final Context mContext; - private Pattern[] mPatterns = null; - private int[] mProfiles = null; + private final LinkedHashMap mAppProfiles = new LinkedHashMap<>(); + private final ArrayMap mProfiles = new ArrayMap<>(); - /** Active profile that based on low power mode, user and app rules */ - private int mCurrentProfile = -1; private int mNumProfiles = 0; private final ServiceThread mHandlerThread; - private final PerformanceManagerHandler mHandler; + private final BoostHandler mHandler; // keep in sync with hardware/libhardware/include/hardware/power.h private final int POWER_HINT_CPU_BOOST = 0x00000010; @@ -65,45 +88,110 @@ public class PerformanceManagerService extends CMSystemService { private final int POWER_FEATURE_SUPPORTED_PROFILES = 0x00001000; private PowerManagerInternal mPm; - private boolean mLowPowerModeEnabled = false; - private String mCurrentActivityName = null; + + // Observes user-controlled settings + private PerformanceSettingsObserver mObserver; // Max time (microseconds) to allow a CPU boost for private static final int MAX_CPU_BOOST_TIME = 5000000; - private static final boolean DEBUG = false; + + // Standard weights + private static final float WEIGHT_POWER_SAVE = 0.0f; + private static final float WEIGHT_BALANCED = 0.5f; + private static final float WEIGHT_HIGH_PERFORMANCE = 1.0f; + + // Take lock when accessing mProfiles + private final Object mLock = new Object(); + + // Manipulate state variables under lock + private boolean mLowPowerModeEnabled = false; + private boolean mSystemReady = false; + private boolean mBoostEnabled = true; + private int mUserProfile = -1; + private int mActiveProfile = -1; + private String mCurrentActivityName = null; + + // Dumpable circular buffer for boost logging + private final BoostLog mBoostLog = new BoostLog(); + + // Events on the handler + private static final int MSG_CPU_BOOST = 1; + private static final int MSG_LAUNCH_BOOST = 2; + private static final int MSG_SET_PROFILE = 3; public PerformanceManagerService(Context context) { super(context); mContext = context; + Resources res = context.getResources(); - String[] activities = context.getResources().getStringArray( - R.array.config_auto_perf_activities); + String[] activities = res.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]); + mAppProfiles.put(Pattern.compile(info[0]), Integer.valueOf(info[1])); if (DEBUG) { Slog.d(TAG, String.format("App profile #%d: %s => %s", - i, info[0], info[1])); + i, info[0], info[1])); } } } } - // We need a high priority thread to handle these requests in front of + // We need a higher priority thread to handle these requests in front of // everything else asynchronously mHandlerThread = new ServiceThread(TAG, - Process.THREAD_PRIORITY_URGENT_DISPLAY + 1, false /*allowIo*/); + Process.THREAD_PRIORITY_DISPLAY, false /*allowIo*/); mHandlerThread.start(); - mHandler = new PerformanceManagerHandler(mHandlerThread.getLooper()); + mHandler = new BoostHandler(mHandlerThread.getLooper()); } + private class PerformanceSettingsObserver extends ContentObserver { + + private final Uri APP_PERFORMANCE_PROFILES_ENABLED_URI = + getUriFor(APP_PERFORMANCE_PROFILES_ENABLED); + + private final Uri PERFORMANCE_PROFILE_URI = + getUriFor(PERFORMANCE_PROFILE); + + private final ContentResolver mCR; + + public PerformanceSettingsObserver(Context context, Handler handler) { + super(handler); + mCR = context.getContentResolver(); + } + + public void observe(boolean enabled) { + if (enabled) { + mCR.registerContentObserver(APP_PERFORMANCE_PROFILES_ENABLED_URI, false, this); + mCR.registerContentObserver(PERFORMANCE_PROFILE_URI, false, this); + onChange(false); + + } else { + mCR.unregisterContentObserver(this); + } + } + + @Override + public void onChange(boolean selfChange) { + int profile = getInt(mCR, PERFORMANCE_PROFILE, PROFILE_BALANCED); + boolean boost = getInt(mCR, APP_PERFORMANCE_PROFILES_ENABLED, 1) == 1; + + synchronized (mLock) { + if (hasProfiles() && mProfiles.containsKey(profile)) { + boost = boost && mProfiles.get(profile).isBoostEnabled(); + } + + mBoostEnabled = boost; + if (mUserProfile < 0) { + mUserProfile = profile; + } + } + } + }; + @Override public String getFeatureDeclaration() { return CMContextConstants.Features.PERFORMANCE; @@ -115,85 +203,94 @@ public class PerformanceManagerService extends CMSystemService { publishLocalService(PerformanceManagerInternal.class, new LocalService()); } + private void populateProfilesLocked() { + mProfiles.clear(); + + Resources res = mContext.getResources(); + String[] profileNames = res.getStringArray(R.array.perf_profile_entries); + int[] profileIds = res.getIntArray(R.array.perf_profile_values); + String[] profileWeights = res.getStringArray(R.array.perf_profile_weights); + String[] profileDescs = res.getStringArray(R.array.perf_profile_summaries); + + for (int i = 0; i < profileIds.length; i++) { + if (profileIds[i] >= mNumProfiles) { + continue; + } + float weight = Float.valueOf(profileWeights[i]); + mProfiles.put(profileIds[i], new PerformanceProfile(profileIds[i], + weight, profileNames[i], profileDescs[i], shouldUseOptimizations(weight))); + } + } + @Override public void onBootPhase(int phase) { - if (phase == PHASE_SYSTEM_SERVICES_READY) { - synchronized (this) { + if (phase == PHASE_SYSTEM_SERVICES_READY && !mSystemReady) { + synchronized (mLock) { mPm = getLocalService(PowerManagerInternal.class); mNumProfiles = mPm.getFeature(POWER_FEATURE_SUPPORTED_PROFILES); - if (mNumProfiles > 0) { - int profile = getUserProfile(); - if (profile == PerformanceManager.PROFILE_HIGH_PERFORMANCE) { - Slog.i(TAG, String.format("Reverting profile %d to %d", - profile, PerformanceManager.PROFILE_BALANCED)); - setPowerProfileInternal( - PerformanceManager.PROFILE_BALANCED, true); + + if (hasProfiles()) { + populateProfilesLocked(); + + mObserver = new PerformanceSettingsObserver(mContext, mHandler); + mObserver.observe(true); + } + + mSystemReady = true; + + if (hasProfiles()) { + if (mUserProfile == PROFILE_HIGH_PERFORMANCE) { + Slog.w(TAG, "Reverting profile HIGH_PERFORMANCE to BALANCED"); + setPowerProfileLocked(PROFILE_BALANCED, true); } else { - setPowerProfileInternal(profile, false); + setPowerProfileLocked(mUserProfile, true); } mPm.registerLowPowerModeObserver(mLowPowerModeListener); + mContext.registerReceiver(mLocaleChangedReceiver, + new IntentFilter(Intent.ACTION_LOCALE_CHANGED)); } } } } + private boolean hasProfiles() { + return mNumProfiles > 0; + } + private boolean hasAppProfiles() { - return mNumProfiles > 0 && mPatterns != null && - (CMSettings.Secure.getInt(mContext.getContentResolver(), - CMSettings.Secure.APP_PERFORMANCE_PROFILES_ENABLED, 1) == 1); - } - - private boolean getProfileHasAppProfilesInternal(int profile) { - if (profile < 0 || profile > mNumProfiles) { - Slog.e(TAG, "Invalid profile: " + profile); - return false; - } - - if (profile == PerformanceManager.PROFILE_BALANCED) { - return mPatterns != null; - } - - return false; - } - - /** - * Get the profile saved by the user - */ - private int getUserProfile() { - return CMSettings.Secure.getInt(mContext.getContentResolver(), - CMSettings.Secure.PERFORMANCE_PROFILE, - PerformanceManager.PROFILE_BALANCED); + return hasProfiles() && mBoostEnabled && mAppProfiles.size() > 0; } /** * Apply a power profile and persist if fromUser = true + *

+ * Must call with lock held. * - * @param profile power profile - * @param fromUser true to persist the profile - * @return true if the active profile changed + * @param profile power profile + * @param fromUser true to persist the profile + * @return true if the active profile changed */ - private synchronized boolean setPowerProfileInternal(int profile, boolean fromUser) { + private boolean setPowerProfileLocked(int profile, boolean fromUser) { if (DEBUG) { - Slog.v(TAG, String.format( - "setPowerProfileInternal(profile=%d, fromUser=%b)", - profile, fromUser)); + Slog.v(TAG, String.format("setPowerProfileL(%d, fromUser=%b)", profile, fromUser)); } - if (mPm == null) { + + if (!mSystemReady) { Slog.e(TAG, "System is not ready, dropping profile request"); return false; } - if (profile < 0 || profile > mNumProfiles) { + + if (!mProfiles.containsKey(profile)) { Slog.e(TAG, "Invalid profile: " + profile); return false; } - boolean isProfileSame = profile == mCurrentProfile; + boolean isProfileSame = profile == mActiveProfile; if (!isProfileSame) { - if (profile == PerformanceManager.PROFILE_POWER_SAVE) { - // Handle the case where toggle power saver mode - // failed + if (profile == PROFILE_POWER_SAVE) { + // Handle the case where toggle power saver mode failed long token = Binder.clearCallingIdentity(); try { if (!mPm.setPowerSaveMode(true)) { @@ -202,7 +299,7 @@ public class PerformanceManagerService extends CMSystemService { } finally { Binder.restoreCallingIdentity(token); } - } else if (mCurrentProfile == PerformanceManager.PROFILE_POWER_SAVE) { + } else if (mActiveProfile == PROFILE_POWER_SAVE) { long token = Binder.clearCallingIdentity(); mPm.setPowerSaveMode(false); Binder.restoreCallingIdentity(token); @@ -215,8 +312,8 @@ public class PerformanceManagerService extends CMSystemService { * early if there is no work to be done. */ if (fromUser) { - CMSettings.Secure.putInt(mContext.getContentResolver(), - CMSettings.Secure.PERFORMANCE_PROFILE, profile); + putInt(mContext.getContentResolver(), PERFORMANCE_PROFILE, profile); + mUserProfile = profile; } if (isProfileSame) { @@ -229,10 +326,10 @@ public class PerformanceManagerService extends CMSystemService { long token = Binder.clearCallingIdentity(); - mCurrentProfile = profile; + mActiveProfile = profile; mHandler.obtainMessage(MSG_SET_PROFILE, profile, - (fromUser ? 1 : 0)).sendToTarget(); + (fromUser ? 1 : 0)).sendToTarget(); Binder.restoreCallingIdentity(token); @@ -240,69 +337,78 @@ public class PerformanceManagerService extends CMSystemService { } private int getProfileForActivity(String componentName) { + int profile = -1; if (componentName != null) { - for (int i = 0; i < mPatterns.length; i++) { - if (mPatterns[i].matcher(componentName).matches()) { - return mProfiles[i]; + for (Map.Entry entry : mAppProfiles.entrySet()) { + if (entry.getKey().matcher(componentName).matches()) { + profile = entry.getValue(); + break; } } } - return PerformanceManager.PROFILE_BALANCED; + if (DEBUG) { + Slog.d(TAG, "getProfileForActivity: activity=" + componentName + " profile=" + profile); + } + return profile < 0 ? mUserProfile : profile; + } + + private static boolean shouldUseOptimizations(float weight) { + return weight >= (WEIGHT_BALANCED / 2) && + weight <= (WEIGHT_BALANCED + (WEIGHT_BALANCED / 2)); } private void cpuBoostInternal(int duration) { - synchronized (PerformanceManagerService.this) { - if (mPm == null) { - Slog.e(TAG, "System is not ready, dropping cpu boost request"); - return; - } + if (!mSystemReady) { + Slog.e(TAG, "System is not ready, dropping cpu boost request"); + return; } + + if (!mBoostEnabled) { + return; + } + 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.obtainMessage(MSG_CPU_BOOST, duration, 0).sendToTarget(); } else { Slog.e(TAG, "Invalid boost duration: " + duration); } } - private void applyProfile(boolean fromUser) { - if (mNumProfiles < 1) { + private void applyAppProfileLocked(boolean fromUser) { + if (!hasProfiles()) { // don't have profiles, bail. return; } - int profile; + final int profile; if (mLowPowerModeEnabled) { // LPM always wins - profile = PerformanceManager.PROFILE_POWER_SAVE; - } else if (fromUser && mCurrentProfile == PerformanceManager.PROFILE_POWER_SAVE) { - profile = PerformanceManager.PROFILE_BALANCED; + profile = PROFILE_POWER_SAVE; + } else if (fromUser && mActiveProfile == PROFILE_POWER_SAVE) { + // leaving LPM + profile = PROFILE_BALANCED; + } else if (hasAppProfiles()) { + profile = getProfileForActivity(mCurrentActivityName); } else { - profile = getUserProfile(); - // use app specific rules if profile is balanced - if (hasAppProfiles() && getProfileHasAppProfilesInternal(profile)) { - profile = getProfileForActivity(mCurrentActivityName); - } + profile = mUserProfile; } - setPowerProfileInternal(profile, fromUser); + + setPowerProfileLocked(profile, fromUser); } private final IBinder mBinder = new IPerformanceManager.Stub() { @Override public boolean setPowerProfile(int profile) { - return setPowerProfileInternal(profile, true); + synchronized (mLock) { + return setPowerProfileLocked(profile, true); + } } /** * Boost the CPU * * @param duration Duration to boost the CPU for, in milliseconds. - * @hide */ @Override public void cpuBoost(int duration) { @@ -311,7 +417,23 @@ public class PerformanceManagerService extends CMSystemService { @Override public int getPowerProfile() { - return getUserProfile(); + synchronized (mLock) { + return mUserProfile; + } + } + + @Override + public PerformanceProfile getPowerProfileById(int profile) { + synchronized (mLock) { + return mProfiles.get(profile); + } + } + + @Override + public PerformanceProfile getActivePowerProfile() { + synchronized (mLock) { + return mProfiles.get(mUserProfile); + } } @Override @@ -320,8 +442,45 @@ public class PerformanceManagerService extends CMSystemService { } @Override - public boolean getProfileHasAppProfiles(int profile) { - return getProfileHasAppProfilesInternal(profile); + public PerformanceProfile[] getPowerProfiles() throws RemoteException { + synchronized (mLock) { + return mProfiles.values().toArray( + new PerformanceProfile[mProfiles.size()]); + } + } + + @Override + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); + + synchronized (mLock) { + pw.println(); + pw.println("PerformanceManager Service State:"); + pw.println(); + pw.println(" Boost enabled: " + mBoostEnabled); + + if (!hasProfiles()) { + pw.println(" No profiles available."); + } else { + pw.println(" User-selected profile: " + + Objects.toString(mProfiles.get(mUserProfile))); + if (mUserProfile != mActiveProfile) { + pw.println(" System-selected profile: " + + Objects.toString(mProfiles.get(mActiveProfile))); + } + pw.println(); + pw.println(" Supported profiles:"); + for (Map.Entry profile : mProfiles.entrySet()) { + pw.println(" " + profile.getKey() + ": " + profile.getValue().toString()); + } + if (hasAppProfiles()) { + pw.println(); + pw.println(" App trigger count: " + mAppProfiles.size()); + } + pw.println(); + mBoostLog.dump(pw); + } + } } }; @@ -334,15 +493,11 @@ public class PerformanceManagerService extends CMSystemService { @Override public void launchBoost(int pid, String packageName) { - synchronized (PerformanceManagerService.this) { - if (mPm == null) { - Slog.e(TAG, "System is not ready, dropping launch boost request"); - return; - } + if (!mSystemReady) { + Slog.e(TAG, "System is not ready, dropping launch boost request"); + return; } - // Don't send boosts if we're in another power profile - if (mCurrentProfile == PerformanceManager.PROFILE_POWER_SAVE || - mCurrentProfile == PerformanceManager.PROFILE_HIGH_PERFORMANCE) { + if (!mBoostEnabled) { return; } mHandler.obtainMessage(MSG_LAUNCH_BOOST, pid, 0, packageName).sendToTarget(); @@ -358,20 +513,65 @@ public class PerformanceManagerService extends CMSystemService { } } - mCurrentActivityName = activityName; - applyProfile(false); + synchronized (mLock) { + mCurrentActivityName = activityName; + applyAppProfileLocked(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; + private static class BoostLog { + static final int APP_PROFILE = 0; + static final int CPU_BOOST = 1; + static final int LAUNCH_BOOST = 2; + static final int USER_PROFILE = 3; + + static final String[] EVENTS = new String[] { + "APP_PROFILE", "CPU_BOOST", "LAUNCH_BOOST", "USER_PROFILE" }; + + private static final int LOG_BUF_SIZE = 25; + + static class Entry { + private final long timestamp; + private final int event; + private final String info; + + Entry(long timestamp_, int event_, String info_) { + timestamp = timestamp_; + event = event_; + info = info_; + } + } + + private final ArrayDeque mBuffer = new ArrayDeque<>(LOG_BUF_SIZE); + + void log(int event, String info) { + synchronized (mBuffer) { + mBuffer.add(new Entry(System.currentTimeMillis(), event, info)); + if (mBuffer.size() >= LOG_BUF_SIZE) { + mBuffer.poll(); + } + } + } + + void dump(PrintWriter pw) { + synchronized (mBuffer) { + pw.println(" Boost log:"); + for (Entry entry : mBuffer) { + pw.println(String.format(" %1$tH:%1$tM:%1$tS.%1$tL: %2$14s %3$s", + new Date(entry.timestamp), EVENTS[entry.event], entry.info)); + } + pw.println(); + } + } + } /** * Handler for asynchronous operations performed by the performance manager. */ - private final class PerformanceManagerHandler extends Handler { - public PerformanceManagerHandler(Looper looper) { + private final class BoostHandler extends Handler { + + public BoostHandler(Looper looper) { super(looper, null, true /*async*/); } @@ -380,6 +580,7 @@ public class PerformanceManagerService extends CMSystemService { switch (msg.what) { case MSG_CPU_BOOST: mPm.powerHint(POWER_HINT_CPU_BOOST, msg.arg1); + mBoostLog.log(BoostLog.CPU_BOOST, "duration=" + msg.arg1); break; case MSG_LAUNCH_BOOST: int pid = msg.arg1; @@ -387,9 +588,12 @@ public class PerformanceManagerService extends CMSystemService { if (NativeHelper.isNativeLibraryAvailable() && packageName != null) { native_launchBoost(pid, packageName); } + mBoostLog.log(BoostLog.LAUNCH_BOOST, "package=" + packageName); break; case MSG_SET_PROFILE: mPm.powerHint(POWER_HINT_SET_PROFILE, msg.arg1); + mBoostLog.log((msg.arg2 == 1 ? BoostLog.USER_PROFILE : BoostLog.APP_PROFILE), + "profile=" + msg.arg1); break; } } @@ -400,16 +604,27 @@ public class PerformanceManagerService extends CMSystemService { @Override public void onLowPowerModeChanged(boolean enabled) { - if (enabled == mLowPowerModeEnabled) { - return; + synchronized (mLock) { + if (enabled == mLowPowerModeEnabled) { + return; + } + if (DEBUG) { + Slog.d(TAG, "low power mode enabled: " + enabled); + } + mLowPowerModeEnabled = enabled; + applyAppProfileLocked(true); } - if (DEBUG) { - Slog.d(TAG, "low power mode enabled: " + enabled); - } - mLowPowerModeEnabled = enabled; - applyProfile(true); } }; + private final BroadcastReceiver mLocaleChangedReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + synchronized (mLock) { + populateProfilesLocked(); + } + } + }; + private native final void native_launchBoost(int pid, String packageName); } diff --git a/cm/res/res/values/arrays.xml b/cm/res/res/values/arrays.xml index 13d05734..44d29c6a 100644 --- a/cm/res/res/values/arrays.xml +++ b/cm/res/res/values/arrays.xml @@ -36,6 +36,26 @@ 2 + + + 0.0 + 0.25 + 0.5 + 0.75 + 1.0 + + + + + @string/perf_profile_pwrsv_summary + @string/perf_profile_bias_power_summary + @string/perf_profile_bal_summary + @string/perf_profile_bias_perf_summary + @string/perf_profile_perf_summary + + + @string/live_display_auto diff --git a/cm/res/res/values/strings.xml b/cm/res/res/values/strings.xml index 538d2ed9..504cd2df 100644 --- a/cm/res/res/values/strings.xml +++ b/cm/res/res/values/strings.xml @@ -107,6 +107,14 @@ Efficiency Quick + + Power save + Balanced + Performance + Efficiency + Quick + + LiveDisplay Automatic diff --git a/cm/res/res/values/symbols.xml b/cm/res/res/values/symbols.xml index 395b9366..a547bc87 100644 --- a/cm/res/res/values/symbols.xml +++ b/cm/res/res/values/symbols.xml @@ -30,11 +30,18 @@ + + + + + + + diff --git a/sdk/src/java/cyanogenmod/power/IPerformanceManager.aidl b/sdk/src/java/cyanogenmod/power/IPerformanceManager.aidl index bf44ac99..3017b1cc 100644 --- a/sdk/src/java/cyanogenmod/power/IPerformanceManager.aidl +++ b/sdk/src/java/cyanogenmod/power/IPerformanceManager.aidl @@ -16,6 +16,8 @@ package cyanogenmod.power; +import cyanogenmod.power.PerformanceProfile; + /** @hide */ interface IPerformanceManager { @@ -27,5 +29,9 @@ interface IPerformanceManager { int getNumberOfProfiles(); - boolean getProfileHasAppProfiles(int profile); + PerformanceProfile[] getPowerProfiles(); + + PerformanceProfile getPowerProfileById(int profile); + + PerformanceProfile getActivePowerProfile(); } diff --git a/sdk/src/java/cyanogenmod/power/PerformanceManager.java b/sdk/src/java/cyanogenmod/power/PerformanceManager.java index baf1fb96..66c36efe 100644 --- a/sdk/src/java/cyanogenmod/power/PerformanceManager.java +++ b/sdk/src/java/cyanogenmod/power/PerformanceManager.java @@ -21,6 +21,11 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.util.Log; +import java.util.Arrays; +import java.util.Collections; +import java.util.SortedSet; +import java.util.TreeSet; + import cyanogenmod.app.CMContextConstants; /** @@ -177,10 +182,31 @@ public class PerformanceManager { return changed; } + /** + * Set the system power profile + * + * @throws IllegalArgumentException if invalid + */ + public boolean setPowerProfile(PerformanceProfile profile) { + if (mNumberOfProfiles < 1) { + throw new IllegalArgumentException("Power profiles not enabled on this system!"); + } + + boolean changed = false; + try { + if (checkService()) { + changed = sService.setPowerProfile(profile.getId()); + } + } catch (RemoteException e) { + throw new IllegalArgumentException(e); + } + return changed; + } + /** * Gets the current power profile * - * Returns null if power profiles are not enabled + * Returns -1 if power profiles are not enabled */ public int getPowerProfile() { int ret = -1; @@ -196,6 +222,43 @@ public class PerformanceManager { return ret; } + /** + * Gets the specified power profile + * + * Returns null if power profiles are not enabled or the profile was not found + */ + public PerformanceProfile getPowerProfile(int profile) { + PerformanceProfile ret = null; + if (mNumberOfProfiles > 0) { + try { + if (checkService()) { + ret = sService.getPowerProfileById(profile); + } + } catch (RemoteException e) { + // nothing + } + } + return ret; + } + + /** + * Gets the currently active performance profile + * + * Returns null if no profiles are available. + */ + public PerformanceProfile getActivePowerProfile() { + PerformanceProfile ret = null; + if (mNumberOfProfiles > 0) { + try { + if (checkService()) { + ret = sService.getActivePowerProfile(); + } + } catch (RemoteException e) { + // nothing + } + } + return ret; + } /** * Check if profile has app-specific profiles * @@ -206,7 +269,7 @@ public class PerformanceManager { if (mNumberOfProfiles > 0) { try { if (checkService()) { - ret = sService.getProfileHasAppProfiles(profile); + ret = sService.getPowerProfileById(profile).isBoostEnabled(); } } catch (RemoteException e) { // nothing @@ -214,4 +277,26 @@ public class PerformanceManager { } return ret; } + + /** + * Gets a set, sorted by weight, of all supported power profiles + * + * Returns an empty set if power profiles are not enabled + */ + public SortedSet getPowerProfiles() { + final SortedSet profiles = new TreeSet(); + if (mNumberOfProfiles > 0) { + try { + if (checkService()) { + PerformanceProfile[] p = sService.getPowerProfiles(); + if (p != null) { + profiles.addAll(Arrays.asList(p)); + } + } + } catch (RemoteException e) { + // nothing + } + } + return Collections.unmodifiableSortedSet(profiles); + } } diff --git a/sdk/src/java/cyanogenmod/power/PerformanceProfile.aidl b/sdk/src/java/cyanogenmod/power/PerformanceProfile.aidl new file mode 100644 index 00000000..94cb8db7 --- /dev/null +++ b/sdk/src/java/cyanogenmod/power/PerformanceProfile.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2016 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; + +parcelable PerformanceProfile; diff --git a/sdk/src/java/cyanogenmod/power/PerformanceProfile.java b/sdk/src/java/cyanogenmod/power/PerformanceProfile.java new file mode 100644 index 00000000..b88aded0 --- /dev/null +++ b/sdk/src/java/cyanogenmod/power/PerformanceProfile.java @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2016 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.os.Parcel; +import android.os.Parcelable; + +import java.util.Objects; + +import cyanogenmod.os.Build; +import cyanogenmod.os.Concierge; + +/** + * Encapsulates information about an available system power/peformance profile, managed + * by the PerformanceManager. + */ +public class PerformanceProfile implements Parcelable, Comparable { + + private final int mId; + + private final float mWeight; + + private final String mName; + + private final String mDescription; + + private final boolean mBoostEnabled; + + public PerformanceProfile(int id, float weight, String name, String description, + boolean boostEnabled) { + mId = id; + mWeight = weight; + mName = name; + mDescription = description; + mBoostEnabled = boostEnabled; + } + + private PerformanceProfile(Parcel in) { + Concierge.ParcelInfo parcelInfo = Concierge.receiveParcel(in); + int parcelableVersion = parcelInfo.getParcelVersion(); + + mId = in.readInt(); + mWeight = in.readFloat(); + mName = in.readString(); + mDescription = in.readString(); + mBoostEnabled = in.readInt() == 1; + + if (parcelableVersion >= Build.CM_VERSION_CODES.GUAVA) { + // nothing yet + } + + parcelInfo.complete(); + } + + /** + * Unique identifier for this profile. Must match values used by the PowerHAL. + * + * @return the id + */ + public int getId() { + return mId; + } + + /** + * The profile's weight, from 0 to 1, with 0 being lowest (power save), 1 being + * highest (performance), and 0.5 as the balanced default profile. Other + * values may be seen, depending on the device. This value can be used for + * sorting. + * + * @return weight + */ + public float getWeight() { + return mWeight; + } + + /** + * A localized name for the profile, suitable for display. + * + * @return name + */ + public String getName() { + return mName; + } + + /** + * A localized description of the profile, suitable for display. + * + * @return description + */ + public String getDescription() { + return mDescription; + } + + /** + * Whether or not per-app profiles and boosting will be used when this + * profile is active. Far-end modes (powersave / high performance) do + * not use boosting. + * + * @return true if boosting and per-app optimization will be used + */ + public boolean isBoostEnabled() { + return mBoostEnabled; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + Concierge.ParcelInfo parcelInfo = Concierge.prepareParcel(dest); + + dest.writeInt(mId); + dest.writeFloat(mWeight); + dest.writeString(mName); + dest.writeString(mDescription); + dest.writeInt(mBoostEnabled ? 1 : 0); + + parcelInfo.complete(); + } + + public static final Creator CREATOR = new Creator() { + @Override + public PerformanceProfile createFromParcel(Parcel in) { + return new PerformanceProfile(in); + } + + @Override + public PerformanceProfile[] newArray(int size) { + return new PerformanceProfile[size]; + } + }; + + @Override + public int compareTo(PerformanceProfile other) { + return Float.compare(mWeight, other.mWeight); + } + + @Override + public boolean equals(Object other) { + if (other == null) { + return false; + } + if (!getClass().equals(other.getClass())) { + return false; + } + + PerformanceProfile o = (PerformanceProfile) other; + return Objects.equals(mId, o.mId); + } + + @Override + public int hashCode() { + return Objects.hash(mId); + } + + @Override + public String toString() { + return String.format("PerformanceProfile[id=%d, weight=%f, name=%s desc=%s " + + "boostEnabled=%b]", mId, mWeight, mName, mDescription, mBoostEnabled); + } +} diff --git a/tests/src/org/cyanogenmod/tests/power/unit/PerfomanceManagerTest.java b/tests/src/org/cyanogenmod/tests/power/unit/PerfomanceManagerTest.java index 8d9f6277..17e24110 100644 --- a/tests/src/org/cyanogenmod/tests/power/unit/PerfomanceManagerTest.java +++ b/tests/src/org/cyanogenmod/tests/power/unit/PerfomanceManagerTest.java @@ -21,6 +21,7 @@ import android.test.suitebuilder.annotation.SmallTest; import cyanogenmod.app.CMContextConstants; import cyanogenmod.power.IPerformanceManager; import cyanogenmod.power.PerformanceManager; +import cyanogenmod.power.PerformanceProfile; /** * Code coverage for public facing {@link PerformanceManager} interfaces. @@ -31,7 +32,7 @@ public class PerfomanceManagerTest extends AndroidTestCase { private static final String TAG = PerfomanceManagerTest.class.getSimpleName(); private static final int IMPOSSIBLE_POWER_PROFILE = -1; private PerformanceManager mCMPerformanceManager; - private int mSavedPerfProfile; + private PerformanceProfile mSavedPerfProfile; @Override protected void setUp() throws Exception { @@ -41,7 +42,8 @@ public class PerfomanceManagerTest extends AndroidTestCase { CMContextConstants.Features.PERFORMANCE)); mCMPerformanceManager = PerformanceManager.getInstance(mContext); // Save the perf profile for later restore. - mSavedPerfProfile = mCMPerformanceManager.getPowerProfile(); + mSavedPerfProfile = mCMPerformanceManager.getPowerProfile( + mCMPerformanceManager.getPowerProfile()); } @SmallTest @@ -104,6 +106,6 @@ public class PerfomanceManagerTest extends AndroidTestCase { protected void tearDown() throws Exception { super.tearDown(); // Reset - mCMPerformanceManager.setPowerProfile(mSavedPerfProfile); + mCMPerformanceManager.setPowerProfile(mSavedPerfProfile.getId()); } }