Android 12 system source code_input event (1) HOME key event response process

Preface

In this article, we mainly analyze the response process of the android system to the HOME button. The HOME button event is a system-level button event monitoring. In the Android system, the system-level button processing logic is in the PhoneWindowManager class.

1. The interceptKeyBeforeDispatching method distributes key events

1. The dispatchUnhandledKey method of PhoneWindowManager is the first to receive system-level key events.

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;
    }
}

The dispatchUnhandledKey method of PhoneWindowManager will continue to trigger another key method, interceptFallback, and the interceptFallback method will further call the interceptKeyBeforeDispatching method.

2. The interceptKeyBeforeDispatching method of PhoneWindowManager is as follows.

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;
    }

The interceptKeyBeforeDispatching method will respond to various key events through each case branch of the switch. We mainly focus on the response process of the HOME key event. The system first obtains the cache object of DisplayHomeButtonHandler type from the mDisplayHomeButtonHandlers collection. If the cache object does not exist, a DisplayHomeButtonHandler is created. Object and added to the collection, anyway, the handleHomeButton method of DisplayHomeButtonHandler will eventually be called.

3. DisplayHomeButtonHandler is the internal class of PhoneWindowManager. The handleHomeButton method is as follows.

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);
    }
}

The handleHomeButton method first obtains whether the Action type of the button is pressed, and performs conditional judgment. Only when the button is lifted will the related operations of returning to the home page be triggered. In order to avoid blocking the input pipeline, the current thread is passed through the Post method of the Handler. Switch to the main thread, and further call the handleShortPressOnHome method, which in turn calls the launchHomeFromHotKey method.

4. The launchHomeFromHotKey method of PhoneWindowManager is as follows.

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);
        }
    }
 }

The launchHomeFromHotKey method first determines whether the screen is currently locked. If the screen is locked, it must be unlocked before the home page can be opened. Then it will be judged whether the recent task is visible:

  • If it is visible, the stopAppSwitches method of AMS will be called to stop the application switching function. The purpose of this method is mainly to pause the operation of opening the Activity in the background to avoid disturbing the user's operation. For example, at this time, if we open a new App in the background, then because we need to return Go to the home page, so you need to delay opening it first. After stopping the application switching function, hideRecentApps will also be called to hide recent tasks.
  • If the recent task is not visible, the startDockOrHome method will be called directly to open the homepage.

2. Pause the function of application switching Activity.

1. The startDockOrHome method of PhoneWindowManager is as follows.

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) {
    
    }
		...代码暂时省略...
    }
}

The startDockOrHome method first calls the ActivityManager.getService().stopAppSwitches() method to pause the application switching Activity function.

2. The stopAppSwitches method of ActivityManagerService is as follows.

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();
    }
}

The stopAppSwitches method of ActivityManagerService does nothing but further calls the stopAppSwitches method of ActivityTaskManagerService.

3. Continue to look at the code related to the stopAppSwitches method of ActivityTaskManagerService.

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;
            }
   }
}
  • The stopAppSwitches method will modify the value of mAppSwitchesState to APP_SWITCH_DISALLOW, which means that the application is not allowed to switch activities, and will send a message of type RESUME_FG_APP_SWITCH_MSG through the Handler, delaying execution for 500 milliseconds, triggering the handleMessage method, and in the RESUME_FG_APP_SWITCH_MSG message branch, modify the value of mAppSwitchesState to APP_SWITCH_FG_ONLY, which only allows foreground applications to switch activities.
  • When AMS starts a new Activity, ActivityStarter will call the getBalAppSwitchesState method of ATMS to determine whether the new Activity is currently allowed to be opened.

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;
	        }
	       ...代码省略...
    }
    
}

We will not go into too much detail about the startup process of Activity here. For details, please refer to the article Android 12 System Source Code_Page Management (2) Activity Startup Process (Part 1) .

3. Close various pop-up windows currently existing in the system

1. After calling the stopAppSwitches method of PhoneWindowManager's startDockOrHome to pause the application switching Activity function, it will call the sendCloseSystemWindows method to close various pop-up windows currently existing in the system.

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. The sendCloseSystemWindows method of PhoneWindowManager is as follows.

public class PhoneWindowManager implements WindowManagerPolicy {
    
    

    void sendCloseSystemWindows(String reason) {
    
    
    	//调用PhoneWindow的sendCloseSystemWindows方法
        PhoneWindow.sendCloseSystemWindows(mContext, reason);
    }
}

The sendCloseSystemWindows method directly calls the sendCloseSystemWindows method of PhoneWindow.

