Android 應用程序建立與WMS服務之間的通信過程

轉載地址:https://blog.csdn.net/yangwen123/article/details/18733631

我們知道WindowManagerService服務運行在SystemServer進程中,應用程序啓動Activity時,需要請求WMS爲啓動的Activity創建對應的窗口,同時WMS也負責修改窗口屬性,因此這裏就涉及到應用程序進程與WMS服務之間的跨進程交互過程。在前面我們介紹了Android中的Binder通信機制,應用程序進程正是使用Binder通信方式和SystemServer進程交互的。


在應用程序進程中啓動的每一個Activity都擁有一個ViewRootImpl對象:

frameworks\base\core\java\android\view\ViewRootImpl.java

  1. public ViewRootImpl(Context context, Display display) {
  2. //在WMS服務中創建Session Binder對象,同時返回Binder代理對象給當前應用程序進程,並保存在ViewRootImpl的成員變量mWindowSession中
  3. mWindowSession = WindowManagerGlobal.getWindowSession();
  4. ...
  5. //爲每一個ViewRootImpl對象創建W Binder對象,用於WMS服務訪問對應的Activity
  6. mWindow = new W(this);
  7. ...
  8. mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this);
  9. ...
  10. mChoreographer = Choreographer.getInstance();
  11. ...
  12. loadSystemProperties();
  13. }


1.建立應用程序到WMS服務之間的連接


從上面圖中可以看到,要使應用程序進程可以請求WMS服務,必須在WMS服務這邊創建一個類型爲Session的Binder本地對象,同時應用程序這邊獲取Session的代理對象,通過該代理對象,應用程序進程就可以請求WMS服務了。
frameworks\base\core\java\android\view\WindowManagerGlobal.java
  1. public static IWindowSession getWindowSession() {
  2. synchronized (WindowManagerGlobal.class) {
  3. if (sWindowSession == null) {
  4. try {
  5. InputMethodManager imm = InputMethodManager.getInstance();
  6. //得到WindowManagerService服務的代理對象
  7. IWindowManager windowManager = getWindowManagerService();
  8. //通過WindowManagerService服務代理對象請求在WMS中創建Session本地對象
  9. sWindowSession = windowManager.openSession(imm.getClient(), imm.getInputContext());
  10. float animatorScale = windowManager.getAnimationScale(2);
  11. ValueAnimator.setDurationScale(animatorScale);
  12. } catch (RemoteException e) {
  13. Log.e(TAG, "Failed to open window session", e);
  14. }
  15. }
  16. return sWindowSession;
  17. }
  18. }
函數首先通過getWindowManagerService來獲取WMS服務的代理對象,由於WMS服務是一個有名Binder對象,即已經註冊到了ServiceManager進程中,因此可以通過查詢服務的方式來獲取WMS的代理對象。
  1. public static IWindowManager getWindowManagerService() {
  2. synchronized (WindowManagerGlobal.class) {
  3. if (sWindowManagerService == null) {
  4. sWindowManagerService = IWindowManager.Stub.asInterface(
  5. ServiceManager.getService("window"));
  6. }
  7. return sWindowManagerService;
  8. }
  9. }
得到了WMS服務的代理對象後,就可以通過該代理對象請求WMS服務創建一個Session Binder本地對象,該對象用於應用程序進程訪問WMS服務。關於應用程序進程使用WMS代理對象如何請求WMS創建Session的過程這裏不在介紹,這是Binder通信機制中函數遠程調用流程。上面通過調用WMS代理對象的openSession函數後,WMS服務的openSession函數實現如下:
frameworks\base\services\java\com\android\server\wm\WindowManagerService.java
  1. public IWindowSession openSession(IInputMethodClient client,
  2. IInputContext inputContext) {
  3. if (client == null) throw new IllegalArgumentException("null client");
  4. if (inputContext == null) throw new IllegalArgumentException("null inputContext");
  5. Session session = new Session(this, client, inputContext);
  6. return session;
  7. }
這裏直接構造一個Session對象,並將該對象返回給應用程序進程,這樣在應用程序這邊就可以得到Session的代理對象IWindowSession.Proxy,並保存在ViewRootImpl對象的成員變量mWindowSession中。

2.建立WMS服務到應用程序進程之間的連接


