背景
以前直接用scrcpy代码进行屏幕关闭,但后来手机系统升级版本没有效果了,我也没有更新代码,就一直没有管他,一直没有弄清楚的他的原理,他这个最神器地方,屏幕关闭应用还是正常跑。
思考
如果要用弄清楚关闭屏幕代码就从原来的调用函数分析代码引用
过程
一、它使用的SurfaceControl.setDisplayPowerMode 调用从而关闭屏幕
二、找他们有关的介绍
这个就说明屏幕灭屏的有关系,那么基本推测,scrcpy根据分析android 灭屏流程分析得出来调用这个函数
三、通过源代码搜索setDisplayPowerMode的引用,找到有关系的代码
Search (androidxref.com)
找到发现在LocalDisplayAdapter.java 中调用,分析这个文件可以找到几个关键函数
//这个函数可以到通过displayid 互殴displaytoken,这个就是函数关键
private void tryConnectDisplayLocked(int builtInDisplayId) {
IBinder displayToken = SurfaceControl.getBuiltInDisplay(builtInDisplayId);
if (displayToken != null) {
SurfaceControl.PhysicalDisplayInfo[] configs =
SurfaceControl.getDisplayConfigs(displayToken);
if (configs == null) {
// There are no valid configs for this device, so we can't use it
Slog.w(TAG, "No valid configs found for display device " +
builtInDisplayId);
return;
}
int activeConfig = SurfaceControl.getActiveConfig(displayToken);
if (activeConfig < 0) {
// There is no active config, and for now we don't have the
// policy to set one.
Slog.w(TAG, "No active config found for display device " +
builtInDisplayId);
return;
}
int activeColorMode = SurfaceControl.getActiveColorMode(displayToken);
if (activeColorMode < 0) {
// We failed to get the active color mode. We don't bail out here since on the next
// configuration pass we'll go ahead and set it to whatever it was set to last (or
// COLOR_MODE_NATIVE if this is the first configuration).
Slog.w(TAG, "Unable to get active color mode for display device " +
builtInDisplayId);
activeColorMode = Display.COLOR_MODE_INVALID;
}
int[] colorModes = SurfaceControl.getDisplayColorModes(displayToken);
LocalDisplayDevice device = mDevices.get(builtInDisplayId);
if (device == null) {
// Display was added.
device = new LocalDisplayDevice(displayToken, builtInDisplayId,
configs, activeConfig, colorModes, activeColorMode);
mDevices.put(builtInDisplayId, device);
sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_ADDED);
} else if (device.updatePhysicalDisplayInfoLocked(configs, activeConfig,
colorModes, activeColorMode)) {
// Display properties changed.
sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_CHANGED);
}
} else {
// The display is no longer available. Ignore the attempt to add it.
// If it was connected but has already been disconnected, we'll get a
// disconnect event that will remove it from mDevices.
}
}
//这个设置屏幕状态
public Runnable requestDisplayStateLocked(final int state, final int brightness) {
// Assume that the brightness is off if the display is being turned off.
assert state != Display.STATE_OFF || brightness == PowerManager.BRIGHTNESS_OFF;
final boolean stateChanged = (mState != state);
final boolean brightnessChanged = (mBrightness != brightness) && mBacklight != null;
if (stateChanged || brightnessChanged) {
final int displayId = mBuiltInDisplayId;
final IBinder token = getDisplayTokenLocked();
final int oldState = mState;
if (stateChanged) {
mState = state;
updateDeviceInfoLocked();
}
if (brightnessChanged) {
mBrightness = brightness;
}
// Defer actually setting the display state until after we have exited
// the critical section since it can take hundreds of milliseconds
// to complete.
return new Runnable() {
@Override
public void run() {
// Exit a suspended state before making any changes.
int currentState = oldState;
if (Display.isSuspendedState(oldState)
|| oldState == Display.STATE_UNKNOWN) {
if (!Display.isSuspendedState(state)) {
setDisplayState(state);
currentState = state;
} else if (state == Display.STATE_DOZE_SUSPEND
|| oldState == Display.STATE_DOZE_SUSPEND) {
setDisplayState(Display.STATE_DOZE);
currentState = Display.STATE_DOZE;
} else if (state == Display.STATE_ON_SUSPEND
|| oldState == Display.STATE_ON_SUSPEND) {
setDisplayState(Display.STATE_ON);
currentState = Display.STATE_ON;
} else {
return; // old state and new state is off
}
}
// If the state change was from or to VR, then we need to tell the light
// so that it can apply appropriate VR brightness settings. Also, update the
// brightness so the state is propogated to light.
boolean vrModeChange = false;
if ((state == Display.STATE_VR || currentState == Display.STATE_VR) &&
currentState != state) {
setVrMode(state == Display.STATE_VR);
vrModeChange = true;
}
// Apply brightness changes given that we are in a non-suspended state.
if (brightnessChanged || vrModeChange) {
setDisplayBrightness(brightness);
}
// Enter the final desired state, possibly suspended.
if (state != currentState) {
setDisplayState(state);
}
}
private void setVrMode(boolean isVrEnabled) {
if (DEBUG) {
Slog.d(TAG, "setVrMode("
+ "id=" + displayId
+ ", state=" + Display.stateToString(state) + ")");
}
mBacklight.setVrMode(isVrEnabled);
}
private void setDisplayState(int state) {
if (DEBUG) {
Slog.d(TAG, "setDisplayState("
+ "id=" + displayId
+ ", state=" + Display.stateToString(state) + ")");
}
// We must tell sidekick to stop controlling the display before we
// can change its power mode, so do that first.
if (mSidekickActive) {
Trace.traceBegin(Trace.TRACE_TAG_POWER,
"SidekickInternal#endDisplayControl");
try {
mSidekickInternal.endDisplayControl();
} finally {
Trace.traceEnd(Trace.TRACE_TAG_POWER);
}
mSidekickActive = false;
}
final int mode = getPowerModeForState(state);
Trace.traceBegin(Trace.TRACE_TAG_POWER, "setDisplayState("
+ "id=" + displayId
+ ", state=" + Display.stateToString(state) + ")");
try {
SurfaceControl.setDisplayPowerMode(token, mode);
Trace.traceCounter(Trace.TRACE_TAG_POWER, "DisplayPowerMode", mode);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_POWER);
}
// If we're entering a suspended (but not OFF) power state and we
// have a sidekick available, tell it now that it can take control.
if (Display.isSuspendedState(state) && state != Display.STATE_OFF
&& mSidekickInternal != null && !mSidekickActive) {
Trace.traceBegin(Trace.TRACE_TAG_POWER,
"SidekickInternal#startDisplayControl");
try {
mSidekickActive = mSidekickInternal.startDisplayControl(state);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_POWER);
}
}
}
private void setDisplayBrightness(int brightness) {
if (DEBUG) {
Slog.d(TAG, "setDisplayBrightness("
+ "id=" + displayId + ", brightness=" + brightness + ")");
}
Trace.traceBegin(Trace.TRACE_TAG_POWER, "setDisplayBrightness("
+ "id=" + displayId + ", brightness=" + brightness + ")");
try {
mBacklight.setBrightness(brightness);
Trace.traceCounter(Trace.TRACE_TAG_POWER,
"ScreenBrightness", brightness);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_POWER);
}
}
};
}
return null;
}
所以scrcpy反射用到获取displaytoken 代码是上面第一个函数得出来的,你可以分析不同版本android代码,你就知道怎么适配对应版本的代码
你去搜搜LocalDisplayAdapter就有很多分析,找到很多资料,这个也是侧面验证分析逻辑是通过代码分析得到的
总结
android实现一些特别的功能基本通过分析源代码流程得出来的,这个挺有意思的,我之前看scrcpy实现点击等等,也是通过类似方法找到源代码cmd实现逻辑,我后面就全面自己做适配,高版本cmd命令完全是通用的,可以简单构建就可以快速调用。
todo
准备自己适配android 14屏幕关闭