3. The sendCloseSystemWindows method of PhoneWindow is as follows.

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) {
    
    
            }
        }
    }
}

The sendCloseSystemWindows method of PhoneWindow will further call the sendCloseSystemWindows method of ActivityManagerService.

4. The sendCloseSystemWindows method of ActivityManagerService is as follows.

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);
    }
}

The closeSystemDialogs method of ActivityManagerService further calls the closeSystemDialogs method of ActivityTaskManagerInternal.

5. ActivityTaskManagerInternal is an abstract class, and its internal class LocalService inherits and implements the abstract method of this class.

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's closeSystemDialogs will further call MWS's closeSystemDialogs and RootWindowContainer's closeSystemDialogActivities methods.

6. The closeSystemDialogs method of WindowManagerService is as follows.

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);
        }
    }
}

The closeSystemDialogs method will eventually call the closeSystemDialogs method of RootWindowContainer.

7. Let’s take a look at the closeSystemDialogs and closeSystemDialogActivities methods of RootWindowContainer.

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 */);
            }
        });
    }
    
}

The closeSystemDialogs method of RootWindowContainer will eventually call the closeSystemDialogs method of WindowState internal class mClient.

8. WindowState’s internal class mClient belongs to IWindow, and IWindow is an aidl file.

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. The internal class W of ViewRootImpl implements the abstract method of 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;
            }
       }
   }
   
 }

The closeSystemDialogs method of the internal class W of ViewRootImpl continues to call the dispatchCloseSystemDialogs method of ViewRootImpl, which calls ViewRootHandler to send a message of type MSG_CLOSE_SYSTEM_DIALOGS, and then in the MSG_CLOSE_SYSTEM_DIALOGS message branch of the handleMessageImpl method of ViewRootHandler, the onCloseSystemDialogs method of DecorView is finally called.

10. DecorView’s onCloseSystemDialogs method will eventually call PhoneWindow’s closeAllPanels method.

public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {
    
    
    
    private PhoneWindow mWindow;
    
    @Override
    public void onCloseSystemDialogs(String reason) {
    
    
        if (mFeatureId >= 0) {
    
    
            mWindow.closeAllPanels();
        }
    }
    
}

4. Wake up the screensaver.

1. After calling sendCloseSystemWindows to close various pop-up windows currently existing in the system, PhoneWindowManager's startDockOrHome will call the awakeDreams method to wake up the screensaver.

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. The awakenDreams method of PhoneWindowManager is as follows.

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 is an aidl.

frameworks/base/core/java/android/service/dreams/IDreamManager.aidl

interface IDreamManager {
    
    
    @UnsupportedAppUsage
    void awaken();
}

4. The internal class BinderService of DreamManagerService implements the abstract method of awake.

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");
    }
}

The awaken method of BinderService displays the detection permission, and then calls the requestAwakenInternal method of DreamManagerService. Regarding how the requestAwakenInternal method wakes up the screensaver, due to the length of the article, I will not go into too much detail here.

5. Create the home page intent object.

1. After calling the awakenDreams method to wake up the screensaver, PhoneWindowManager's startDockOrHome will call createHomeDockIntent to create the home page intent object.

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. The createHomeDockIntent method of PhoneWindowManager is as follows.

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;
    }
}

In the init method of PhoneWindowManager, various types of home page intent objects will be assigned values. Then the createHomeDockIntent method will select the corresponding home page intent object and return it according to the mode of the current system. The startDockOrHome method will determine whether the object is empty or not. If it is empty, the startActivityAsUser(dock, UserHandle.CURRENT) method will be called directly to open the object and return. If it is empty, the startHomeOnDisplay method of ActivityTaskManagerInternal will be called.

6. Call the startHomeOnDisplay method to open the homepage.

1. If the homepage intent object is not successfully opened in step 5, the startDockOrHome method of PhoneWindowManager will continue to call the startHomeOnDisplay method of ActivityTaskManagerInternal to open the homepage.

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. In Step 3, Section 5, we mentioned that ActivityTaskManagerInternal is an abstract class. The internal class LocalService of ActivityTaskManagerService inherits from this abstract class and implements abstract methods.

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);
            }
        }
    }

} 

The startHomeOnDisplay method of the internal class LocalService of ActivityTaskManagerService will further call the startHomeOnDisplay method of RootWindowContainer.

3. The startHomeOnDisplay method of RootWindowContainer is as follows.

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 */);
    }
 }

Guess you like

Origin blog.csdn.net/abc6368765/article/details/132164705