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
This commit is contained in:
Steve Kondik
2016-10-12 04:06:33 -07:00
parent 7928707006
commit cd98e0b30b
10 changed files with 690 additions and 135 deletions

View File

@@ -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<cyanogenmod.power.PerformanceProfile> 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<cyanogenmod.power.PerformanceProfile> CREATOR;
}
}
package cyanogenmod.preference {

View File

@@ -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<Pattern, Integer> mAppProfiles = new LinkedHashMap<>();
private final ArrayMap<Integer, PerformanceProfile> 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
* <p>
* 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<Pattern, Integer> 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<Integer, PerformanceProfile> 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<Entry> 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);
}

View File

@@ -36,6 +36,26 @@
<item>2</item>
</integer-array>
<!-- Weights for each performance profile, 0 being lowest (powersave) and 1 being
highest (performance). Used for sorting -->
<string-array name="perf_profile_weights" translatable="false">
<item>0.0</item>
<item>0.25</item>
<item>0.5</item>
<item>0.75</item>
<item>1.0</item>
</string-array>
<!-- Summary of each profile -->
<string-array name="perf_profile_summaries" translatable="false">
<item>@string/perf_profile_pwrsv_summary</item>
<item>@string/perf_profile_bias_power_summary</item>
<item>@string/perf_profile_bal_summary</item>
<item>@string/perf_profile_bias_perf_summary</item>
<item>@string/perf_profile_perf_summary</item>
</string-array>
<!-- LiveDisplay -->
<string-array name="live_display_entries" translatable="false">
<item>@string/live_display_auto</item>

View File

@@ -107,6 +107,14 @@
<string name="perf_profile_bias_power">Efficiency</string>
<string name="perf_profile_bias_perf">Quick</string>
<!-- Performance profiles -->
<string name="perf_profile_pwrsv_summary">Power save</string>
<string name="perf_profile_bal_summary">Balanced</string>
<string name="perf_profile_perf_summary">Performance</string>
<string name="perf_profile_bias_power_summary">Efficiency</string>
<string name="perf_profile_bias_perf_summary">Quick</string>
<!-- LiveDisplay strings -->
<string name="live_display_title" translatable="false">LiveDisplay</string>
<string name="live_display_auto">Automatic</string>

View File

@@ -30,11 +30,18 @@
<!-- Performance Profiles -->
<java-symbol type="array" name="perf_profile_entries" />
<java-symbol type="array" name="perf_profile_values" />
<java-symbol type="array" name="perf_profile_weights" />
<java-symbol type="array" name="perf_profile_summaries" />
<java-symbol type="string" name="perf_profile_pwrsv" />
<java-symbol type="string" name="perf_profile_bal" />
<java-symbol type="string" name="perf_profile_perf" />
<java-symbol type="string" name="perf_profile_bias_power" />
<java-symbol type="string" name="perf_profile_bias_perf" />
<java-symbol type="string" name="perf_profile_pwrsv_summary" />
<java-symbol type="string" name="perf_profile_bal_summary" />
<java-symbol type="string" name="perf_profile_perf_summary" />
<java-symbol type="string" name="perf_profile_bias_power_summary" />
<java-symbol type="string" name="perf_profile_bias_perf_summary" />
<!-- Array of default activities with custom power management -->
<java-symbol type="array" name="config_auto_perf_activities" />

View File

@@ -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();
}

View File

@@ -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<PerformanceProfile> getPowerProfiles() {
final SortedSet<PerformanceProfile> profiles = new TreeSet<PerformanceProfile>();
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);
}
}

View File

@@ -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;

View File

@@ -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<PerformanceProfile> {
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<PerformanceProfile> CREATOR = new Creator<PerformanceProfile>() {
@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);
}
}

View File

@@ -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());
}
}