前言
本文我们主要分析一下android系统对HOME按键的响应过程,HOME按键事件是属于系统级别的按键事件监听,而在Android系统中,系统级别的按键处理逻辑都在PhoneWindowManager这个类中。
一、interceptKeyBeforeDispatching方法分发按键事件
1、PhoneWindowManager的dispatchUnhandledKey方法会先收到按键事件。
frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
public class PhoneWindowManager implements WindowManagerPolicy {
@Override
public KeyEvent dispatchUnhandledKey(IBinder focusedToken, KeyEvent event, int policyFlags) {
KeyEvent fallbackEvent = null;
if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
final KeyCharacterMap kcm = event.getKeyCharacterMap();
final int keyCode = event.getKeyCode();
final int metaState = event.getMetaState();
final boolean initialDown = event.getAction() == KeyEvent.ACTION_DOWN
&& event.getRepeatCount() == 0;
// Check for fallback actions specified by the key character map.
final FallbackAction fallbackAction;
if (initialDown) {
fallbackAction = kcm.getFallbackAction(keyCode, metaState);
} else {
fallbackAction = mFallbackActions.get(keyCode);
}
if (fallbackAction != null) {
if (DEBUG_INPUT) {
Slog.d(TAG, "Fallback: keyCode=" + fallbackAction.keyCode
+ " metaState=" + Integer.toHexString(fallbackAction.metaState));
}
final int flags = event.getFlags() | KeyEvent.FLAG_FALLBACK;
fallbackEvent = KeyEvent.obtain(
event.getDownTime(), event.getEventTime(),
event.getAction(), fallbackAction.keyCode,
event.getRepeatCount(), fallbackAction.metaState,
event.getDeviceId(), event.getScanCode(),
flags, event.getSource(), event.getDisplayId(), null);
//在这里进一步触发interceptFallback方法
if (!interceptFallback(focusedToken, fallbackEvent, policyFlags)) {
fallbackEvent.recycle();
fallbackEvent = null;
}
if (initialDown) {
mFallbackActions.put(keyCode, fallbackAction);
} else if (event.getAction() == KeyEvent.ACTION_UP) {
mFallbackActions.remove(keyCode);
fallbackAction.recycle();
}
}
}
return fallbackEvent;
}
private boolean interceptFallback(IBinder focusedToken, KeyEvent fallbackEvent,
int policyFlags) {
int actions = interceptKeyBeforeQueueing(fallbackEvent, policyFlags);
if ((actions & ACTION_PASS_TO_USER) != 0) {
//进一步调用interceptKeyBeforeDispatching
long delayMillis = interceptKeyBeforeDispatching(
focusedToken, fallbackEvent, policyFlags);
if (delayMillis == 0) {
return true;
}
}
return false;
}
}
PhoneWindowManager的dispatchUnhandledKey方法会继续触发另一个关键方法interceptFallback,而interceptFallback方法又会进一步调用interceptKeyBeforeDispatching方法。
2、PhoneWindowManager的interceptKeyBeforeDispatching方法如下所示。
public class PhoneWindowManager implements WindowManagerPolicy {
private final SparseArray<DisplayHomeButtonHandler> mDisplayHomeButtonHandlers = new SparseArray<>();
@Override
public long interceptKeyBeforeDispatching(IBinder focusedToken, KeyEvent event,
int policyFlags) {
final boolean keyguardOn = keyguardOn();
final int keyCode = event.getKeyCode();//按键编码
final int repeatCount = event.getRepeatCount();
final int metaState = event.getMetaState();
final int flags = event.getFlags();
final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
final boolean canceled = event.isCanceled();
final int displayId = event.getDisplayId();//屏幕设备id
final long key_consumed = -1;
...代码省略...
switch(keyCode) {
case KeyEvent.KEYCODE_HOME://系统主要是在这里对HOME按键事件进行响应的
//从缓存集合中获取Handler
DisplayHomeButtonHandler handler = mDisplayHomeButtonHandlers.get(displayId);
if (handler == null) {
//如果没有缓存,则创建Handler并添加到缓存集合中
handler = new DisplayHomeButtonHandler(displayId);
mDisplayHomeButtonHandlers.put(displayId, handler);
}
return handler.handleHomeButton(focusedToken, event);
case KeyEvent.KEYCODE_MENU://菜单按键事件
...代码省略...
break;
case KeyEvent.KEYCODE_APP_SWITCH://应用切换按键事件
...代码省略...
return key_consumed;
...代码省略...
}
if (isValidGlobalKey(keyCode)
&& mGlobalKeyManager.handleGlobalKey(mContext, keyCode, event)) {
return key_consumed;
}
if ((metaState & KeyEvent.META_META_ON) != 0) {
return key_consumed;
}
return 0;
}
interceptKeyBeforeDispatching方法会通过switch的各个case分支,分别来响应各种按键事件,我们这里主要关注HOME按键事件的响应过程,系统首先从mDisplayHomeButtonHandlers集合中获取DisplayHomeButtonHandler类型的缓存对象,如果不存在缓存对象则创建DisplayHomeButtonHandler对象并添加到集合中,反正最终会调用DisplayHomeButtonHandler的handleHomeButton方法。
3、DisplayHomeButtonHandler是PhoneWindowManager的内部类,handleHomeButton方法如下所示。
public class PhoneWindowManager implements WindowManagerPolicy {
private class DisplayHomeButtonHandler {
private final int mDisplayId;
private boolean mHomeDoubleTapPending;
private boolean mHomePressed;
private boolean mHomeConsumed;
private final Runnable mHomeDoubleTapTimeoutRunnable = new Runnable() {
@Override
public void run() {
if (mHomeDoubleTapPending) {
mHomeDoubleTapPending = false;
handleShortPressOnHome(mDisplayId);
}
}
};
DisplayHomeButtonHandler(int displayId) {
mDisplayId = displayId;
}
int handleHomeButton(IBinder focusedToken, KeyEvent event) {
final boolean keyguardOn = keyguardOn();
final int repeatCount = event.getRepeatCount();
final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;//是否是按下事件
final boolean canceled = event.isCanceled();
//一个按键事件大多都有两种Action,按下(ACTION_DOWN)和抬起(ACTION_UP)
//!down意味着只有用户触发按键抬起的时候这里才会做响应回到首页
if (!down) {
if (mDisplayId == DEFAULT_DISPLAY) {
cancelPreloadRecentApps();
}
mHomePressed = false;
if (mHomeConsumed) {
mHomeConsumed = false;
return -1;
}
if (canceled) {
Log.i(TAG, "Ignoring HOME; event canceled.");
return -1;
}
if (mDoubleTapOnHomeBehavior != DOUBLE_TAP_HOME_NOTHING) {
mHandler.removeCallbacks(mHomeDoubleTapTimeoutRunnable); // just in case
mHomeDoubleTapPending = true;
mHandler.postDelayed(mHomeDoubleTapTimeoutRunnable,
ViewConfiguration.getDoubleTapTimeout());
return -1;
}
//为了避免阻塞输入管道,这里通过Handler的post方法切换到了主线程
mHandler.post(() -> handleShortPressOnHome(mDisplayId));
return -1;
}
...代码省略...
return -1;
}
}
private void handleShortPressOnHome(int displayId) {
// Turn on the connected TV and switch HDMI input if we're a HDMI playback device.
final HdmiControl hdmiControl = getHdmiControl();
if (hdmiControl != null) {
hdmiControl.turnOnTv();
}
// If there's a dream running then use home to escape the dream
// but don't actually go home.
if (mDreamManagerInternal != null && mDreamManagerInternal.isDreaming()) {
mDreamManagerInternal.stopDream(false /*immediate*/);
return;
}
// Go home!
launchHomeFromHotKey(displayId);
}
}
handleHomeButton方法首先获取按键的Action类型是否为按下,并且进行了条件判断,只有当按键抬起的时候才会触发返回首页的相关操作,为了避免阻塞输入管道,这里通过Handler的post方法将当前线程切换到了主线程,并进一步调用handleShortPressOnHome方法,该方法又进一步调用launchHomeFromHotKey方法。
4、PhoneWindowManager的launchHomeFromHotKey方法如下所示。
public class PhoneWindowManager implements WindowManagerPolicy {
void launchHomeFromHotKey(int displayId) {
launchHomeFromHotKey(displayId, true /* awakenFromDreams */, true /*respectKeyguard*/);
}
/**
* A home key -> launch home action was detected. Take the appropriate action
* given the situation with the keyguard.
*/
void launchHomeFromHotKey(int displayId, final boolean awakenFromDreams,
final boolean respectKeyguard) {
if (respectKeyguard) {
if (isKeyguardShowingAndNotOccluded()) {
return;
}
if (!isKeyguardOccluded() && mKeyguardDelegate.isInputRestricted()) {
//当处于锁屏模式的时候,首先应该解锁然后才能打开首页
mKeyguardDelegate.verifyUnlock(new OnKeyguardExitResult() {
@Override
public void onKeyguardExitResult(boolean success) {
if (success) {
startDockOrHome(displayId, true /*fromHomeKey*/, awakenFromDreams);
}
}
});
return;
}
}
//判断最近任务是否可见
if (mRecentsVisible) {
try {
//如果最近任务视图可见,则会先停止应用切换功能
ActivityManager.getService().stopAppSwitches();
} catch (RemoteException e) {}
if (awakenFromDreams) {
awakenDreams();
}
//隐藏最近任务
hideRecentApps(false, true);
} else {
//否则,打开首页
startDockOrHome(displayId, true /*fromHomeKey*/, awakenFromDreams);
}
}
}
launchHomeFromHotKey方法先是判断当前是否处于锁屏状态,如果处于锁屏状体则必须先解锁然后才能打开首页。然后会判断最近任务是否可见:
- 如果可见则会调用AMS的stopAppSwitches方法停止应用切换功能,该方法的目的主要是为了暂停后台打开Activity的操作,避免打扰用户的操作.比如这时候我们在后台打开一个新的App,那么由于要回到home页面,所以需要先延时打开。在停止应用切换功能之后还会调用hideRecentApps隐藏最近任务。
- 如果最近任务不可见,则会直接调用startDockOrHome方法打开首页。
二、暂停应用切换Activity的功能。
1、PhoneWindowManager的startDockOrHome方法如下所示。
public class PhoneWindowManager implements WindowManagerPolicy {
static public final String SYSTEM_DIALOG_REASON_HOME_KEY = "homekey";
void startDockOrHome(int displayId, boolean fromHomeKey, boolean awakenFromDreams,
String startReason) {
try {
//先停止应用切换功能
ActivityManager.getService().stopAppSwitches();
} catch (RemoteException e) {}
...代码暂时省略...
}
}
startDockOrHome方法首先会调用ActivityManager.getService().stopAppSwitches()方法暂停掉应用切换Activity的功能。
2、ActivityManagerService的stopAppSwitches方法如下所示。
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
public class ActivityManagerService extends IActivityManager.Stub
implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback, ActivityManagerGlobalLock {
public ActivityTaskManagerService mActivityTaskManager;
@Override
public void stopAppSwitches() {
mActivityTaskManager.stopAppSwitches();
}
}
ActivityManagerService的stopAppSwitches方法什么都没做,只是进一步调用ActivityTaskManagerService的stopAppSwitches方法。
3、继续来看下和ActivityTaskManagerService的stopAppSwitches方法相关的代码。
frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
/**是否允许一个正常的应用切换Acivity**/
private volatile int mAppSwitchesState = APP_SWITCH_ALLOW;
//重新允许后台应用进行应用切换的时间间隔
private static final long RESUME_FG_APP_SWITCH_MS = 500;
/** 不允许应用切换Activity */
static final int APP_SWITCH_DISALLOW = 0;
/**只允许前台应用进行Activity切换 */
static final int APP_SWITCH_FG_ONLY = 1;
/** 允许应用切换Activity */
static final int APP_SWITCH_ALLOW = 2;
@IntDef({
APP_SWITCH_DISALLOW,
APP_SWITCH_FG_ONLY,
APP_SWITCH_ALLOW,
})
@Retention(RetentionPolicy.SOURCE)
@interface AppSwitchState {}
//最后一次设置禁止应用切换Activity的时间
private volatile long mLastStopAppSwitchesTime;
/**
* 是否允许应用切换Activity
*/
@AppSwitchState int getBalAppSwitchesState() {
return mAppSwitchesState;
}
//禁止应用切换Activity的功能
@Override
public void stopAppSwitches() {
mAmInternal.enforceCallingPermission(STOP_APP_SWITCHES, "stopAppSwitches");
synchronized (mGlobalLock) {
mAppSwitchesState = APP_SWITCH_DISALLOW;//不允许应用切换Activity
mLastStopAppSwitchesTime = SystemClock.uptimeMillis();//纪录当前时间
mH.removeMessages(H.RESUME_FG_APP_SWITCH_MSG);//清除原来的消息
//发送消息,延时500毫秒执行,恢复前台应用切换Activity的功能
mH.sendEmptyMessageDelayed(H.RESUME_FG_APP_SWITCH_MSG, RESUME_FG_APP_SWITCH_MS);
}
}
///恢复应用切换Activity的功能
@Override
public void resumeAppSwitches() {
mAmInternal.enforceCallingPermission(STOP_APP_SWITCHES, "resumeAppSwitches");
synchronized (mGlobalLock) {
mAppSwitchesState = APP_SWITCH_ALLOW;//允许应用切换Activity
mH.removeMessages(H.RESUME_FG_APP_SWITCH_MSG);
}
}
final class H extends Handler {
static final int RESUME_FG_APP_SWITCH_MSG = 4;//恢复应用切换Activity的功能
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
...代码省略...
case RESUME_FG_APP_SWITCH_MSG: {
synchronized (mGlobalLock) {
if (mAppSwitchesState == APP_SWITCH_DISALLOW) {
mAppSwitchesState = APP_SWITCH_FG_ONLY;//只允许前台应用切换Activity
}
}
}
break;
}
}
}
- stopAppSwitches方法会将mAppSwitchesState的数值修改为APP_SWITCH_DISALLOW,即不允许应用切换Activity,并且会通过Handler发送类型为RESUME_FG_APP_SWITCH_MSG的消息,延时500毫秒执行,触发handleMessage方法,在RESUME_FG_APP_SWITCH_MSG消息分支,将mAppSwitchesState的数值修改为APP_SWITCH_FG_ONLY,即只允许前台应用切换Activity。
- AMS在启动一个新的Activity的时候,ActivityStarter会调用ATMS的getBalAppSwitchesState方法来判断当前是否允许打开新的Activity。
frameworks/base/services/core/java/com/android/server/wm/ActivityStarter.java
class ActivityStarter {
private final ActivityTaskManagerService mService;
//是否终止后台Activity的启动
boolean shouldAbortBackgroundActivityStart(int callingUid, int callingPid,
final String callingPackage, int realCallingUid, int realCallingPid,
WindowProcessController callerApp, PendingIntentRecord originatingPendingIntent,
boolean allowBackgroundActivityStart, Intent intent) {
...代码省略...
//是否允许应用切换Activity
final boolean appSwitchAllowedOrFg = appSwitchState == APP_SWITCH_ALLOW || appSwitchState == APP_SWITCH_FG_ONLY;
if (((appSwitchAllowedOrFg || mService.mActiveUids.hasNonAppVisibleWindow(callingUid))
&& callingUidHasAnyVisibleWindow)
|| isCallingUidPersistentSystemProcess) {
return false;
}
...代码省略...
}
}
关于Activity的启动流程这里我们不做过多展开,具体请参考Android 12系统源码_页面管理(二)Activity的启动流程(上)这篇文章。
三、关闭系统当前存在的各种弹窗
1、PhoneWindowManager的startDockOrHome在调用stopAppSwitches方法暂停应用切换Activity的功能之后,会调用sendCloseSystemWindows方法关闭系统当前存在的各种弹窗。
public class PhoneWindowManager implements WindowManagerPolicy {
static public final String SYSTEM_DIALOG_REASON_HOME_KEY = "homekey";
void startDockOrHome(int displayId, boolean fromHomeKey, boolean awakenFromDreams,
String startReason) {
try {
//先停止应用切换功能
ActivityManager.getService().stopAppSwitches();
} catch (RemoteException e) {}
//关闭系统当前存在的各种弹窗
sendCloseSystemWindows(SYSTEM_DIALOG_REASON_HOME_KEY);
...代码暂时省略...
}
}
2、PhoneWindowManager的sendCloseSystemWindows方法如下所示。
public class PhoneWindowManager implements WindowManagerPolicy {
void sendCloseSystemWindows(String reason) {
//调用PhoneWindow的sendCloseSystemWindows方法
PhoneWindow.sendCloseSystemWindows(mContext, reason);
}
}
sendCloseSystemWindows方法直接调用了PhoneWindow的sendCloseSystemWindows方法
3、PhoneWindow的sendCloseSystemWindows方法如下所示。
frameworks/base/core/java/com/android/internal/policy/PhoneWindow.java
public class PhoneWindow extends Window implements MenuBuilder.Callback {
public static void sendCloseSystemWindows(Context context, String reason) {
if (ActivityManager.isSystemReady()) {
try {
//调用AMS的closeSystemDialogs方法
ActivityManager.getService().closeSystemDialogs(reason);
} catch (RemoteException e) {
}
}
}
}
PhoneWindow的sendCloseSystemWindows方法会进一步调用ActivityManagerService的sendCloseSystemWindows方法。
4、ActivityManagerService的sendCloseSystemWindows方法如下所示。
public class ActivityManagerService extends IActivityManager.Stub
implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback, ActivityManagerGlobalLock {
public ActivityTaskManagerInternal mAtmInternal;
public ActivityManagerService(Context systemContext, ActivityTaskManagerService atm) {
...代码省略...
mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
...代码省略...
}
@Override
public void closeSystemDialogs(String reason) {
//调用AMS的closeSystemDialogs方法
mAtmInternal.closeSystemDialogs(reason);
}
}
ActivityManagerService的closeSystemDialogs方法又进一步调用ActivityTaskManagerInternal的closeSystemDialogs方法。
5、ActivityTaskManagerInternal是一个抽象类,其内部类LocalService继承并实现了这个类的抽象方法。
frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
public abstract class ActivityTaskManagerInternal {
public abstract void closeSystemDialogs(String reason);
}
public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
final ActivityTaskManagerInternal mInternal;
public ActivityTaskManagerService(Context context) {
...代码省略...
mInternal = new LocalService();//创建ActivityTaskManagerInternal对象实例
...代码省略...
}
private void start() {
//将mInternal存储到LocalServices中
LocalServices.addService(ActivityTaskManagerInternal.class, mInternal);
}
RootWindowContainer mRootWindowContainer;//窗口根对象
WindowManagerService mWindowManager;//窗口管理者服务
public void setWindowManager(WindowManagerService wm) {
synchronized (mGlobalLock) {
mWindowManager = wm;
mRootWindowContainer = wm.mRoot;
...代码省略...
}
}
final class LocalService extends ActivityTaskManagerInternal {
@Override
public void closeSystemDialogs(String reason) {
enforceNotIsolatedCaller("closeSystemDialogs");
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
if (!checkCanCloseSystemDialogs(pid, uid, null)) {
return;
}
final long origId = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
// Only allow this from foreground processes, so that background
// applications can't abuse it to prevent system UI from being shown.
if (uid >= FIRST_APPLICATION_UID) {
final WindowProcessController proc = mProcessMap.getProcess(pid);
if (!proc.isPerceptible()) {
Slog.w(TAG, "Ignoring closeSystemDialogs " + reason
+ " from background process " + proc);
return;
}
}
//调用窗口管理者服务对象WMS的closeSystemDialogs方法
mWindowManager.closeSystemDialogs(reason);
//调用窗口根对象RootWindowContainer的closeSystemDialogActivities方法。
mRootWindowContainer.closeSystemDialogActivities(reason);
}
// Call into AM outside the synchronized block.
mAmInternal.broadcastCloseSystemDialogs(reason);
} finally {
Binder.restoreCallingIdentity(origId);
}
}
}
}
ActivityTaskManagerService的closeSystemDialogs会进一步调用MWS的closeSystemDialogs和RootWindowContainer的closeSystemDialogActivities方法。
6、WindowManagerService的closeSystemDialogs方法如下所示。
public class WindowManagerService extends IWindowManager.Stub
implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
RootWindowContainer mRoot;
@Override
public void closeSystemDialogs(String reason) {
int callingPid = Binder.getCallingPid();
int callingUid = Binder.getCallingUid();
if (!mAtmInternal.checkCanCloseSystemDialogs(callingPid, callingUid, null)) {
return;
}
synchronized (mGlobalLock) {
mRoot.closeSystemDialogs(reason);
}
}
}
closeSystemDialogs方法最终会调用RootWindowContainer的closeSystemDialogs方法。
7、来看下RootWindowContainer的closeSystemDialogs和closeSystemDialogActivities方法。
class RootWindowContainer extends WindowContainer<DisplayContent>
implements DisplayManager.DisplayListener {
private final Consumer<WindowState> mCloseSystemDialogsConsumer = w -> {
if (w.mHasSurface) {
try {
//调用WindowState内部类mClient的closeSystemDialogs方法。
w.mClient.closeSystemDialogs(mCloseSystemDialogsReason);
} catch (RemoteException e) {
}
}
};
//关闭系统弹窗
void closeSystemDialogs(String reason) {
mCloseSystemDialogsReason = reason;
//触发mCloseSystemDialogsConsumer回调对象的方法
forAllWindows(mCloseSystemDialogsConsumer, false /* traverseTopToBottom */);
}
//关闭系统弹窗Activity
void closeSystemDialogActivities(String reason) {
forAllActivities((r) -> {
if ((r.info.flags & ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS) != 0
|| shouldCloseAssistant(r, reason)) {
//r是WindowContainer对象实例,这里遍历并依次调用ActivityRecord的finishIfPossible
r.finishIfPossible(reason, true /* oomAdj */);
}
});
}
}
RootWindowContainer的closeSystemDialogs方法最终会调用WindowState内部类mClient的closeSystemDialogs方法。
8、WindowState的内部类mClient属于IWindow,而IWindow是一个aidl文件。
class WindowState extends WindowContainer<WindowState> implements WindowManagerPolicy.WindowState,
InsetsControlTarget, InputTarget {
final IWindow mClient;//窗口客户端
}
frameworks/base/core/java/android/view/IWindow.aidl
oneway interface IWindow {
void closeSystemDialogs(String reason);
}
9、ViewRootImpl的内部类W实现了IWindow的抽象方法。
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks,
AttachedSurfaceControl {
final W mWindow;
public ViewRootImpl(@UiContext Context context, Display display, IWindowSession session,
boolean useSfChoreographer) {
...代码省略...
mWindow = new W(this);//创建W对象实例,将自己作为参数传入
...代码省略...
}
static class W extends IWindow.Stub {
private final WeakReference<ViewRootImpl> mViewAncestor;
private final IWindowSession mWindowSession;
W(ViewRootImpl viewAncestor) {
//viewAncestor指向了ViewRootImpl
mViewAncestor = new WeakReference<ViewRootImpl>(viewAncestor);
mWindowSession = viewAncestor.mWindowSession;
}
@Override
public void closeSystemDialogs(String reason) {
final ViewRootImpl viewAncestor = mViewAncestor.get();
if (viewAncestor != null) {
//调用ViewRootImpl的dispatchCloseSystemDialogs
viewAncestor.dispatchCloseSystemDialogs(reason);
}
}
}
private static final int MSG_CLOSE_SYSTEM_DIALOGS = 14;
View mView;//指向了DecorView
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView, int userId) {
synchronized (this) {
if (mView == null) {
mView = view;
}
}
}
public void dispatchCloseSystemDialogs(String reason) {
Message msg = Message.obtain();
msg.what = MSG_CLOSE_SYSTEM_DIALOGS;
msg.obj = reason;
mHandler.sendMessage(msg);
}
final class ViewRootHandler extends Handler {
private void handleMessageImpl(Message msg) {
switch (msg.what) {
case MSG_CLOSE_SYSTEM_DIALOGS: {
if (mView != null) {
//调用DecorView的onCloseSystemDialogs方法
mView.onCloseSystemDialogs((String) msg.obj);
}
} break;
}
}
}
}
ViewRootImpl的内部类W的closeSystemDialogs方法继续调用ViewRootImpl的dispatchCloseSystemDialogs方法,该方法调用ViewRootHandler发送类型为MSG_CLOSE_SYSTEM_DIALOGS的消息,然后在ViewRootHandler的handleMessageImpl方法的MSG_CLOSE_SYSTEM_DIALOGS消息分支中,最终会调用DecorView的onCloseSystemDialogs方法。
10、DecorView的onCloseSystemDialogs方法最终会调用PhoneWindow的closeAllPanels方法。
public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {
private PhoneWindow mWindow;
@Override
public void onCloseSystemDialogs(String reason) {
if (mFeatureId >= 0) {
mWindow.closeAllPanels();
}
}
}
四、唤醒屏保。
1、PhoneWindowManager的startDockOrHome在调用sendCloseSystemWindows关闭系统当前存在的各种弹窗之后,会调用awakenDreams方法唤醒屏保。
public class PhoneWindowManager implements WindowManagerPolicy {
static public final String SYSTEM_DIALOG_REASON_HOME_KEY = "homekey";
ActivityTaskManagerInternal mActivityTaskManagerInternal;
void startDockOrHome(int displayId, boolean fromHomeKey, boolean awakenFromDreams,
String startReason) {
try {
//先停止应用切换功能
ActivityManager.getService().stopAppSwitches();
} catch (RemoteException e) {}
//关闭系统当前存在的各种弹窗
sendCloseSystemWindows(SYSTEM_DIALOG_REASON_HOME_KEY);
if (awakenFromDreams) {
awakenDreams();//唤醒屏保
}
...代码暂时省略...
}
}
2、PhoneWindowManager的awakenDreams方法如下所示。
public class PhoneWindowManager implements WindowManagerPolicy {
//唤醒屏保
private static void awakenDreams() {
IDreamManager dreamManager = getDreamManager();
if (dreamManager != null) {
try {
//调用屏保服务DreamManagerService的awaken方法唤醒屏保
dreamManager.awaken();
} catch (RemoteException e) {
// fine, stay asleep then
}
}
}
static IDreamManager getDreamManager() {
return IDreamManager.Stub.asInterface(
ServiceManager.checkService(DreamService.DREAM_SERVICE));
}
}
3、IDreamManager是一个aidl。
frameworks/base/core/java/android/service/dreams/IDreamManager.aidl
interface IDreamManager {
@UnsupportedAppUsage
void awaken();
}
4、DreamManagerService的内部类BinderService实现了awaken的抽象方法。
public final class DreamManagerService extends SystemService {
private final class BinderService extends IDreamManager.Stub {
@Override // Binder call
public void awaken() {
checkPermission(android.Manifest.permission.WRITE_DREAM_STATE);//检测应用是否有唤醒屏保的权限
final long ident = Binder.clearCallingIdentity();
try {
requestAwakenInternal();//调用DreamManagerService的requestAwakenInternal方法
} finally {
Binder.restoreCallingIdentity(ident);
}
}
}
private void requestAwakenInternal() {
// Treat an explicit request to awaken as user activity so that the
// device doesn't immediately go to sleep if the timeout expired,
// for example when being undocked.
long time = SystemClock.uptimeMillis();
mPowerManager.userActivity(time, false /*noChangeLights*/);
stopDreamInternal(false /*immediate*/, "request awaken");
}
}
BinderService的awaken方法显示检测权限,然后会调用DreamManagerService的requestAwakenInternal方法,关于requestAwakenInternal方法是如何唤醒屏保的,碍于文章篇幅,这里不做过多的展开。
五、创建首页意图对象。
1、PhoneWindowManager的startDockOrHome在调用awakenDreams方法唤醒屏保之后,会调用createHomeDockIntent创建首页意图对象。
public class PhoneWindowManager implements WindowManagerPolicy {
static public final String SYSTEM_DIALOG_REASON_HOME_KEY = "homekey";
ActivityTaskManagerInternal mActivityTaskManagerInternal;
void startDockOrHome(int displayId, boolean fromHomeKey, boolean awakenFromDreams,
String startReason) {
try {
//先停止应用切换功能
ActivityManager.getService().stopAppSwitches();
} catch (RemoteException e) {}
//关闭系统当前存在的各种弹窗
sendCloseSystemWindows(SYSTEM_DIALOG_REASON_HOME_KEY);
if (awakenFromDreams) {
awakenDreams();//唤醒屏保
}
if (!mHasFeatureAuto && !isUserSetupComplete()) {
Slog.i(TAG, "Not going home because user setup is in progress.");
return;
}
//创建桌面意图对象
Intent dock = createHomeDockIntent();
if (dock != null) {
//如果桌面意图对象不为空则打开该意图对象
try {
if (fromHomeKey) {
dock.putExtra(WindowManagerPolicy.EXTRA_FROM_HOME_KEY, fromHomeKey);
}
startActivityAsUser(dock, UserHandle.CURRENT);
return;
} catch (ActivityNotFoundException e) {
}
}
...代码暂时省略...
}
}
2、PhoneWindowManager的createHomeDockIntent方法如下所示。
public class PhoneWindowManager implements WindowManagerPolicy {
Intent mHomeIntent;
Intent mCarDockIntent;
Intent mDeskDockIntent;
Intent mVrHeadsetHomeIntent;
@Override
public void init(Context context, IWindowManager windowManager,
WindowManagerFuncs windowManagerFuncs) {
...代码省略...
mHomeIntent = new Intent(Intent.ACTION_MAIN, null);
mHomeIntent.addCategory(Intent.CATEGORY_HOME);
mHomeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
mEnableCarDockHomeCapture = context.getResources().getBoolean(
com.android.internal.R.bool.config_enableCarDockHomeLaunch);
mCarDockIntent = new Intent(Intent.ACTION_MAIN, null);
mCarDockIntent.addCategory(Intent.CATEGORY_CAR_DOCK);
mCarDockIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
mDeskDockIntent = new Intent(Intent.ACTION_MAIN, null);
mDeskDockIntent.addCategory(Intent.CATEGORY_DESK_DOCK);
mDeskDockIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
mVrHeadsetHomeIntent = new Intent(Intent.ACTION_MAIN, null);
mVrHeadsetHomeIntent.addCategory(Intent.CATEGORY_VR_HOME);
mVrHeadsetHomeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
...代码省略...
}
//创建首页意图对象
Intent createHomeDockIntent() {
Intent intent = null;
// What home does is based on the mode, not the dock state. That
// is, when in car mode you should be taken to car home regardless
// of whether we are actually in a car dock.
if (mUiMode == Configuration.UI_MODE_TYPE_CAR) {
if (mEnableCarDockHomeCapture) {
intent = mCarDockIntent;
}
} else if (mUiMode == Configuration.UI_MODE_TYPE_DESK) {
if (ENABLE_DESK_DOCK_HOME_CAPTURE) {
intent = mDeskDockIntent;
}
} else if (mUiMode == Configuration.UI_MODE_TYPE_WATCH) {
final int dockMode = mDefaultDisplayPolicy.getDockMode();
if (dockMode == Intent.EXTRA_DOCK_STATE_DESK
|| dockMode == Intent.EXTRA_DOCK_STATE_HE_DESK
|| dockMode == Intent.EXTRA_DOCK_STATE_LE_DESK) {
// Always launch dock home from home when watch is docked, if it exists.
intent = mDeskDockIntent;
}
} else if (mUiMode == Configuration.UI_MODE_TYPE_VR_HEADSET) {
if (ENABLE_VR_HEADSET_HOME_CAPTURE) {
intent = mVrHeadsetHomeIntent;
}
}
if (intent == null) {
return null;
}
ActivityInfo ai = null;
//获取Intent对应的ResolveInfo信息
ResolveInfo info = mPackageManager.resolveActivityAsUser(
intent,
PackageManager.MATCH_DEFAULT_ONLY | PackageManager.GET_META_DATA,
mCurrentUserId);
if (info != null) {
ai = info.activityInfo;
}
if (ai != null
&& ai.metaData != null
&& ai.metaData.getBoolean(Intent.METADATA_DOCK_HOME)) {
intent = new Intent(intent);
intent.setClassName(ai.packageName, ai.name);
return intent;
}
return null;
}
}
在PhoneWindowManager的init方法中,会为各种类型的首页意图对象赋值,然后createHomeDockIntent方法会根据当前系统所属的模式来选择对应的首页意图对象进行返回,startDockOrHome方法会判断该对象是否为空,不为空则直接调用startActivityAsUser(dock, UserHandle.CURRENT)方法打开该对象并返回,如果为空最后会调用ActivityTaskManagerInternal的startHomeOnDisplay方法。
六、调用startHomeOnDisplay方法打开首页。
1、如果在第五步没有成功打开首页意图对象,PhoneWindowManager的startDockOrHome方法会继续调用ActivityTaskManagerInternal的startHomeOnDisplay方法打开首页。
public class PhoneWindowManager implements WindowManagerPolicy {
static public final String SYSTEM_DIALOG_REASON_HOME_KEY = "homekey";
ActivityTaskManagerInternal mActivityTaskManagerInternal;
void startDockOrHome(int displayId, boolean fromHomeKey, boolean awakenFromDreams,
String startReason) {
try {
//先停止应用切换功能
ActivityManager.getService().stopAppSwitches();
} catch (RemoteException e) {}
//关闭系统当前存在的各种弹窗
sendCloseSystemWindows(SYSTEM_DIALOG_REASON_HOME_KEY);
if (awakenFromDreams) {
awakenDreams();//唤醒屏保
}
if (!mHasFeatureAuto && !isUserSetupComplete()) {
Slog.i(TAG, "Not going home because user setup is in progress.");
return;
}
//创建桌面意图对象
Intent dock = createHomeDockIntent();
if (dock != null) {
//如果桌面意图对象不为空则打开该意图对象
try {
if (fromHomeKey) {
dock.putExtra(WindowManagerPolicy.EXTRA_FROM_HOME_KEY, fromHomeKey);
}
startActivityAsUser(dock, UserHandle.CURRENT);
return;
} catch (ActivityNotFoundException e) {
}
}
//调用ATMS的startHomeOnDisplay方法打开首页
mActivityTaskManagerInternal.startHomeOnDisplay(mCurrentUserId, startReason,
displayId, true /* allowInstrumenting */, fromHomeKey);
}
}
2、在第三步第5节,我们有讲过ActivityTaskManagerInternal是一个抽象类,ActivityTaskManagerService的内部类LocalService继承自这个抽象类并实现了抽象方法。
public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
RootWindowContainer mRootWindowContainer;//窗口跟对象
final class LocalService extends ActivityTaskManagerInternal {
@Override
public boolean startHomeOnDisplay(int userId, String reason, int displayId,
boolean allowInstrumenting, boolean fromHomeKey) {
synchronized (mGlobalLock) {
return mRootWindowContainer.startHomeOnDisplay(userId, reason, displayId,
allowInstrumenting, fromHomeKey);
}
}
}
}
ActivityTaskManagerService的内部类LocalService的startHomeOnDisplay方法会进一步调用RootWindowContainer的startHomeOnDisplay方法。
3、RootWindowContainer的startHomeOnDisplay方法如下所示。
frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java
class RootWindowContainer extends WindowContainer<DisplayContent>
implements DisplayManager.DisplayListener {
boolean startHomeOnDisplay(int userId, String reason, int displayId, boolean allowInstrumenting,
boolean fromHomeKey) {
// Fallback to top focused display or default display if the displayId is invalid.
if (displayId == INVALID_DISPLAY) {
//如果当前设备id不合法,则回退到顶部焦点设备或者默认设备
final Task rootTask = getTopDisplayFocusedRootTask();
displayId = rootTask != null ? rootTask.getDisplayId() : DEFAULT_DISPLAY;
}
final DisplayContent display = getDisplayContent(displayId);
return display.reduceOnAllTaskDisplayAreas((taskDisplayArea, result) ->
result | startHomeOnTaskDisplayArea(userId, reason, taskDisplayArea,
allowInstrumenting, fromHomeKey),
false /* initValue */);
}
}
💡 技术无价,赞赏随心
写文不易,如果本文帮你避开了“八小时踩坑”,或者让你直呼“学到了!”
欢迎扫码赞赏,让我知道这篇内容值得!