前面介紹了應用程序進程到WMS服務之間的連接過程,雖然應用程序進程中的ViewRootImpl對象已經獲取WMS服務中的Session的代理對象,也就是說應用程序進程可以請求WMS服務了,但是WMS服務還是不能請求應用程序進程,怎麼在應用程序進程和WMS服務之間建立雙向連接呢?在前面ViewRootImpl的構造函數中,創建了一個類型爲W的對象,W實現了IWindow接口,WMS服務正是通過W的代理對象來請求應用程序進程的。那WMS服務是如何獲取W的代理對象的呢?
frameworks\base\core\java\android\view\ViewRootImpl.java
  1. public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
  2. synchronized (this) {
  3. if (mView == null) {
  4. mView = view;
  5. ...
  6. //mWindow爲W本地對象
  7. res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
  8. getHostVisibility(), mDisplay.getDisplayId(),
  9. mAttachInfo.mContentInsets, mInputChannel);
  10. ...
  11. if (mInputChannel != null) {
  12. if (mInputQueueCallback != null) {
  13. mInputQueue = new InputQueue();
  14. mInputQueueCallback.onInputQueueCreated(mInputQueue);
  15. }
  16. mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
  17. Looper.myLooper());
  18. }
  19. ...
  20. }
  21. }
  22. }
在上一節中已經介紹了,ViewRootImpl對象的成員變量mWindowSession保存了Session的代理對象,向addToDisplay函數傳遞的第一個參數爲成員變量mWindow,而mWindow中保存了W類型的Binder本地對象,因此這裏通過函數addToDisplay就可以將W的代理對象傳遞給WMS服務。
frameworks\base\services\java\com\android\server\wm\Session.java
  1. public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
  2. int viewVisibility, int displayId, Rect outContentInsets,
  3. InputChannel outInputChannel) {
  4. return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
  5. outContentInsets, outInputChannel);
  6. }
Session接收某一個應用程序進程的函數調用請求,並將請求轉交給WMS服務來完成。Session和WMS服務之間的關係如下:
frameworks\base\services\java\com\android\server\wm\WindowManagerService.java
  1. public int addWindow(Session session, IWindow client, int seq,
  2. WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
  3. Rect outContentInsets, InputChannel outInputChannel) {
  4. ...
  5. WindowToken token = mTokenMap.get(attrs.token);
  6. if (token == null) {
  7. ...
  8. token = new WindowToken(this, attrs.token, -1, false);
  9. addToken = true;
  10. //應用程序窗口
  11. } else if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {
  12. AppWindowToken atoken = token.appWindowToken;
  13. ...
  14. //輸入法窗口
  15. } else if (type == TYPE_INPUT_METHOD) {
  16. ...
  17. //牆紙窗口
  18. } else if (type == TYPE_WALLPAPER) {
  19. ...
  20. } else if (type == TYPE_DREAM) {
  21. ...
  22. }
  23. //在WMS服務中爲窗口創建WindowState對象
  24. win = new WindowState(this, session, client, token,
  25. attachedWindow, appOp[0], seq, attrs, viewVisibility, displayContent);
  26. ...
  27. win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs));
  28. res = mPolicy.prepareAddWindowLw(win, attrs);
  29. ...
  30. if (outInputChannel != null && (attrs.inputFeatures
  31. & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
  32. String name = win.makeInputChannelName();
  33. InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
  34. win.setInputChannel(inputChannels[0]);
  35. inputChannels[1].transferTo(outInputChannel);
  36. mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle);
  37. }
  38. ...
  39. if (addToken) {
  40. //mTokenMap哈希表保存了所有的WindowToken對象
  41. mTokenMap.put(attrs.token, token);
  42. }
  43. win.attach();
  44. //mWindowMap哈希表以鍵值對的方式保存了所有的WindowState對象及W代理對象:<IWindow.Proxy,WindowState>
  45. mWindowMap.put(client.asBinder(), win);
  46. if (win.mAppOp != AppOpsManager.OP_NONE) {
  47. if (mAppOps.startOpNoThrow(win.mAppOp, win.getOwningUid(), win.getOwningPackage())
  48. != AppOpsManager.MODE_ALLOWED) {
  49. win.setAppOpVisibilityLw(false);
  50. }
  51. }
  52. ...
  53. }
  54. ...
  55. }
該函數首先爲應用程序進程新增的窗口在WMS服務中創建對應的WindowState對象,並且將WMS,接收應用程序進程的Session,應用程序進程中的W代理對象保存到WindowState中。
  1. WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
  2. WindowState attachedWindow, int appOp, int seq, WindowManager.LayoutParams a,
  3. int viewVisibility, final DisplayContent displayContent) {
  4. mService = service;//標識WMS服務
  5. mSession = s;//標識接收應用程序進程的Session本地對象
  6. mClient = c;//標識應用程序進程中窗口的W代理對象
  7. mAppOp = appOp;
  8. mToken = token;//標識應用程序進程中的窗口布局參數的Token
  9. mOwnerUid = s.mUid;
  10. mWindowId = new IWindowId.Stub() {
  11. @Override
  12. public void registerFocusObserver(IWindowFocusObserver observer) {
  13. WindowState.this.registerFocusObserver(observer);
  14. }
  15. @Override
  16. public void unregisterFocusObserver(IWindowFocusObserver observer) {
  17. WindowState.this.unregisterFocusObserver(observer);
  18. }
  19. @Override
  20. public boolean isFocused() {
  21. return WindowState.this.isFocused();
  22. }
  23. };
  24. ...
  25. }
