深入理解 WindowManagerService

作者: lijiankun24 | 来源:发表于2019-01-23 14:08 被阅读283次

    在上篇文章中 初步理解 Window 体系,我们初步分析了 Window 的体系,这篇文章我们分析一下 WindowManagerService(以下简称 WMS)。WMS 错综负责,与 ActivityManagerService、InputManagerService、SurfaceFlinger 关系也很紧密,如果想分析的清楚彻底,恐怕是一两篇文章难以做到的。本篇文章初步分析 WMS 的创建,以及应用进程中的 WindowManager 与 WMS 通信。

    一. 理解 WindowManagerService 相关知识

    1.1 WindowManagerService 的诞生

    1.1.1 在 SystemServer 中的创建

    WMS 是在 SystemServer 进程中启动的,SystemServer 进程是 Android 系统启动的时候初始化的,我们首先来看一下 SystemServer 的入口函数 main()

    public final class SystemServer {
    
        /**
         * The main entry point from zygote.
         */
        public static void main(String[] args) {
            new SystemServer().run();
        }
    }
    

    从上述代码可见,是创建了一个 SystemServer 对象并调用了 run() 方法

    
        private void run() {
    
            ......
            mSystemServiceManager = new SystemServiceManager(mSystemContext);  // 代码 1
    
            // Start services.
            try {
                traceBeginAndSlog("StartServices");
                startBootstrapServices();                              // 代码 2
                startCoreServices();                                   // 代码 3
                startOtherServices();                                  // 代码 4
                SystemServerInitThreadPool.shutdown();
            } catch (Throwable ex) {
                Slog.e("System", "******************************************");
                Slog.e("System", "************ Failure starting system services", ex);
                throw ex;
            } finally {
                traceEnd();
            }
        }
    
    • 代码1处,创建了一个 SystemServiceManager 对象,用于创建各种系统服务并管理他们的生命周期
    • 代码2处,调用 startBootstrapServices() 启动 ActivityManagerService、PackageManagerService 等服务进程
    • 代码3处,调用 startCoreServices() 启动BatteryService、WebViewUpdateService 等服务进程 startOtherServices() 启动
    • 代码4处,调用 startOtherServices() 启动 WindowManagerService、InputManagerService 等服务进程

    startOtherServices() 方法很长,我们分析下其中和 WMS 相关的部分

        private void startOtherServices() {
            final Context context = mSystemContext;
            WindowManagerService wm = null;
            InputManagerService inputManager = null;
            
            ......
            try {
                // 代码 1
                traceBeginAndSlog("StartInputManagerService");
                inputManager = new InputManagerService(context);
                traceEnd();
    
                // 代码 2
                traceBeginAndSlog("StartWindowManagerService");
                // WMS needs sensor service ready
                ConcurrentUtils.waitForFutureNoInterrupt(mSensorServiceStart, START_SENSOR_SERVICE);
                mSensorServiceStart = null;
                wm = WindowManagerService.main(context, inputManager,
                        mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
                        !mFirstBoot, mOnlyCore, new PhoneWindowManager());
                ServiceManager.addService(Context.WINDOW_SERVICE, wm);
                ServiceManager.addService(Context.INPUT_SERVICE, inputManager);
                traceEnd();
    
                // 代码 3
                traceBeginAndSlog("SetWindowManagerService");
                mActivityManagerService.setWindowManager(wm);
                traceEnd();
    
                // 代码 4
                traceBeginAndSlog("StartInputManager");
                inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
                inputManager.start();
                traceEnd();
                ......
            } catch (RuntimeException e) {
                Slog.e("System", "******************************************");
                Slog.e("System", "************ Failure starting core service", e);
            }
            ......
            // 代码 5
            traceBeginAndSlog("MakeDisplayReady");
            try {
                wm.displayReady();
            } catch (Throwable e) {
                reportWtf("making display ready", e);
            }
            traceEnd();
            ......
            // 代码 6
            traceBeginAndSlog("MakeWindowManagerServiceReady");
            try {
                wm.systemReady();
            } catch (Throwable e) {
                reportWtf("making Window Manager Service ready", e);
            }
            traceEnd();
    
            if (safeMode) {
                mActivityManagerService.showSafeModeOverlay();
            }
            // 代码 7
            // Update the configuration for this context by hand, because we're going
            // to start using it before the config change done in wm.systemReady() will
            // propagate to it.
            final Configuration config = wm.computeNewConfiguration(DEFAULT_DISPLAY);
            DisplayMetrics metrics = new DisplayMetrics();
            WindowManager w = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
            w.getDefaultDisplay().getMetrics(metrics);
            context.getResources().updateConfiguration(config, metrics);
            ......
        }
    
    • 代码1处,创建了 InputManagerService 对象,InputManagerService 主要用于接收系统的输入事件,包括按键、触摸等
    • 代码2处,调用 WindowManager.main() 方法创建 WindowManagerService 对象,并将 WindowManagerService 和 InputManagerService 对象添加到 ServiceManager 中
    • 代码3处,为 ActivityManagerService 对象设置 WindowManagerService 对象
    • 代码4处,为 InputManagerService 设置 WindowManagerService 对象的 InputMonitor 对象,并启动 InputManagerService 对象
    • 代码5处,调用 WMS 的 displayReady() 方法初始化显示信息
    • 代码6处,调用 WMS 的 systemReady() 方法通知 WMS 系统的初始化工作完成
    • 代码7处,为 Context 中的 WindowManagerImpl 实例对象设置 DisplayMetrics 对象,并更新当前 Context 的 Resources 中的 Configuration 和 DisplayMetircs 属性
    1.1.2 WMS 的构造函数

    在上面一段代码中,最重要的莫过于调用 WMS 的 main() 方法创建一个 WindowManagerService 对象了,我们来分析下这个方法

    public class WindowManagerService extends IWindowManager.Stub
            implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
    
        private static WindowManagerService sInstance;
        static WindowManagerService getInstance() {
            return sInstance;
        }
    
        public static WindowManagerService main(final Context context, final InputManagerService im,
                final boolean haveInputMethods, final boolean showBootMsgs, final boolean onlyCore,
                WindowManagerPolicy policy) {
            DisplayThread.getHandler().runWithScissors(() ->
                    sInstance = new WindowManagerService(context, im, haveInputMethods, showBootMsgs,
                            onlyCore, policy), 0);
            return sInstance;
        }
    
    }
    
    • 我们看到在 main() 方法中,在 DisplayThread 线程中通过 WMS 的构造方法创建一个 WMS 实例对象
    • DisplayThread 线程是一个系统前台线程,用于执行一些延时要非常小的关于显示的操作,一般只会在 WindowManager、DisplayManager 和 InputManager 中使用,代码也比较简单,如下所示:
      /**
       * Shared singleton foreground thread for the system.  This is a thread for
       * operations that affect what's on the display, which needs to have a minimum
       * of latency.  This thread should pretty much only be used by the WindowManager,
       * DisplayManager, and InputManager to perform quick operations in real time.
       */
      public final class DisplayThread extends ServiceThread {
          private static DisplayThread sInstance;
          private static Handler sHandler;
      
          private DisplayThread() {
              // DisplayThread runs important stuff, but these are not as important as things running in
              // AnimationThread. Thus, set the priority to one lower.
              super("android.display", Process.THREAD_PRIORITY_DISPLAY + 1, false /*allowIo*/);
          }
      
          private static void ensureThreadLocked() {
              if (sInstance == null) {
                  sInstance = new DisplayThread();
                  sInstance.start();
                  sInstance.getLooper().setTraceTag(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                  sHandler = new Handler(sInstance.getLooper());
              }
          }
      
          public static DisplayThread get() {
              synchronized (DisplayThread.class) {
                  ensureThreadLocked();
                  return sInstance;
              }
          }
      
          public static Handler getHandler() {
              synchronized (DisplayThread.class) {
                  ensureThreadLocked();
                  return sHandler;
              }
          }
      }
      

    我们接着上面的 main() 方法分析,上面代码调用了 WMS 的构造方法创建了 WMS 实例对象,我们来看一下 WMS 中的一些重要的成员属性和构造方法,如下所示:

    public class WindowManagerService extends IWindowManager.Stub
            implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
    
        final WindowManagerPolicy mPolicy;
        final ArraySet<Session> mSessions = new ArraySet<>();
        final WindowHashMap mWindowMap = new WindowHashMap();
        final ArrayList<AppWindowToken> mFinishedStarting = new ArrayList<>();
    
        final H mH = new H();
    
        final InputManagerService mInputManager;
    
        final WindowAnimator mAnimator;
    
        private WindowManagerService(Context context, InputManagerService inputManager,
                boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore,
                WindowManagerPolicy policy) {
    
            ......
            // 代码 1
            mInputManager = inputManager; // Must be before createDisplayContentLocked.
            // 代码 2 
            mPolicy = policy;
            if(mInputManager != null) {
                final InputChannel inputChannel = mInputManager.monitorInput(TAG_WM);
                mPointerEventDispatcher = inputChannel != null
                        ? new PointerEventDispatcher(inputChannel) : null;
            } else {
                mPointerEventDispatcher = null;
            }
    
            ......
            // 代码 3
            mAnimator = new WindowAnimator(this);
    
            ......
            // 代码 4
            initPolicy();
    
            // 代码 5
            // Add ourself to the Watchdog monitors.
            Watchdog.getInstance().addMonitor(this);
            ......
        }
    }
    
    • 代码1 处,保存 SystemServer 中传入的 InputManagerService 实例对象,输入事件最终要分发给具有焦点的窗口,而 WMS 是窗口的管理者。mInputManager 用于管理每个窗口的输入事件通道,并向通道上派发事件
    • 代码2 处,mPolicy 对象是 WMS 中非常重要的一个对象,是 WindowManagerPolicy 类型的,WindowManagerPolicy(简称 WMP) 是一个接口,具体的实现类是 PhoneWindowManagermPolicy 对象可以说是 WMS 的首席顾问,WMS 的许多操作都是需要 WMP 规定的,比如:多个窗口的上下顺序,监听屏幕旋转的状态,预处理一些系统按键事件(例如HOME、BACK键等的默认行为就是在这里实现的)
    • 代码3 处,创建一个 WindowAnimator 对象,用于管理所有窗口的动画
    • 代码4 处,初始化 mPolicy 对象
    • 代码5 处,将 WMS 实例对象本身添加到 Watchdog 中,WMS 类实现了 Watchdog.Monitor 接口。Watchdog 用于监控系统的一些关键服务

    1.2 WMS 中的几个重要概念

    除开上面构造方法中提到的一些成员属性之外,还有一些非常重要的概念需要理解

    1.2.1 Session

    在上篇文章 初步理解 Window 体系 中,我们提到 ViewRootImpl 和 WMS 之间的通信就是通过 Session 对象完成的。
    Session 类继承自 IWindowSession.Stub,每一个应用进程都有一个唯一的 Session 对象与 WMS 通信,如下图所示

    Session.png

    图片来源:Window与WMS通信过程

    在 WMS 中的 mSessions 成员属性,是 ArraySet 类型,其中保存着 Session 型的对象。

    public class WindowManagerService extends IWindowManager.Stub
            implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
            /**
             * All currently active sessions with clients.
             */
            final ArraySet<Session> mSessions = new ArraySet<>();
    
            ......
    
            @Override
            public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client,
                IInputContext inputContext) {
                if (client == null) throw new IllegalArgumentException("null client");
                if (inputContext == null) throw new IllegalArgumentException("null inputContext");
                Session session = new Session(this, callback, client, inputContext);
                return session;
        }
    }
    
    public class Session extends IWindowSession.Stub
            implements IBinder.DeathRecipient {
        final WindowManagerService mService;
        private int mNumWindow = 0;                  // 代码 1
        ......
    
        // 代码 2
        public Session(WindowManagerService service, IWindowSessionCallback callback,
                IInputMethodClient client, IInputContext inputContext) {
            mService = service;
            ......
        }
    
        void windowAddedLocked(String packageName) {
            mPackageName = packageName;
            mRelayoutTag = "relayoutWindow: " + mPackageName;
            if (mSurfaceSession == null) {
                if (WindowManagerService.localLOGV) Slog.v(
                    TAG_WM, "First window added to " + this + ", creating SurfaceSession");
                mSurfaceSession = new SurfaceSession();
                if (SHOW_TRANSACTIONS) Slog.i(
                        TAG_WM, "  NEW SURFACE SESSION " + mSurfaceSession);
                // 代码 3
                mService.mSessions.add(this);
                if (mLastReportedAnimatorScale != mService.getCurrentAnimatorScale()) {
                    mService.dispatchNewAnimatorScaleLocked(this);
                }
            }
            mNumWindow++;
        }
    
        void windowRemovedLocked() {
            mNumWindow--;
            killSessionLocked();
        }
    
        private void killSessionLocked() {
            if (mNumWindow > 0 || !mClientDead) {
                return;
            }
            // 代码 4
            mService.mSessions.remove(this);
            ......
        }
    
    }
    
    • 代码 1 处,mNumWindow 变量记录着此 Session 中共有多少个 Window
    • 代码 2 处的 Session 的构造方法中,mService 保存着 WMS 的实例对象
    • 代码 3 处,将此 Session 对象添加进 WMS 的 mSessions 队列中
    • 代码 4 处,将此 Session 对象从 WMS 的 mSessions 队列中移除
    1.2.2 WindowState

    WindowState 是 WMS 中一个重要的概念,在 WMS 中的一个 WindowState 对象就对应着一个应用进程中的 Window 对象。
    我们在上篇文章 初步理解 Window 体系 最后,在调用WindowManagerGlobal.addView 方法时,经过一系列的方法调用,最后走到了 WindowManagerService.addWindow 方法中

    addWindow.png

    在 WindowManagerService.addWindow 方法中,会创建一个与 Window 对象对应的 WindowState 对象并调用 WindowState.attach 方法,然后将该 WindowState 对象添加到 WMS 的 mWindowMap Map 中

    public class WindowManagerService extends IWindowManager.Stub
            implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
    
        final WindowHashMap mWindowMap = new WindowHashMap();
    
        public int addWindow(Session session, IWindow client, int seq,
                WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
                Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
                InputChannel outInputChannel) {
                ......
    
                final WindowState win = new WindowState(this, session, client, token, parentWindow,
                        appOp[0], seq, attrs, viewVisibility, session.mUid,
                        session.mCanAddInternalSystemWindow);
                ......
                win.attach();
                mWindowMap.put(client.asBinder(), win);
                ......
                win.mToken.addWindow(win);
        }
    }
    
    class WindowState extends WindowContainer<WindowState> implements WindowManagerPolicy.WindowState {
        final WindowManagerService mService;
        final WindowManagerPolicy mPolicy;
        final Context mContext;
        final Session mSession;
        final IWindow mClient;
    
    
        WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
               WindowState parentWindow, int appOp, int seq, WindowManager.LayoutParams a,
               int viewVisibility, int ownerId, boolean ownerCanAddInternalSystemWindow) {
            mService = service;
            mSession = s;
            mClient = c;
            mAppOp = appOp;
            mToken = token;
            mAppToken = mToken.asAppWindowToken();
    
            ......
        }
    
        void attach() {
            if (localLOGV) Slog.v(TAG, "Attaching " + this + " token=" + mToken);
            mSession.windowAddedLocked(mAttrs.packageName);
        }
        
        ......
    }
    

    在 WindowState 中保存了 WMS 对象、WMP 对象、Session 对象和 IWindow 对象,IWindow 对象就是与此 WindowState 对象相对应的在应用进程中的 Window 对象。

    final WindowHashMap mWindowMap = new WindowHashMap(); 是一个 HashMap 的子类,key 是 IBinder,value 是 WindowState,用于保存 WMS 中所有的 WindowState 对象,

    /**
     * Subclass of HashMap such that we can instruct the compiler to boost our thread priority when
     * locking this class. See makefile.
     */
    class WindowHashMap extends HashMap<IBinder, WindowState> {
    }
    
     mWindowMap.put(client.asBinder(), win);
    

    IWindow client 对象,其实是 ViewRootImpl 中的 final W mWindow 成员,如下所示:

    public final class ViewRootImpl implements ViewParent,
            View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
        final W mWindow;
    
        ......
    
        static class W extends IWindow.Stub {
            ......
        }
        ......
    }
    
    1.2.3 WindowToken

    简单的理解,WindowToken 有两个作用:

    1. 在 WMS 中,一个 WindowToken 就代表着一个应用组件,应用组件包括:Activity、InputMethod 等。在 WMS 中,会将属于同一 WindowToken 的做统一处理,比如在对窗口进行 ZOrder 排序时,会将属于统一 WindowToken 的排在一起。
    2. WindowToken 也具有令牌的作用。应用组件在创建 Window 时都需要提供一个有效的 WindowToken 以表明自己的身份,并且窗口的类型必须与所持有的 WindowToken 类型保持一致。如果是系统类型的窗口,可以不用提供 WindowToken,WMS 会自动为该系统窗口隐式的创建 WindowToken,但是要求应用必须具有创建该系统类型窗口的权限

    概念上有了初步的理解,我们来看下 WindowToken 的代码,如下所示,在 WindowToken 类中,最重要的其实是其中的成员属性

    /**
     * Container of a set of related windows in the window manager. Often this is an AppWindowToken,
     * which is the handle for an Activity that it uses to display windows. For nested windows, there is
     * a WindowToken created for the parent window to manage its children.
     */
    class WindowToken extends WindowContainer<WindowState> {
        private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowToken" : TAG_WM;
    
        // The window manager!
        protected final WindowManagerService mService;
    
        // The actual token.
        final IBinder token;
    
        // The type of window this token is for, as per WindowManager.LayoutParams.
        final int windowType;    
        ......
    
        // The display this token is on.
        protected DisplayContent mDisplayContent;
        ......
    
        WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty,
                DisplayContent dc, boolean ownerCanManageAppTokens) {
            mService = service;
            token = _token;
            windowType = type;
            mPersistOnEmpty = persistOnEmpty;
            mOwnerCanManageAppTokens = ownerCanManageAppTokens;
            onDisplayChanged(dc);
        }
    
        void onDisplayChanged(DisplayContent dc) {
            dc.reParentWindowToken(this);
            mDisplayContent = dc;
    
            // TODO(b/36740756): One day this should perhaps be hooked
            // up with goodToGo, so we don't move a window
            // to another display before the window behind
            // it is ready.
            SurfaceControl.openTransaction();
            for (int i = mChildren.size() - 1; i >= 0; --i) {
                final WindowState win = mChildren.get(i);
                win.mWinAnimator.updateLayerStackInTransaction();
            }
            SurfaceControl.closeTransaction();
    
            super.onDisplayChanged(dc);
        }
    
        ......
    
    }
    

    其实,对于 WMS 来讲,只要是一个 IBinder 对象都可以作为 Token,比如在之前分析添加 Window 时,调用 WindowManagerService.addWindow 方法时,传入的 Token 对象就是一个 IWindow.Stub 的对象。我们来看一下 WMS 中的 addWindowToken 方法,如下所示:

        @Override
        public void addWindowToken(IBinder binder, int type, int displayId) {
            if (!checkCallingPermission(MANAGE_APP_TOKENS, "addWindowToken()")) {
                throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
            }
    
            synchronized(mWindowMap) {
                final DisplayContent dc = mRoot.getDisplayContentOrCreate(displayId);
                // 代码 1
                WindowToken token = dc.getWindowToken(binder);
                // 代码 2
                if (token != null) {
                    Slog.w(TAG_WM, "addWindowToken: Attempted to add binder token: " + binder
                            + " for already created window token: " + token
                            + " displayId=" + displayId);
                    return;
                }
                // 代码 3
                if (type == TYPE_WALLPAPER) {
                    new WallpaperWindowToken(this, binder, true, dc,
                            true /* ownerCanManageAppTokens */);
                } else {
                    new WindowToken(this, binder, type, true, dc, true /* ownerCanManageAppTokens */);
                }
            }
        }
    
    • 代码 1 处,从 DisplayContent 中取一个 WindowToken 对象。从这儿可以看出每一个 WindowToken 又具体是属于每个 DisplayContent 对象的,DisplayContent 对象可以理解为一块屏幕的对应,这个概念在之后详细介绍。
      class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowContainer> {
      
          WindowManagerService mService;
          // mTokenMap 一个 HashMap 对象,用于映射 IBinder 和 WindowToken 对象
          // Mapping from a token IBinder to a WindowToken object on this display.
          private final HashMap<IBinder, WindowToken> mTokenMap = new HashMap();
          
          ......
      
          // 从 mTokenMap 取出对应于 IBinder 的 WindowToken 对象
          WindowToken getWindowToken(IBinder binder) {
              return mTokenMap.get(binder);
          }
      
          // 在创建 WindowToken 对象时,会通过此方法将 WindowToken 从属于此 DisplayContent 对象,并添加到 mTokenMap 中
          /** Changes the display the input window token is housed on to this one. */
          void reParentWindowToken(WindowToken token) {
              final DisplayContent prevDc = token.getDisplayContent();
              if (prevDc == this) {
                  return;
              }
              if (prevDc != null && prevDc.mTokenMap.remove(token.token) != null
                  && token.asAppWindowToken() == null) {
                  // Removed the token from the map, but made sure it's not an app token before removing
                  // from parent.
                  token.getParent().removeChild(token);
              }
      
              addWindowToken(token.token, token);
          }
      
          private void addWindowToken(IBinder binder, WindowToken token) {
              final DisplayContent dc = mService.mRoot.getWindowTokenDisplay(token);
              if (dc != null) {
                  // We currently don't support adding a window token to the display if the display
                  // already has the binder mapped to another token. If there is a use case for supporting
                  // this moving forward we will either need to merge the WindowTokens some how or have
                  // the binder map to a list of window tokens.
                  throw new IllegalArgumentException("Can't map token=" + token + " to display="
                      + getName() + " already mapped to display=" + dc + " tokens=" + dc.mTokenMap);
              }
              if (binder == null) {
                  throw new IllegalArgumentException("Can't map token=" + token + " to display="
                      + getName() + " binder is null");
              }
              if (token == null) {
                  throw new IllegalArgumentException("Can't map null token to display="
                      + getName() + " binder=" + binder);
              }
      
              mTokenMap.put(binder, token);
      
              if (token.asAppWindowToken() == null) {
                  // Add non-app token to container hierarchy on the display. App tokens are added through
                  // the parent container managing them (e.g. Tasks).
                  switch (token.windowType) {
                      case TYPE_WALLPAPER:
                          mBelowAppWindowsContainers.addChild(token);
                          break;
                      case TYPE_INPUT_METHOD:
                      case TYPE_INPUT_METHOD_DIALOG:
                          mImeWindowsContainers.addChild(token);
                          break;
                      default:
                          mAboveAppWindowsContainers.addChild(token);
                          break;
                  }
              }
          }
      
          // 通过此方法,将 IBinder 所对应的 WindowToken 对象从此 DisplayContent 中的 mTokenMap 移除
          WindowToken removeWindowToken(IBinder binder) {
              final WindowToken token = mTokenMap.remove(binder);
              if (token != null && token.asAppWindowToken() == null) {
                  token.setExiting();
              }
              return token;
          }
      
          ......
      
      }
      
    • 代码 2 处,若该 IBinder 对象对应的 WindowToken 不为空,则返回,可见 一个 IBinder 对象只能创建一个对应的 WindowToken 对象。
    • 代码 3 处,根据 Window 的 type 类型创建对应的 WindowToken 对象。

    AppWindowToken 是 WindowToken 的子类,与 WindowToken 不同的是,AppWindowToken 只可以用于 Activity 中的 Window 的 WindowToken。

    1.2.4 DisplayContent

    DisplayContent 是 Android 4.2 中支持 WiFi Display 多屏幕显示提出的一个概念,一个 DisplayContent 对象就代表着一块屏幕信息,一个 DisplayContent 对象用一个整型变量作为其 ID,系统默认屏幕所对应的 DisplayContent 对象 ID 是 Display.DEFAULT_DISPLAY。

    属于同一个 DisplayContent 对象的 Window 对象会被绘制到同一块屏幕上,在添加窗口时可以指定对应的 DisplayContent 的 id,从而指定被添加到哪个 DisplayContent 上面。

    DisplayContent 对象是由 DisplayManagerService 统一管理的,在此只做概念性的介绍,详细的关于 DisplayContent 和 DisplayManagerService 知识请查阅相关文档和资料

    二. WMS 与 WindowManager 的通信

    在上篇文章 初步理解 Window 体系 中,我们最后分析到了 ViewRootImpl,ViewRootImpl 是连接 WindowManager 和 WMS 的桥梁,自然他们之间的通信也是通过 ViewRootImpl 完成的。

    2.1 ViewRootImpl 的成员变量

    在 ViewRootImpl 中有两个个非常重要的成员变量:mWindowSessionmWindow,这两个变量都是用于 ViewRootImpl 和 WMS 通信使用的

    public final class ViewRootImpl implements ViewParent,
            View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
        ......
        final IWindowSession mWindowSession;
        final W mWindow;
        ......
    
        public ViewRootImpl(Context context, Display display) {
            mContext = context;
            // 代码 1,通过 WindowManagerGlobal.getWindowSession() 方法得到一个 IWindowSession 对象
            mWindowSession = WindowManagerGlobal.getWindowSession();   
            ......
            // 代码 2,通过 W 构造方法直接创建一个新的 W 对象
            mWindow = new W(this);                                     
            ......
        }
    
        ......
    }
    
    2.1.1 IWindowSession

    IWindowSession 是一个 AIDL 接口,其服务端进程是 WMS,客户端进程是应用进程,IWindowSession 的创建是在 WindowManagerGlobal 中,如下所示:

        public final class WindowManagerGlobal {
    
            private static IWindowManager sWindowManagerService;
            private static IWindowSession sWindowSession;
            ......
            public static IWindowManager getWindowManagerService() {
                synchronized (WindowManagerGlobal.class) {
                    if (sWindowManagerService == null) {
                        sWindowManagerService = IWindowManager.Stub.asInterface(
                                ServiceManager.getService("window"));
                        try {
                            if (sWindowManagerService != null) {
                                ValueAnimator.setDurationScale(
                                        sWindowManagerService.getCurrentAnimatorScale());
                            }
                        } catch (RemoteException e) {
                            throw e.rethrowFromSystemServer();
                        }
                    }
                    return sWindowManagerService;
                }
            }
    
            public static IWindowSession getWindowSession() {
                synchronized (WindowManagerGlobal.class) {
                    if (sWindowSession == null) {
                        try {
                            InputMethodManager imm = InputMethodManager.getInstance();
                            IWindowManager windowManager = getWindowManagerService();
                            sWindowSession = windowManager.openSession(
                                    new IWindowSessionCallback.Stub() {
                                        @Override
                                        public void onAnimatorScaleChanged(float scale) {
                                            ValueAnimator.setDurationScale(scale);
                                        }
                                    },
                                    imm.getClient(), imm.getInputContext());
                        } catch (RemoteException e) {
                            throw e.rethrowFromSystemServer();
                        }
                    }
                    return sWindowSession;
                }
            }
            ......
        }
    
    • getWindowSession() 方法中我们可以看出,IWindowSession 对象的创建依赖于 IWindowManager 对象
    • IWindowManager 也是一个 AIDL 接口,通过 getWindowManagerService() 方法得到其对象,在 getWindowManagerService() 方法中,可以看到是典型的 Android 中 Binder 通信得到服务端在客户端进程中的代理对象的方式,远程端的对象即是 WMS,WMS 实现了 IWindowManager 接口
      public class WindowManagerService extends IWindowManager.Stub
          implements Watchdog.Monitor,     WindowManagerPolicy.WindowManagerFuncs {
          ......
      }
      
    • getWindowSession() 方法中,我们可以看到是调用了 IWindowManager 的 openSession 方法,其实际的实现是在 WMS 中,WMS 中的 openSession 方法如下所示
      public class WindowManagerService extends IWindowManager.Stub
          implements Watchdog.Monitor,     WindowManagerPolicy.WindowManagerFuncs {
      
          @Override
          public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client,
              IInputContext inputContext) {
              if (client == null) throw new IllegalArgumentException("null client");
              if (inputContext == null) throw new IllegalArgumentException("null inputContext");
              Session session = new Session(this, callback, client, inputContext);
              return session;
          }
      }
      

    可以看到,其实 ViewRootImpl 中的 IWindowSession 对象实际对应着 WMS 中的 Session 对象。

    WindowManagerGlobal 和 WMS 实现的是单方向的通信,都是通过如下图所示的 Binder 方式进行进程间通信的


    WindowManagerGlobal.png
    2.1.2 W

    W 类是 ViewRootImpl 的一个内部类,实现了 IWindow 接口,IWindow 也是一个 AIDL 接口,可以猜想到,IWindow 接口是供 WMS 使用的,WSM 通过调用 IWindow 一些方法,通过 Binder 通信的方式,最后执行到了 W 中对应的方法中

    public final class ViewRootImpl implements ViewParent,
            View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
    
    
        static class W extends IWindow.Stub {
            private final WeakReference<ViewRootImpl> mViewAncestor;
            private final IWindowSession mWindowSession;
    
            W(ViewRootImpl viewAncestor) {
                mViewAncestor = new WeakReference<ViewRootImpl>(viewAncestor);
                mWindowSession = viewAncestor.mWindowSession;
            }
    
            @Override
            public void resized(Rect frame, Rect overscanInsets, Rect contentInsets,
                    Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw,
                    MergedConfiguration mergedConfiguration, Rect backDropFrame, boolean forceLayout,
                    boolean alwaysConsumeNavBar, int displayId) {
                final ViewRootImpl viewAncestor = mViewAncestor.get();
                if (viewAncestor != null) {
                    viewAncestor.dispatchResized(frame, overscanInsets, contentInsets,
                            visibleInsets, stableInsets, outsets, reportDraw, mergedConfiguration,
                            backDropFrame, forceLayout, alwaysConsumeNavBar, displayId);
                }
            }
    
            @Override
            public void moved(int newX, int newY) {
                final ViewRootImpl viewAncestor = mViewAncestor.get();
                if (viewAncestor != null) {
                    viewAncestor.dispatchMoved(newX, newY);
                }
            }
    
            @Override
            public void dispatchAppVisibility(boolean visible) {
                final ViewRootImpl viewAncestor = mViewAncestor.get();
                if (viewAncestor != null) {
                    viewAncestor.dispatchAppVisibility(visible);
                }
            }
    
            @Override
            public void dispatchGetNewSurface() {
                final ViewRootImpl viewAncestor = mViewAncestor.get();
                if (viewAncestor != null) {
                    viewAncestor.dispatchGetNewSurface();
                }
            }
    
            @Override
            public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) {
                final ViewRootImpl viewAncestor = mViewAncestor.get();
                if (viewAncestor != null) {
                    viewAncestor.windowFocusChanged(hasFocus, inTouchMode);
                }
            }
    
            private static int checkCallingPermission(String permission) {
                try {
                    return ActivityManager.getService().checkPermission(
                            permission, Binder.getCallingPid(), Binder.getCallingUid());
                } catch (RemoteException e) {
                    return PackageManager.PERMISSION_DENIED;
                }
            }
            
            ......
    
        }
    }
    

    比如在 ViewRootImpl#setView 方法中,有如下代码,在代码 1 处通过 mWindowSession 调用 addToDisplay 方法时,会将 mWindow 传入,最后传给 WMS,这样 WMS 便得到了一个 W 对象的实例对象。

    public final class ViewRootImpl implements ViewParent,
            View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
    
        public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
            synchronized (this) {
                if (mView == null) {
    
                    ......
    
                    int res; /* = WindowManagerImpl.ADD_OKAY; */
    
                    // Schedule the first layout -before- adding to the window
                    // manager, to make sure we do the relayout before receiving
                    // any other events from the system.
                    requestLayout();
                    if ((mWindowAttributes.inputFeatures
                            & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                        mInputChannel = new InputChannel();
                    }
                    mForceDecorViewVisibility = (mWindowAttributes.privateFlags
                            & PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY) != 0;
                    try {
                        mOrigWindowType = mWindowAttributes.type;
                        mAttachInfo.mRecomputeGlobalAttributes = true;
                        collectViewAttributes();
                        // 代码 1
                        res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                                getHostVisibility(), mDisplay.getDisplayId(),
                                mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                                mAttachInfo.mOutsets, mInputChannel);
                    } catch (RemoteException e) {
                        mAdded = false;
                        mView = null;
                        mAttachInfo.mRootView = null;
                        mInputChannel = null;
                        mFallbackEventHandler.setView(null);
                        unscheduleTraversals();
                        setAccessibilityFocus(null, null);
                        throw new RuntimeException("Adding window failed", e);
                    } finally {
                        if (restore) {
                            attrs.restore();
                        }
                    }
    
                    ......
    
                }
            }
        }
    }
    

    从上面代码可以看到,在 ViewRootImpl 中不仅实现了从 ViewRootImpl 向 WMS 的通信,也实现了从 WMS 向 ViewRootImpl 的通信,如下图所示


    ViewRootImpl.png

    相关文章

      网友评论

        本文标题:深入理解 WindowManagerService

        本文链接:https://www.haomeiwen.com/subject/iqvxrqtx.html