blob: 1a2e5f8a5bc71c0895337b18717bdeacf237e74a [file] [log] [blame]
package com.android.clockwork.power;
import android.app.AlarmManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.SystemProperties;
import android.util.Log;
import com.android.clockwork.common.EventHistory;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter;
/** This class computes preferred core control mode based on device interactive states. */
public class WearCoreControlMediator implements PowerTracker.Listener {
private static final String TAG = WearPowerConstants.LOG_TAG;
@VisibleForTesting static final String SYSPROP_CORE_CONTROL_ENABLE = "ro.enable_core_ctl";
@VisibleForTesting static final String SYSPROP_CORE_CONTROL_MODE = "sys.cw_corectl_mode";
@VisibleForTesting static final String MODE_PERF = "perf";
@VisibleForTesting static final String MODE_IDLE = "idle";
// Reasons for different core control mode sorted by decision priority
enum Reason {
// Performance mode when device just finished boot
PERF_BOOT_COMPLETE(true),
// Idle mode when PowerManager.isDeviceIdleMode() is true
IDLE_DEVICE_IDLE(false),
// Performance mode when screen is on
PERF_SCREEN_ON(true),
// Performance mode when charging
PERF_CHARGING(true),
// Idle mode when screen off
IDLE_SCREEN_OFF(false);
final boolean performanceMode;
Reason(boolean performanceMode) {
this.performanceMode = performanceMode;
}
}
private final EventHistory<CoreCtlDecision> mDecisionHistory =
new EventHistory<>("Core Control Decision History", 10, false);
private final Context mContext;
private final AlarmManager mAlarmManager;
private final PowerTracker mPowerTracker;
private boolean mScreenOn = true;
private boolean mIsCharging = false;
private boolean mDeviceIdle = false;
private boolean mPerformanceMode = true;
static boolean shouldEnable() {
return SystemProperties.getBoolean(SYSPROP_CORE_CONTROL_ENABLE, false);
}
public WearCoreControlMediator(Context context, PowerTracker powerTracker) {
this(context, context.getSystemService(AlarmManager.class), powerTracker);
}
@VisibleForTesting
public WearCoreControlMediator(
Context context, AlarmManager alarmManager, PowerTracker powerTracker) {
Log.d(TAG, "WearCoreControlMediator enabled");
mContext = context;
mAlarmManager = alarmManager;
// TODO(b/156985829): Use AlarmManager to pin and unpin performance mode.
mPowerTracker = powerTracker;
mPowerTracker.addListener(this);
}
private void updateCoreControlDecision() {
final boolean isCharging = mPowerTracker.isCharging();
final boolean isDeviceIdle = mPowerTracker.isDeviceIdle();
Reason reason = Reason.IDLE_SCREEN_OFF;
if (isDeviceIdle) {
reason = Reason.IDLE_DEVICE_IDLE;
} else if (mScreenOn) {
reason = Reason.PERF_SCREEN_ON;
} else if (isCharging) {
reason = Reason.PERF_CHARGING;
}
if (reason.performanceMode == mPerformanceMode) {
// De-dup state transition
return;
}
updateCoreControlDecision(reason);
}
private void updateCoreControlDecision(Reason reason) {
mPerformanceMode = reason.performanceMode;
SystemProperties.set(SYSPROP_CORE_CONTROL_MODE, mPerformanceMode ? MODE_PERF : MODE_IDLE);
mDecisionHistory.recordEvent(new CoreCtlDecision(reason));
}
public void onBootCompleted() {
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_SCREEN_ON);
intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
mContext.registerReceiver(receiver, intentFilter);
// TODO(b/156985829): Use AlarmManager to pin and unpin performance mode.
updateCoreControlDecision(Reason.PERF_BOOT_COMPLETE);
}
@Override
public void onDeviceIdleModeChanged() {
updateCoreControlDecision();
}
@Override
public void onChargingStateChanged() {
updateCoreControlDecision();
}
BroadcastReceiver receiver =
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, String.format("[WearCoreControl] received action: %s", action));
}
switch (action) {
case Intent.ACTION_SCREEN_ON:
mScreenOn = true;
break;
case Intent.ACTION_SCREEN_OFF:
mScreenOn = false;
break;
default:
return;
}
// Recompute decision
updateCoreControlDecision();
}
};
private class CoreCtlDecision extends EventHistory.Event {
private final Reason mReason;
public CoreCtlDecision(Reason reason) {
this.mReason = reason;
}
@Override
public String getName() {
return mReason.name();
}
}
public void dump(IndentingPrintWriter ipw) {
// Other states are already logged by WearPowerService.
ipw.println("======== WearCoreControlMediator ========");
ipw.println();
ipw.increaseIndent();
mDecisionHistory.dump(ipw);
ipw.decreaseIndent();
ipw.println();
}
}