添加一个新物理按键
本文以SPRD平台为例
一、kernel修改
sprd.mocor12.androidS/bsp/kernel/kernel5.4/include/dt-bindings/input/linux-event-codes.h中定义新按键:
#define KEY_F18 188
#define KEY_F19 189
#define KEY_F20 190 //添加新按键,映射给Android 上层的也是此值
#define KEY_F21 191
#define KEY_F22 192
zprj\FS301L3\board_cfg\sp9863a-1h10-overlay.dts
/*AI_SIRI_START
key-google {
label = "Google Key";
linux,code = <190>;
gpios = <&ap_gpio 125 1>;
debounce-interval = <2>;
gpio-key,wakeup;
gpio-key,level-trigger;
};
AI_SIRI_END*/
添加成功后操作此GPIO,在adb 串口可使用getevent命令,get到其映射到上层的值190的十六进制值 为be
G0830WW:/ # getevent
/dev/input/event1: 0001 00be 00000001
/dev/input/event1: 0000 0000 00000000
/dev/input/event1: 0001 00be 00000000
/dev/input/event1: 0000 0000 00000000
其中:
00be: 是190的十六进制的值
1,0: 分别是按下和弹起的动作。
二、Android修改
- sprd.mocor12.androidS/device/sprd/mpool/module/keylayout/gpio-keys.kl
key 114 VOLUME_DOWN WAKE
key 115 VOLUME_UP WAKE
key 116 POWER WAKE
key 212 CAMERA WAKE
key 0x210 FOCUS WAKE
key 190 F20 //添加的新按键,此值为kernel映射到上层的键值
- sprd.mocor12.androidS/frameworks/base/core/java/android/view/KeyEvent.java
public static final int KEYCODE_THUMBS_DOWN = 287;
/**
* Key code constant: Used to switch current {@link android.accounts.Account} that is
* consuming content. May be consumed by system to set account globally.
*/
public static final int KEYCODE_PROFILE_SWITCH = 288;
public static final int KEYCODE_F20 = 289; //添加新的按键
/**
* Integer value of the last KEYCODE. Increases as new keycodes are added to KeyEvent.
* @hide
*/
@TestApi
public static final int LAST_KEYCODE = KEYCODE_F20;//将其修改为我们添加的最后一个按键KEYCODE_F20
此处源码下方有提示我们还需要修改哪些文件:
// NOTE: If you add a new keycode here you must also add it to:
// isSystem()
// isWakeKey()
// frameworks/native/include/android/keycodes.h
// frameworks/native/include/input/InputEventLabels.h
// frameworks/base/core/res/res/values/attrs.xml
// emulator?
// LAST_KEYCODE
//
// Also Android currently does not reserve code ranges for vendor-
// specific key codes. If you have new key codes to have, you
// MUST contribute a patch to the open source project to define
// those new codes. This is intended to maintain a consistent
// set of key code definitions across all Android devices.
// Symbolic names of all metakeys in bit order from least significant to most significant.
// Accordingly there are exactly 32 values in this table.
所以在KeyEvent.java文件中我们还需要修改 isSystem()、isWakeKey()如下
public static final boolean isSystemKey(int keyCode) {
switch (keyCode) {
........
case KeyEvent.KEYCODE_F20:
return true;
}
return false;
}
/** @hide */
public static final boolean isWakeKey(int keyCode) {
switch (keyCode) {
........
case KeyEvent.KEYCODE_F20:
return true;
}
return false;
}
- sprd.mocor12.androidS/frameworks/native/include/android/keycodes.h
enum {
........
AKEYCODE_PROFILE_SWITCH = 288,
/** 新增 */
AKEYCODE_F20 = 289
};
- sprd.mocor12.androidS/frameworks/native/libs/input/InputEventLabels.cpp
#define KEYCODES_SEQUENCE \
.........
DEFINE_KEYCODE(F20)
- sprd.mocor12.androidS/frameworks/base/core/res/res/values/attrs.xml
<attr name="keycode">
.........
<enum name="KEYCODE_F20" value="289" />
</attr>
- sprd.mocor12.androidS/frameworks/base/data/keyboards/Generic.kl
# key 189 F19
key 190 F20 # 驱动上报的键值绑定F20字符串
# key 191 F21
最后执行指令更新下api,再编译
make update-api
sprd.mocor12.androidS/frameworks/base/core/api/current.txt
sprd.mocor12.androidS/frameworks/base/core/api/test-current.txt
至此Android上层就能完整收到按键触发
三、关于make update-api
何时需要执行make update-api命令
- 添加系统API或者修改@hide的API后,需要执行make update-api,然后再make
- 修改公共api后,需要make update-api
- 在修改完系统Api或部分公共Api后(常见于修改Intent.java、KeyEvent.java等等),执行源码编译时会有如下提示
******************************
You have tried to change the API from what has been previously approved.
To make these errors go away, you have two choices:
1. You can add '@hide' javadoc comments (and remove @SystemApi/@TestApi/etc)
to the new methods, etc. shown in the above diff.
2. You can update current.txt and/or removed.txt by executing the following command:
m api-stubs-docs-non-updatable-update-current-api
To submit the revised current.txt to the main Android repository,
you will need approval.
******************************
- 错误信息表明是由于API错误导致
谷歌对于所有的类和API,分为开方和非开放两种,而开放的类和API,可以通过“Javadoc标签”与源码同步生成“程序的开发文档”;当我们修改或者添加一个新的API时,我们有两种方案可以避免出现上述错误.
- 其一是将该接口加上 非公开的标签:/*{@hide}/;
- 再者可以在修改后执行:make update-api(公开),将修改内容与API的doc文件更新到一致。
- 解决办法
- 执行: make update-api
- 修改后相应API文件后,在base库下面会产生“current.txt、test-current.txt”文件的差异,提交时将该差异一并提交审核即可。
- 应用层反射获取隐藏api
上面既然提到了hide api让我想到曾经开发一款app时用到获取hide api,一般我们反射是获取不到hide api的。
在查询相关资料以及文章时发现Github上有一个开源库AndroidHiddenApiBypass就不多说展开了有兴趣的可以去试一下
四、getevent命令使用
130|G0830WW:/ $ getevent -h
Usage: getevent [-t] [-n] [-s switchmask] [-S] [-v [mask]] [-d] [-p] [-i] [-l] [-q] [-c count] [-r] [device]
-t: show time stamps
-n: don't print newlines
-s: print switch states for given bits
-S: print all switch states
-v: verbosity mask (errs=1, dev=2, name=4, info=8, vers=16, pos. events=32, props=64)
-d: show HID descriptor, if available
-p: show possible events (errs, dev, name, pos. events)
-i: show all device info and possible events
-l: label event types and names in plain text
-q: quiet (clear verbosity mask)
-c: print given number of events then exit
-r: print rate events are received
-t:显示时间戳-n:不换行打印-s:显示指定位的开关状态-S:显示所有位的开关状态-p︰显示设备支持的事件类型和编码方式-l:以文本形式输出事件类型和名称-r:显示事件上报速率
- 其中最常用的参数就是:
-t: show time stamps—显示时间打印的时间
-l: label event types and names in plain text—显示event时间的类型
-r: print rate events are received—显示一下时间接受的速率
- 具体使用示例
C:\Users\MinusOne>adb shell getevent -lrt
[ 2197.095893] /dev/input/event0: EV_KEY KEY_POWER DOWN
[ 2197.095893] /dev/input/event0: EV_SYN SYN_REPORT 00000000
[ 2197.262830] /dev/input/event0: EV_KEY KEY_POWER UP
[ 2197.262830] /dev/input/event0: EV_SYN SYN_REPORT 00000000 rate 5
[ 2197.868133] /dev/input/event5: EV_SYN SYN_MT_REPORT 00000000
[ 2197.868133] /dev/input/event5: EV_SYN SYN_REPORT 00000000 rate 1
[ 2199.965429] /dev/input/event0: EV_KEY KEY_VOLUMEDOWN DOWN
[ 2199.965429] /dev/input/event0: EV_SYN SYN_REPORT 00000000 rate 0
[ 2200.132085] /dev/input/event0: EV_KEY KEY_VOLUMEDOWN UP
[ 2200.132085] /dev/input/event0: EV_SYN SYN_REPORT 00000000 rate 6
[ 2202.466210] /dev/input/event0: EV_KEY KEY_VOLUMEUP DOWN
[ 2202.466210] /dev/input/event0: EV_SYN SYN_REPORT 00000000 rate 0
[ 2202.669186] /dev/input/event0: EV_KEY KEY_VOLUMEUP UP
[ 2202.669186] /dev/input/event0: EV_SYN SYN_REPORT 00000000 rate 4
PhoneWindowManager浅析
一、 PhoneWindowManager 简介
- PhoneWindowManager
PhoneWindowManager代码路径如下:
sprd.mocor12.androidS\frameworks\base\services\core\java\com\android\server\policy\PhoneWindowManager.java
sprd.mocor12.androidS\frameworks\base\services\core\java\com\android\server\policy\WindowManagerPolicy.java
tips:修改此路径源码可执行make services进行push验证
PhoneWindowManager类实现接口如下:
java.lang.Object
↳ com.android.server.policy.WindowManagerPolicy.java
↳ com.android.server.policy.PhoneWindowManager.java
PhoneWindowManager初始化
wms::wms
wms::initPolicy
UiThread::Handler::runWithScissors
PhoneWindowManager::init
WindowManagerService、PhoneWindowManager、WindowManagerImpl关系
**WindowManagerService:**高层级窗口管理服务,主要委托PhoneWindowManager来处理Phone UI。
**WindowManagerImpl:**低层次,负责与系统窗口管理服务进行操作通信,与Context进行关联。
**WindowManagerGlobal:**低层次,负责与系统窗口管理服务进行操作通信,不与Context进行关联。
ViewRootImpl创建时--setView()---Session.addToDisPlay()----WindowManagerService.addWindow();
WindowToken来记录窗口信息。
WindowManagerImpl是客户端使用的类,实现WindowManger API,是供SDK开发使用。
PhoneWindowManager是供系统进程使用,是WindowManagerService 的一部分。
如果一个Activity想和WindowManagerService通信,那么它会先调用用WindowManagerImpl API ,然后WindowManagerService 会利用PhoneWindowManager 决定一些策略来处理UI
PhoneWindowManager运行于systemserver线程中,在android系统中,每一个App所呈现的UI都是一个Activity,而UI的呈现实际上则是由Window对象来实现的,每个Window对象实际上又是PhoneWindow对象;而每个PhonewWindow对象又是一个PhoneWindowManager(PWM)对象。在将按键处理操作分发到App之前,首选会回调PWM中的dispatchUnhandledKey()方法,该方法主要是用于在执行当前App处理之前的操作,处理一些特殊按键。
PWM中有两个对按键比较重要的函数(按键预处理,针对特殊按键,如Power):intercepetKeyBeforeDispatching()和intercepetKeyBeforeQueueing()。
intercepetKeyBeforeQueueing()主要是用来处理音量和电源键,该函数中有一个重要的变量ACTION_PASS_TO_USER(变量解释:传递按键事件到用户app进行处理),该函数的返回值如果是0则表示该按键事件被消费,按键事件不会传递到用户App,返回1则表示允许按键事件传递给用户App中进行处理。
intercepetKeyBeforeDispatching()主要是用来预处理home、menu、search按键事件;该函数的返回值如果是-1则表示的是该按键事件被消费,返回0则代表允许事件的往下传递。这些处理的实现都是在PWM,其调用者是InputDispatchThread,这样处理的好处就是把平台相关的东西都放置在PWM中,而InputDispatchThread则是平台通用的,厂商要定制特殊策略只需要更改PWM,这样就很好的做到了Mechanism和Policy的分离。
interceptKeyBeforeQueueing调用栈
InputDispatcher::injectInputEvent // 触发调用1
InputDispatcher::notifyKey // 触发调用2
com_android_server_input_InputManagerService::NativeInputManager::interceptKeyBeforeQueueing
IMS::WindowManagerCallbacks::interceptKeyBeforeQueueing // InputMontor是WindowManagerCallbacks的实现类
InputMontor::interceptKeyBeforeQueueing
WMS::PhoneWindowManager::interceptKeyBeforeQueueing
interceptKeyBeforeDispatching调用栈
InputDispatcher::dispatchOnce
InputDispatcher::dispatchOnceInnerLocked(
//InputDispatcher::mPolicy // mPolicy:: com_android_server_input_InputManagerService.cpp
InputDispatcher::dispatchKeyLocked
InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible
com_android_server_input_InputManagerService::NativeInputManager::interceptKeyBeforeDispatching
// jni InputManagerService::nativeInit中初始化
IMS::WindowManagerCallbacks::interceptKeyBeforeDispatching // InputMontor是WindowManagerCallbacks的实现类
InputMontor::interceptKeyBeforeDispatching
WMS::PhoneWindowManager::interceptKeyBeforeDispatching
PhoneWindowManager相关类图
InputMonitor 实现IMS::WindowManagerCallbacks接口,并且持有WMS引用;
WMS持有WindowManagerPolicy接口的实现类PhoneWindowManager;
PhoneWindowManager的内部类PolicyHandler分发业务逻辑;
PhoneWindowManager的初始化在android.ui 线程;
在Android 中会有以下5个按键(Back、Home、Menu、Power、Volume)与用户进行交互,Framework层中实现按键功能,因此,从手机系统定制的角度,可以满足客户的客制化要求。本文主要从Framework层浅析这些客制化需求的实现。
二、电源键(Power)函数分析
- power键按下的时候调用的是interceptPowerKeyDown方法
@Override
public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
...此处省略部分代码
case KeyEvent.KEYCODE_POWER: {
EventLogTags.writeInterceptPower(
KeyEvent.actionToString(event.getAction()),
mPowerKeyHandled ? 1 : 0,
mSingleKeyGestureDetector.getKeyPressCounter(KeyEvent.KEYCODE_POWER));
// Any activity on the power button stops the accessibility shortcut
result &= ~ACTION_PASS_TO_USER;
isWakeKey = false; // wake-up will be handled separately
if (down) {
/*SPRD : add power debug log start*/
Slog.d(TAG, "Receive Input KeyEvent of Powerkey down");
/*SPRD : add power debug log end*/
interceptPowerKeyDown(event, interactiveAndOn);//power键按下
} else {
/*SPRD : add power debug log start*/
Slog.d(TAG, "Receive Input KeyEvent of Powerkey up");
/*SPRD : add power debug log end*/
interceptPowerKeyUp(event, canceled);//power键抬起
}
break;
}
...此处省略部分代码
}
private void interceptPowerKeyDown(KeyEvent event, boolean interactive) {
// Hold a wake lock until the power key is released.
if (!mPowerKeyWakeLock.isHeld()) {
mPowerKeyWakeLock.acquire();
}
mWindowManagerFuncs.onPowerKeyDown(interactive);
// Stop ringing or end call if configured to do so when power is pressed.
TelecomManager telecomManager = getTelecommService();
boolean hungUp = false;
if (telecomManager != null) {
if (telecomManager.isRinging()) {
// Pressing Power while there's a ringing incoming
// call should silence the ringer.
telecomManager.silenceRinger();
} else if ((mIncallPowerBehavior
& Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0
&& telecomManager.isInCall() && interactive) {
// Otherwise, if "Power button ends call" is enabled,
// the Power button will hang up any current active call.
hungUp = telecomManager.endCall();
}
}
final boolean handledByPowerManager = mPowerManagerInternal.interceptPowerKeyDown(event);
// Inform the StatusBar; but do not allow it to consume the event.
sendSystemKeyToStatusBarAsync(event.getKeyCode());
// If the power key has still not yet been handled, then detect short
// press, long press, or multi press and decide what to do.
mPowerKeyHandled = mPowerKeyHandled || hungUp
|| handledByPowerManager || mKeyCombinationManager.isPowerKeyIntercepted();
if (!mPowerKeyHandled) {
if (!interactive) {
wakeUpFromPowerKey(event.getDownTime());
}
} else {
// handled by another power key policy.
if (!mSingleKeyGestureDetector.isKeyIntercepted(KEYCODE_POWER)) {
mSingleKeyGestureDetector.reset();
}
}
}
- power短按长按规则
/**
* Rule for single power key gesture.
*/
private final class PowerKeyRule extends SingleKeyGestureDetector.SingleKeyRule {
PowerKeyRule(int gestures) {
super(KEYCODE_POWER, gestures);
}
@Override
int getMaxMultiPressCount() {
return getMaxMultiPressPowerCount();
}
@Override
void onPress(long downTime) {
powerPress(downTime, 1 /*count*/,
mSingleKeyGestureDetector.beganFromNonInteractive());
}
@Override
void onLongPress(long eventTime) {
if (mSingleKeyGestureDetector.beganFromNonInteractive()
&& !mSupportLongPressPowerWhenNonInteractive) {
Slog.v(TAG, "Not support long press power when device is not interactive.");
return;
}
powerLongPress(eventTime);
}
@Override
void onVeryLongPress(long eventTime) {
mActivityManagerInternal.prepareForPossibleShutdown();
powerVeryLongPress();
}
@Override
void onMultiPress(long downTime, int count) {
powerPress(downTime, count, mSingleKeyGestureDetector.beganFromNonInteractive());
}
}
- power短按
private void powerPress(long eventTime, int count, boolean beganFromNonInteractive) {
if (mDefaultDisplayPolicy.isScreenOnEarly() && !mDefaultDisplayPolicy.isScreenOnFully()) {
Slog.i(TAG, "Suppressed redundant power key press while "
+ "already in the process of turning the screen on.");
return;
}
final boolean interactive = Display.isOnState(mDefaultDisplay.getState());
Slog.d(TAG, "powerPress: eventTime=" + eventTime + " interactive=" + interactive
+ " count=" + count + " beganFromNonInteractive=" + beganFromNonInteractive
+ " mShortPressOnPowerBehavior=" + mShortPressOnPowerBehavior);
if (count == 2) {
powerMultiPressAction(eventTime, interactive, mDoublePressOnPowerBehavior);
} else if (count == 3) {
powerMultiPressAction(eventTime, interactive, mTriplePressOnPowerBehavior);
} else if (interactive && !beganFromNonInteractive) {
if (mSideFpsEventHandler.onSinglePressDetected(eventTime)) {
Slog.i(TAG, "Suppressing power key because the user is interacting with the "
+ "fingerprint sensor");
return;
}
switch (mShortPressOnPowerBehavior) {
case SHORT_PRESS_POWER_NOTHING:
break;
case SHORT_PRESS_POWER_GO_TO_SLEEP:
//其中我写过其他按键息屏可以调用这个方法
sleepDefaultDisplayFromPowerButton(eventTime, 0);
break;
case SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP:
sleepDefaultDisplayFromPowerButton(eventTime,
PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
break;
case SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP_AND_GO_HOME:
if (sleepDefaultDisplayFromPowerButton(eventTime,
PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE)) {
launchHomeFromHotKey(DEFAULT_DISPLAY);
}
break;
case SHORT_PRESS_POWER_GO_HOME:
shortPressPowerGoHome();
break;
case SHORT_PRESS_POWER_CLOSE_IME_OR_GO_HOME: {
if (mDismissImeOnBackKeyPressed) {
if (mInputMethodManagerInternal == null) {
mInputMethodManagerInternal =
LocalServices.getService(InputMethodManagerInternal.class);
}
if (mInputMethodManagerInternal != null) {
mInputMethodManagerInternal.hideCurrentInputMethod(
SoftInputShowHideReason.HIDE_POWER_BUTTON_GO_HOME);
}
} else {
shortPressPowerGoHome();
}
break;
}
}
}
}
- power长按
private void powerLongPress(long eventTime) {
final int behavior = getResolvedLongPressOnPowerBehavior();
Slog.d(TAG, "powerLongPress: eventTime=" + eventTime
+ " mLongPressOnPowerBehavior=" + mLongPressOnPowerBehavior);
switch (behavior) {
case LONG_PRESS_POWER_NOTHING:
break;
case LONG_PRESS_POWER_GLOBAL_ACTIONS:
mPowerKeyHandled = true;
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false,
"Power - Long Press - Global Actions");
showGlobalActions();
break;
case LONG_PRESS_POWER_SHUT_OFF:
case LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM:
mPowerKeyHandled = true;
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false,
"Power - Long Press - Shut Off");
sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
mWindowManagerFuncs.shutdown(behavior == LONG_PRESS_POWER_SHUT_OFF);
break;
case LONG_PRESS_POWER_GO_TO_VOICE_ASSIST:
mPowerKeyHandled = true;
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false,
"Power - Long Press - Go To Voice Assist");
// Some devices allow the voice assistant intent during setup (and use that intent
// to launch something else, like Settings). So we explicitly allow that via the
// config_allowStartActivityForLongPressOnPowerInSetup resource in config.xml.
launchVoiceAssist(mAllowStartActivityForLongPressOnPowerDuringSetup);
break;
case LONG_PRESS_POWER_ASSISTANT:
mPowerKeyHandled = true;
performHapticFeedback(HapticFeedbackConstants.ASSISTANT_BUTTON, false,
"Power - Long Press - Go To Assistant");
final int powerKeyDeviceId = Integer.MIN_VALUE;
launchAssistAction(null, powerKeyDeviceId, eventTime,
AssistUtils.INVOCATION_TYPE_POWER_BUTTON_LONG_PRESS);
break;
}
}
其中behavior来自LPP特性配置目录:sprd.mocor12.androidS/vendor/partner_gms/overlay/GmsConfigOverlayCommon/res/values/config.xml
安卓12及以上需要有物理按键启动谷歌助手若没有就需要长按power键启动所以这个config_longPressOnPowerBehavior需要配置5
<!-- Update the default behavior of power button to be compatible with that of previous Android
releases. If this behavior is not desired, change the value of the following three
configurations as needed.-->
<!-- Control the behavior when the user long presses the power button.
0 - Nothing
1 - Global actions menu
2 - Power off (with confirmation)
3 - Power off (without confirmation)
4 - Go to voice assist
5 - Go to assistant (Settings.Secure.ASSISTANT)
-->
<!--modify by yxiaobin open LPP begin-->
<integer name="config_longPressOnPowerBehavior">5</integer>
<!-- Whether the setting to change long press on power behaviour from default to assistant (5)
is available in Settings.
-->
<bool name="config_longPressOnPowerForAssistantSettingAvailable">true</bool>
<!-- Control the behavior when the user presses the power and volume up buttons together.
0 - Nothing
1 - Mute toggle
2 - Global actions menu
-->
<!-- UNISOC: bug1668006 update for menu of "Shortcut to prevent ringing"-->
<integer name="config_keyChordPowerVolumeUp">2</integer>
<!--modify by yxiaobin open LPP end-->
一般长按power键弹框是调用showGlobalActionsInternal()方法,这个方法实际上调用的是GlobalActions 的showDialog 方法弹出关机界面Dialog。代码路径:frameworks\base\services\core\java\com\android\server\policy\GlobalActions.java如需定制关机弹窗界面可以跟进代码
三、NewKey启动语音助手和SOS
private boolean mLongPressSOSdown = false;
private final Runnable mLongPressSOS = new Runnable() {
@Override
public void run() {
//if (DEBUG_WAKEUP) Slog.i(TAG, "start sos service --> mLongPressSOSdown = "+mLongPressSOSdown);
if(mLongPressSOSdown){
Intent intentsos = new Intent();
intentsos.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) ;
intentsos.setComponent(new ComponentName("com.dp.op","com.dp.op.module.sos.SosSetActivity"));
mContext.startActivity(intentsos);
}
}
};
@Override
public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
...此处省略部分代码
case KeyEvent.KEYCODE_F20: {
if (SystemProperties.get("ro.product.siri_key").equals("true")) {
final boolean longPressed = event.getRepeatCount() > 0;
if (down && !longPressed) {
Message msg = mHandler.obtainMessage(MSG_LAUNCH_ASSIST, event.getDeviceId(),
0 /* unused */, event.getEventTime() /* eventTime */);
msg.setAsynchronous(true);
msg.sendToTarget();
}
result &= ~ACTION_PASS_TO_USER;
}
break;
}
case KeyEvent.KEYCODE_SOS:{
if(down){
mLongPressSOSdown = true;
mHandler.postDelayed(mLongPressSOS,3000);
}else{
mLongPressSOSdown = false;
if(mLongPressSOS != null) mHandler.removeCallbacks(mLongPressSOS);
}
}
...此处省略部分代码
}
四、如何让App拿到PowerKey值
一般情况下App是拿不到Power的Key值,但通过以下方法可以实现。
- 修改PhoneWindowManager文件实现
在PhoneWindowManager 中修改interceptKeyBeforeQueueing方法实现让特定的APP拿到PowerKey值
case KeyEvent.KEYCODE_POWER: {
if(mKeyguardCandidate!=null && mKeyguardCandidate.getOwningPackage().equals("top.minusoneapp.exm")){
return 1;//传给app处理
}
............
}
- 如果只想让某个app的某个Activity处理
case KeyEvent.KEYCODE_POWER: {
if(mKeyguardCandidate!=null && mKeyguardCandidate.getAttrs()!=null
&&mKeyguardCandidate.getAttrs().getTitle().equals("top.minusoneaoo.exm.MainActivity")){
return 1;
}
............
}
五、展讯三指截屏
研究源码发现展讯的三指截屏的实现在PWM里面
/** {@inheritDoc} */
@Override
public void init(Context context, IWindowManager windowManager,
WindowManagerFuncs windowManagerFuncs) {
...此处省略部分代码
// UNISOC: Add for three-finger screenshot.
check3fingerTouchGestureState();
}
// Called on the DisplayManager's DisplayPowerController thread.
@Override
public void screenTurnedOn(int displayId) {
if (DEBUG_WAKEUP) Slog.i(TAG, "Display " + displayId + " turned on...");
if (displayId != DEFAULT_DISPLAY) {
return;
}
synchronized (mLock) {
if (mKeyguardDelegate != null) {
mKeyguardDelegate.onScreenTurnedOn();
}
}
reportScreenStateToVrManager(true);
check3fingerTouchGestureState();
}
在初始化函数和亮屏时均会调用到check3fingerTouchGestureState()这个方法主要判定有没有开启功能然后进行注册
private void check3fingerTouchGestureState() {
if (mSprd3FingerTouchEventListener == null) initUnisoc3FingerEventListener();
final int offValue = 0; // Default off.
int _3fingerScreenshotValue = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.UNISOC_THREE_POINTER_TOUCH_SCREENSHOT, offValue);
if (_3fingerScreenshotValue == offValue) {
unregister3fingerGesturePointerListener();
} else {
register3fingerGesturePointerListener();
}
}
private void register3fingerGesturePointerListener() {
if (m3FingerGesturePointerListenerRegistered) return;
mSprd3FingerTouchEventListener.registerSensorListener();
mWindowManagerFuncs.registerPointerEventListener(mSprd3FingerTouchEventListener,
DEFAULT_DISPLAY);
m3FingerGesturePointerListenerRegistered = true;
}
private void unregister3fingerGesturePointerListener() {
if (!m3FingerGesturePointerListenerRegistered) return;
mSprd3FingerTouchEventListener.unregisterSensorListener();
mWindowManagerFuncs.unregisterPointerEventListener(mSprd3FingerTouchEventListener,
DEFAULT_DISPLAY);
m3FingerGesturePointerListenerRegistered = false;
}
private void initUnisoc3FingerEventListener() {
final Sprd3FingerTouchEventListener.Callbacks callback = new Sprd3FingerTouchEventListener.Callbacks() {
@Override
public void takeScreenshot() {
mScreenshotRunnable.setScreenshotType(TAKE_SCREENSHOT_FULLSCREEN);
mHandler.post(mScreenshotRunnable);
}
};
mSprd3FingerTouchEventListener = new Sprd3FingerTouchEventListener(mContext, callback, mHandler);
}
在息屏时取消注册监听
@Override
public void screenTurnedOff(int displayId) {
...此处省略部分代码
unregister3fingerGesturePointerListener();
}
到此处我就在想可以在PWM里面自定义手势比如画圈启动SOS等以后有时间去实现一下
六、adb shell dumpsys window
在研究源码时发现PWM有个dump方法
@Override
public void dump(String prefix, PrintWriter pw, String[] args) {
pw.print(prefix); pw.print("mSafeMode="); pw.print(mSafeMode);
pw.print(" mSystemReady="); pw.print(mSystemReady);
pw.print(" mSystemBooted="); pw.println(mSystemBooted);
pw.print(prefix); pw.print("mCameraLensCoverState=");
pw.println(WindowManagerFuncs.cameraLensStateToString(mCameraLensCoverState));
pw.print(prefix); pw.print("mWakeGestureEnabledSetting=");
pw.println(mWakeGestureEnabledSetting);
pw.print(prefix);
pw.print("mUiMode=");
pw.print(Configuration.uiModeToString(mUiMode));
pw.print("mEnableCarDockHomeCapture="); pw.println(mEnableCarDockHomeCapture);
...此处省略部分代码
}
这让我联想但平时一直在使用的一条shell命令dumpsys window经实验发现果然不出我所料我们执行的这条shell命令最终会调用到这个方法