项目的安全需要:
要求客户APP在申请了特定ENABLE_HOME权限后,允许监听home键并对HOME键的系统功能进行屏蔽;
修改基于RK平台Android 5.1.1系统
经过对系统代码的分析,按照用户按下HOME键的流程整理如下:
系统服务进程中:
1、framework/base/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
函数:interceptKeyBeforeQueueing 本次并未修改,不展开
函数:interceptKeyBeforeDispatching
if (keyCode == KeyEvent.KEYCODE_HOME) {
// If we have released the home key, and didn't do anything else
// while it was pressed, then it is time to go home!
if (!down) {
//处理HOME键up(放开)的动作;
mHomePressed = false;
//这部分是我加入的如果应用截取了home键,直接退出,主要通过加入的标志mHomeIgnoreflag来判断,后面会做介绍
//add by liaozongping @20161111 for HOME key ignore!
if( mHomeIgnoreflag == true ){
mHomeIgnoreflag = false;
return 0;
}
//added end
cancelPreloadRecentApps();
if (mHomeConsumed) {
mHomeConsumed = false;
return -1;
}
if (canceled) {
Log.i(TAG, "Ignoring HOME; event canceled.");
return -1;
}
// If an incoming call is ringing, HOME is totally disabled.
// (The user is already on the InCallUI at this point,
// and his ONLY options are to answer or reject the call.)
TelecomManager telecomManager = getTelecommService();
if (telecomManager != null && telecomManager.isRinging()) {
Log.i(TAG, "Ignoring HOME; there's a ringing incoming call.");
return -1;
}
// Delay handling home if a double-tap is possible.
if (mDoubleTapOnHomeBehavior != DOUBLE_TAP_HOME_NOTHING) {
mHandler.removeCallbacks(mHomeDoubleTapTimeoutRunnable); // just in case
mHomeDoubleTapPending = true;
mHandler.postDelayed(mHomeDoubleTapTimeoutRunnable,
ViewConfiguration.getDoubleTapTimeout());
return -1;
}
//add by liaozongping @20161111 for HOME key ignore!
mHomeIgnoreflag = false;
//此处系统处理home键up的实际操作,也就是退回到桌面的动作
handleShortPressOnHome();
return -1;
}
//这下面处理的是HOME键down(按下)的动作,实际的短按系统没有做什么处理动作
//add by liaozongping @20161111 for HOME key ignore!
mHomeIgnoreflag = false;
//跟解说一样,这里是判断,如果是锁屏串口,那么就把按键给锁屏的APP或者是不需要操作的类型,就丢弃
// If a system window has focus, then it doesn't make sense
// right now to interact with applications.
WindowManager.LayoutParams attrs = win != null ? win.getAttrs() : null;
if (attrs != null) {
final int type = attrs.type;
if (type == WindowManager.LayoutParams.TYPE_KEYGUARD_SCRIM
|| type == WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG
|| (attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) {
// the "app" is keyguard, so give it the key
return 0;
}
final int typeCount = WINDOW_TYPES_WHERE_HOME_DOESNT_WORK.length;
for (int i=0; i
这里注册一个广播接收器,用于获取KEYCODE_HOME按键down的用户处理状态,如果被APP捕获并处理了,就不再执行up的动作(即handleShortPressOnHome();)
//add by liaozongping @20161111 for HOME key ignore!
HomeIgnoreReceiver mHomeIgnoreReceiver;
private boolean mHomeIgnoreflag;
//added end
/*
* 接收home键屏蔽广播
*/
class HomeIgnoreReceiver extends BroadcastReceiver{
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "KeyCode_HOME Ignore!!!");
mHomeIgnoreflag = true;
}
}
函数中注册相应的广播接收器(也可以另外找合适的地方进行注册):
//add by liaozongping @20161111 for HOME key ignore!
IntentFilter homeKeyFilter = new IntentFilter();
homeKeyFilter.addAction("keyevent.ignorehome");
mHomeIgnoreReceiver = new HomeIgnoreReceiver();
//这里对于抓取home键的广播进行权限限制,只有具备android.permission.XXXXX.XXX权限的应用能够截取HOME键的操作;
context.registerReceiver( mHomeIgnoreReceiver, homeKeyFilter, "android.permission.XXXXX.XXX", null);
//added end
接下,要对APP的onKeyDown处理结果获取并发送广播,经过查找发现framework中最终调用APP的onKeyDown函数的地方是:
frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
final int keyCode = event.getKeyCode();
final int action = event.getAction();
final boolean isDown = action == KeyEvent.ACTION_DOWN;
if (isDown && (event.getRepeatCount() == 0)) {
// First handle chording of panel key: if a panel key is held
// but not released, try to execute a shortcut in it.
if ((mPanelChordingKey > 0) && (mPanelChordingKey != keyCode)) {
boolean handled = dispatchKeyShortcutEvent(event);
if (handled) {
return true;
}
}
// If a panel is open, perform a shortcut on it without the
// chorded panel key
if ((mPreparedPanel != null) && mPreparedPanel.isOpen) {
if (performPanelShortcut(mPreparedPanel, keyCode, event, 0)) {
return true;
}
}
}
if (!isDestroyed()) {
final Callback cb = getCallback();
//没有经过其他处理,并没有销毁的窗口,最终会到这里调用callback,调用到APP重写的onKeyDown,此处handled即为onKeyDown的返回值
final boolean handled = cb != null && mFeatureId < 0 ? cb.dispatchKeyEvent(event)
: super.dispatchKeyEvent(event);
LOGD("keycallback handled=" + handled);
if (handled) {
//当APP截取到KeyCODE_HOME按键,返回true后,我们需要通知系统按键服务进程,此次操作被APP捕获了,不要再执行退回到桌面的操作
//PS:之所以这里需要使用广播,
//1、是因为这里是属于用户APP的进程范围,而KEYCODE_HOME的down和up是独立的event,并且前一个文件中的event处理是在系统按键进程中进行处理,所以只能使用广播
//2、客户定义了接口要在onKeyDown中进行捕获,那么只能够将down的捕获情况通过广播传递给按键处理进程,让该进程放弃下一次KEYCODE_HOME的up event的处理
//add by liaozongping @20161111 for HOME key ignore!
if(event.getKeyCode() == KeyEvent.KEYCODE_HOME && event.getAction() == KeyEvent.ACTION_DOWN ){
Intent intent = new Intent();
intent.setAction("keyevent.ignorehome");
mContext.sendBroadcast(intent);
}
//added end
return true;
}
}
return isDown ? PhoneWindow.this.onKeyDown(mFeatureId, event.getKeyCode(), event)
: PhoneWindow.this.onKeyUp(mFeatureId, event.getKeyCode(), event);
}
最后,在系统中加入刚才使用的自定义的系统权限:
frameworks/base/core/res/AndroidManifest.xml文件中加入:
2926
2927
附参考链接:http://www.cnblogs.com/android-blogs/p/5684622.html