近日接到一个比较诡异的产品需求,在我看来需要修改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.Stubdefault:
 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;
 + }

 

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