在構造WindowState對象過程中,又創建了一個用於標識指定窗口的IWindowId本地對象,因此應用程序進程必定會獲取該對象的代理對象。關於IWindowId代理對象的獲取過程在接下來分析。在WMS服務中爲應用程序進程中的指定窗口創建了對應的WindowState對象後,接着調用該對象的attach()函數:
frameworks\base\services\java\com\android\server\wm\WindowState.java
  1. void attach() {
  2. if (WindowManagerService.localLOGV) Slog.v(
  3. TAG, "Attaching " + this + " token=" + mToken
  4. + ", list=" + mToken.windows);
  5. mSession.windowAddedLocked();
  6. }
在構造WindowState對象時,將用於接收應用程序進程請求的本地Session對象保存在WindowState的成員變量mSession中,這裏調用Session的windowAddedLocked()函數來創建請求SurfaceFlinger的SurfaceSession對象,同時將接收應用程序進程請求的Session保存到WMS服務的mSessions數組中。
frameworks\base\services\java\com\android\server\wm\Session.java
  1. void windowAddedLocked() {
  2. if (mSurfaceSession == null) {
  3. mSurfaceSession = new SurfaceSession();
  4. mService.mSessions.add(this);
  5. }
  6. mNumWindow++;
  7. }

3. IWindowId代理對象獲取過程


在前面構造WindowState對象過程中,創建了一個IWindowId本地對象,並保存在WindowState的成員變量mWindowId中,因此,應用程序進程也會獲取用於標識每一個窗口對象的IWindowId的代理對象。
frameworks\base\core\java\android\view\View.java
  1. public WindowId getWindowId() {
  2. if (mAttachInfo == null) {
  3. return null;
  4. }
  5. if (mAttachInfo.mWindowId == null) {
  6. try {
  7. /**獲取WMS服務端的IWindowId代理對象,mAttachInfo在前面創建ViewRootImpl對象時創建
  8. * mWindow = new W(this);
  9. * mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this);
  10. * 在AttachInfo的構造函數中:
  11. * mWindow = window;
  12. * mWindowToken = window.asBinder();
  13. * 因此AttachInfo的成員變量mWindow和mWindowToken引用了同一對象,該對象就是類型爲W的本地binder對象
  14. * 下面通過W本地binder對象,來獲取AMS服務端的IWindowId的代理對象
  15. */
  16. mAttachInfo.mIWindowId = mAttachInfo.mSession.getWindowId(mAttachInfo.mWindowToken);
  17. WindowId類的定義主要是爲了方便在進程間傳輸IWindowId Binder對象
  18. mAttachInfo.mWindowId = new WindowId(mAttachInfo.mIWindowId);
  19. } catch (RemoteException e) {
  20. }
  21. }
  22. return mAttachInfo.mWindowId;
  23. }
應用程序進程還是通過Session的代理對象來獲取IWindowId的代理對象,參數window在應用程序進程端爲W本地binder對象,經過Binder傳輸到達WMS服務進程後,就轉換爲W的binder代理對象了。
frameworks\base\services\java\com\android\server\wm\Session.java
  1. public IWindowId getWindowId(IBinder window) {
  2. return mService.getWindowId(window);
  3. }
在WMS服務中的Session本地對象又將IWindowId對象的查找過程交給WMS服務。
frameworks\base\services\java\com\android\server\wm\WindowManagerService.java
  1. public IWindowId getWindowId(IBinder token) {
  2. synchronized (mWindowMap) {
  3. //通過W的binder代理對象從mWindowMap哈希表中查找對應的WindowState對象。
  4. WindowState window = mWindowMap.get(token);
  5. return window != null ? window.mWindowId : null;
  6. }
  7. }
根據W的binder代理對象token在WMS中查找窗口對應的WindowState對象,再將該窗口在WindowState對象中創建的IWindowId Binder本地對象返回,這樣,客戶端進程就可以得到該Binder的代理對象。

查找過程如下:
通過以上介紹,可以知道應用程序進程和WMS服務之間的交互主要是通過IWindowSession,Iwindow,IWindowId三個接口來完成,IWindowSession實現應用程序進程到WMS服務間的通信,而Iwindow和IWindowId則實現WMS服務到應用程序進程間的通信。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章