修改Android 休眠逻辑使系统关屏而不进入休眠

近日接到一个比较诡异的产品需求,在我看来需要修改Android PMS。

具体需求如下:

        要求在某一Activity界面在灭屏之后也能监听屏幕的手势事件,在我看来这种手势事件需求应该放在TP的驱动中来完成是最合理的,但是无奈硬件选型已过,而且手势不能完全支持我们的产品需求,无奈放弃底层的思路,开始改上层的电源管理逻辑。

        首先说一下我修改的思路和逻辑,PMS在走休眠流程的时候,判断一下topActivity是不是该特殊需求的Activity,然后再决定走不走常规休眠流程,如果是,则发一个广播给activity,让该activity去持有亮屏的wakelock,同时强行操作背光的亮度节点,直接关屏;不过不是,就走常规的休眠流程喽~~~

        再者,就是上一步提到系统进入休眠的代码段了,一种是PowerKey直接触发,一种是Timeout计时。


下边上我修改的PMS的代码部分:

diff --git a/frameworks/base/services/java/com/android/server/power/PowerManagerService.java b/frameworks/base/services/java/com/android/server/power/PowerManagerService.java
index 8dc0c7a..3e69a57 100755
--- a/frameworks/base/services/java/com/android/server/power/PowerManagerService.java
+++ b/frameworks/base/services/java/com/android/server/power/PowerManagerService.java
@@ -29,6 +29,7 @@ import com.android.server.display.DisplayManagerService;
import com.android.server.dreams.DreamManagerService;

import android.Manifest;
+import android.content.ComponentName;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
@@ -66,6 +67,8 @@ import android.util.TimeUtils;
import android.view.WindowManagerPolicy;
import android.view.Display;

+import android.app.ActivityManager;
+

import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
@@ -88,6 +91,9 @@ public final class PowerManagerService extends IPowerManager.Stub
implements Watchdog.Monitor {
private static final String TAG = "PowerManagerService";


+ static final String VINCI_MEDIA_PLAY_ACTIVITY = "ConnectActivity";
+ static boolean hasVinciWakelock = false;
+

private static final boolean DEBUG = true;
private static final boolean DEBUG_SPEW = DEBUG && true;

@@ -941,6 +947,8 @@ public final class PowerManagerService extends IPowerManager.Stub
applyWakeLockFlagsOnAcquireLocked(wakeLock);
mDirty |= DIRTY_WAKE_LOCKS;
updatePowerStateLocked();
+ if (tag != null && tag.contains("VINCI"))
+ hasVinciWakelock = true;

}
}

@@ -1011,6 +1019,8 @@ public final class PowerManagerService extends IPowerManager.Stub
applyWakeLockFlagsOnReleaseLocked(wakeLock);
mDirty |= DIRTY_WAKE_LOCKS;
updatePowerStateLocked();
+ if (wakeLock.mTag != null && (wakeLock.mTag).contains("VINCI"))
+ hasVinciWakelock = false;

}
}

@@ -1684,6 +1694,8 @@ public final class PowerManagerService extends IPowerManager.Stub

default:
Slog.i(TAG, "Going to sleep by user request...");
reason = PowerManager.GO_TO_SLEEP_REASON_USER;
+
+ if(ifSendNoSleepBroadcast()) return false;

break;
}

@@ -1727,6 +1739,21 @@ public final class PowerManagerService extends IPowerManager.Stub
return true;
}

+ private boolean ifSendNoSleepBroadcast() {
+ ActivityManager am = (ActivityManager)mContext.getSystemService(Context.ACTIVITY_SERVICE);
+ ComponentName cn = am.getRunningTasks(Integer.MAX_VALUE).get(0).topActivity;
+ if (null != cn && (cn.getClassName()).contains(VINCI_MEDIA_PLAY_ACTIVITY)) {
+ //not true sleep
+ Intent noSleepAction = new Intent("VINCI_MEDIA_PLAY_NOSLEEP_ACTION");
+ if (mContext != null) {
+ mContext.sendBroadcast(noSleepAction);
+ Log.e(TAG, "======I will send broadcast to top activity.\n");
+ return true;
+ }
+ }
+ return false;
+ }
+

@Override // Binder call
public void nap(long eventTime) {
if (eventTime > SystemClock.uptimeMillis()) {
@@ -1799,18 +1826,22 @@ public final class PowerManagerService extends IPowerManager.Stub

// Phase 0: Basic state updates.
updateIsPoweredLocked(mDirty);
+ Slog.e(TAG, "after updateIsPoweredLocked ======\n");
updateStayOnLocked(mDirty);
+ Slog.e(TAG, "after updateStayOnLocked ======\n");

// Phase 1: Update wakefulness.
// Loop because the wake lock and user activity computations are influenced
// by changes in wakefulness.
final long now = SystemClock.uptimeMillis();
int dirtyPhase2 = 0;
+ Slog.e(TAG, "updatePowerStateLocked before for loop ======\n");
for (;;) {
int dirtyPhase1 = mDirty;
dirtyPhase2 |= dirtyPhase1;
mDirty = 0;

+ Slog.e(TAG, "in for loop ======\n");
updateWakeLockSummaryLocked(dirtyPhase1);
updateUserActivitySummaryLocked(now, dirtyPhase1);
if (!updateWakefulnessLocked(dirtyPhase1)) {
@@ -2136,6 +2167,7 @@ else
Message msg = mHandler.obtainMessage(MSG_USER_ACTIVITY_TIMEOUT);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, nextTimeout);
+ Slog.e(TAG, "PMS send MSG_USER_ACTIVITY_TIMEOUT broadcast======\n");
}
} else {
mUserActivitySummary = 0;
@@ -2181,6 +2213,8 @@ else
Slog.d(TAG, "handleUserActivityTimeout");
}

+ if(!hasVinciWakelock && ifSendNoSleepBroadcast()) return;
+

mDirty |= DIRTY_USER_ACTIVITY;
updatePowerStateLocked();
} else {

WakeLock申请注册部分:

pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
mWakelock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "VINCI Wakelock");

filter.addAction("VINCI_MEDIA_PLAY_NOSLEEP_ACTION");

将上述三句添加至onCreate方法。


逻辑跟我之前所说的思路吻合, ifSendNoSleepBroadcast()这个方法的目的就是判断是否发送广播的时机,有两处改动中调用了,分别是PowerKey和Timeout两种情况的休眠代码段。hasVinciWakelock该值用来标示当前Activity有没有持有亮屏的wakelock,判断的条件是根据if (wakeLock.mTag != null && (wakeLock.mTag).contains("VINCI"))来,这就要求activity在申请锁的时候添加的tag字段和此处判断一致。


第二部,我们来看activity对屏幕及wake lock的处理逻辑添加。

} else if (mediaAction.equals(action)) {
try {
Process pcs = Runtime.getRuntime().exec("lcd value");

// 获取shell返回流
BufferedInputStream in = new BufferedInputStream(pcs.getInputStream());
// 字符流转换字节流
BufferedReader br = new BufferedReader(new InputStreamReader(in));
// 这里也可以输出文本日志

String lineStr;
while ((lineStr = br.readLine()) != null) {
result = lineStr;
}
br.close();
in.close();

Log.e(TAG, "==================" + result);
if (0 < Integer.valueOf(result).intValue()) {
if (!hasWakelock) {
Runtime.getRuntime().exec("lcd off");
Log.e(TAG, "exec lcd off\n");
mWakelock.acquire();
Log.e(TAG, "have wakelock\n");
hasWakelock = true;
}
} else {
if (hasWakelock) {
Runtime.getRuntime().exec("lcd on");
Log.e(TAG, "exec lcd on\n");
mWakelock.release();
Log.e(TAG, "wakelock release\n");
hasWakelock = false;
}
}
} catch (Exception e) {}
}
这里边涉及对一个lcd脚本的操作,稍后附上该脚本内容,切换屏幕亮度的值结合是否持有wakelock联合判断,因此需要处理脚本返回的亮度值,从而关屏或者恢复关屏之前的亮度值。


最后,附上脚本的内容,lcd命名的脚本,放置system/bin下即可。

#!/system/bin/sh

echo $1

light_path=/sys/class/leds/lcd-backlight/brightness

if [ $1 == "on" ];then
echo "lcd on"
echo 255 > $light_path
elif [ $1 == "off" ];then
echo "lcd off"
echo 0 > $light_path
elif [ $1 == "value" ];then
echo `cat $light_path`
fi

核心点:理清PMS休眠的流程逻辑,wakelcok相关操作,最后当然是shell脚本功底了。


补充一点:就是在系统休眠的状态下,app如何唤醒系统电量屏幕。

nWakelock = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK | PowerManager.ACQUIRE _CAUSES_WAKEUP,"screen_on");
nWakelock.acquire();
nWakelock.release();
就这么简单,在需要唤醒的时候按照上述TAG去初始化wakelock,然后来一次申请和释放操作,屏幕就能亮瞎你的狗眼了~~~


备注:

timeout的那种关屏广播可能会导致手指在滑动屏幕的时候就触发(屏幕在有用户事件时突然熄灭),修改的点需要改到updateUserActivitySummaryLocked方法:

在发Message msg = mHandler.obtainMessage(MSG_USER_ACTIVITY_TIMEOUT);之前调用下述逻辑:

if (isVinciMediaPlaying() && !isVinciFalseSleep()) {
+ if (0x02 == (mUserActivitySummary & USER_ACTIVITY_SCREEN_DIM)) {
+ ifSendNoSleepBroadcast();
+ mUserActivitySummary |= USER_ACTIVITY_SCREEN_BRIGHT;
+ }
+ //return;
+ }






大功告成~~~需要联测验证是否能达到需求哦,谨以此文,做工作记录,方便日后查验,供有类似需求的同仁参考。。。




























阅读更多
换一批

没有更多推荐了,返回首页