Android O WMS(3) -- addwindow

Android P WMS(1) -- wms簡介

Android P WMS(2) -- wms初始化

Android O WMS(3) -- addwindow

Android P WMS(4) -- removewindow

Android P WMS(5) -- relayoutWindow

Android P WMS(6) -- windowanimator

Android P WMS(7) --wms 問題種類和debug技巧

Android P WMS(8) --View SYstem 簡介

Android P WMS(9) --Surface

 

1.APP啓動addWindow過程

我們在WMS addWindow添加打印堆棧log

//在WMS添加如下堆棧
@WindowManagerService.java
public int addWindow(Session session, IWindow client, int seq,
        LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,
        Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
        DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel) {
             
        new Exception("William WindowManagerService stack").printStackTrace(); //add log  

然後在launcher點擊啓動settings,log如下。第一次在啓動splash screen時執行addWindow,來至於SplashScreenStartingData.createStartingSurface,第二次是啓動settings主界面  com.android.settings/com.android.settings.Settings。這一部分可以參考:

Android懸浮窗TYPE_TOAST小結: 源碼分析

Android解析WindowManager(三)Window的添加過程

        
//發現啓動settings時,會先啓動Splash Screen(啓動畫面也叫歡迎頁)
1077  1148 W System.err: java.lang.Exception: William WindowManagerService stack
1077  1148 W System.err: 	at com.android.server.wm.WindowManagerService.addWindow(WindowManagerService.java:1157)
1077  1148 W System.err: 	at com.android.server.wm.Session.addToDisplay(Session.java:205)
1077  1148 W System.err: 	at android.view.ViewRootImpl.setView(ViewRootImpl.java:771)
1077  1148 W System.err: 	at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:356)
1077  1148 W System.err: 	at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:93)
1077  1148 W System.err: 	at com.android.server.policy.PhoneWindowManager.addSplashScreen(PhoneWindowManager.java:3214)
1077  1148 W System.err: 	at com.android.server.wm.SplashScreenStartingData.createStartingSurface(SplashScreenStartingData.java:56)
1077  1148 W System.err: 	at com.android.server.wm.AppWindowContainerController$1.run(AppWindowContainerController.java:170)
1077  1148 W System.err: 	at android.os.Handler.handleCallback(Handler.java:873)
1077  1148 W System.err: 	at android.os.Handler.dispatchMessage(Handler.java:99)
1077  1148 W System.err: 	at android.os.Looper.loop(Looper.java:193)
1077  1148 W System.err: 	at android.os.HandlerThread.run(HandlerThread.java:65)
1077  1148 W System.err: 	at com.android.server.ServiceThread.run(ServiceThread.java:44)
1077  1148 V WindowManager: Window Window{e56651a u0 Splash Screen com.android.settings} client=android.view.ViewRootImpl$W@f2667c5 token=AppWindowToken{4be29e3 token=Token{afc8e12 ActivityRecord{f88399d u0 com.android.settings/.Settings t7}}} (Token{afc8e12 ActivityRecord{f88399d u0 com.android.settings/.Settings t7}}) params={(0,0)(fillxfill) ty=APPLICATION_STARTING wanim=0x10302f8
1077  1148 V WindowManager: Attaching Window{e56651a u0 Splash Screen com.android.settings} token=AppWindowToken{4be29e3 token=Token{afc8e12 ActivityRecord{f88399d u0 com.android.settings/.Settings t7}}}
1077  1148 V WindowManager: addWindow: New client android.view.ViewRootImpl$W@f2667c5: window=Window{e56651a u0 Splash Screen com.android.settings} Callers=com.android.server.wm.Session.addToDisplay:205 android.view.ViewRootImpl.setView:771 android.view.WindowManagerGlobal.addView:356 android.view.WindowManagerImpl.addView:93 com.android.server.policy.PhoneWindowManager.addSplashScreen:3214 


//然後才add settings window(com.android.settings/com.android.settings.Settings)
1077  1236 W System.err: java.lang.Exception: William WindowManagerService stack
1077  1236 W System.err: 	at com.android.server.wm.WindowManagerService.addWindow(WindowManagerService.java:1157)
1077  1236 W System.err: 	at com.android.server.wm.Session.addToDisplay(Session.java:205)
1077  1236 W System.err: 	at android.view.IWindowSession$Stub.onTransact(IWindowSession.java:129)
1077  1236 W System.err: 	at com.android.server.wm.Session.onTransact(Session.java:164)
1077  1236 V WindowManager: Window Window{c70e488 u0 com.android.settings/com.android.settings.Settings} client=android.os.BinderProxy@d12a82b token=AppWindowToken{4be29e3 token=Token{afc8e12 ActivityRecord{f88399d u0 com.android.settings/.Settings t7}}} (Token{afc8e12 ActivityRecord{f88399d u0 com.android.settings/.Settings t7}}) params={(0,0)(fillxfill) sim={forwardNavigation} ty=BASE_APPLICATION wanim=0x10302f8
1077  1236 V WindowManager: Attaching Window{c70e488 u0 com.android.settings/com.android.settings.Settings} token=AppWindowToken{4be29e3 token=Token{afc8e12 ActivityRecord{f88399d u0 com.android.settings/.Settings t7}}}
1077  1236 V WindowManager: First window added to Session{d84ba9c 3168:1000}, creating SurfaceSession
1077  1236 V WindowManager: addWindow: New client android.os.BinderProxy@d12a82b: window=Window{c70e488 u0 com.android.settings/com.android.settings.Settings} Callers=com.android.server.wm.Session.addToDisplay:205 android.view.IWindowSession$Stub.onTransact:129 com.android.server.wm.Session.onTransact:164 android.os.Binder.execTransact:731 <bottom of call stack> 

Android解析WindowManager(三)Window的添加過程

1.0 概述

WindowManager對Window進行管理,說到管理那就離不開對Window的添加、更新和刪除的操作,在這裏我們把它們統稱爲Window的操作。對於Window的操作,最終都是交由WMS來進行處理。窗口的操作分爲兩大部分,一部分是WindowManager處理部分,另一部分是WMS處理部分。我們知道Window分爲三大類,分別是:Application Window(應用程序窗口)、Sub Windwow(子窗口)和System Window(系統窗口),對於不同類型的窗口添加過程會有所不同,但是對於WMS處理部分,添加的過程基本上是一樣的, WMS對於這三大類的窗口基本是“一視同仁”的。

 

1.1 Activity的添加過程

@frameworks/base/core/java/android/app/ActivityThread.java
 final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
       ...   
         r = performResumeActivity(token, clearHide, reason);//1           
       ...
          if (r.window == null && !a.mFinished && willBeVisible) {
                r.window = r.activity.getWindow();
                View decor = r.window.getDecorView();
                decor.setVisibility(View.INVISIBLE);
                ViewManager wm = a.getWindowManager();//2
                WindowManager.LayoutParams l = r.window.getAttributes();
                a.mDecor = decor;
                l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
                l.softInputMode |= forwardBit;
                if (r.mPreserveWindow) {
                    a.mWindowAdded = true;
                    r.mPreserveWindow = false;
                    ViewRootImpl impl = decor.getViewRootImpl();
                    if (impl != null) {
                        impl.notifyChildRebuilt();
                    }
                }
                if (a.mVisibleFromClient && !a.mWindowAdded) {
                    a.mWindowAdded = true;
                    wm.addView(decor, l);//3
                }
              
}

註釋1處的performResumeActivity方法最終會調用Activity的onResume方法。在註釋2處得到ViewManager類型的wm對象,
在註釋3處調用了wm的addView方法,而addView方法的實現則是在WindowManagerImpl中,此後的過程在上面的系統窗口的添加過程已經講過,
唯一需要注意的是addView的第一個參數是DecorView。

1.2 系統窗口的添加過程

三大類窗口的添加過程會有所不同,這裏以系統窗口StatusBar爲例,StatusBar是SystemUI的重要組成部分,具體就是指系統狀態欄,用於顯示時間、電量和信號等信息。我們來查看StatusBar的實現類PhoneStatusBar的addStatusBarWindow方法,這個方法負責爲StatusBar添加Window,如下所示。

@/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
private void addStatusBarWindow() {
    makeStatusBarView();
    mStatusBarWindowManager = Dependency.get(StatusBarWindowManager.class);
    mRemoteInputController = new RemoteInputController(mHeadsUpManager);
    mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight());
}

@/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
public void add(View statusBarView, int barHeight) {

    // Now that the status bar window encompasses the sliding panel and its
    // translucent backdrop, the entire thing is made TRANSLUCENT and is
    // hardware-accelerated.
    mLp = new WindowManager.LayoutParams(
            ViewGroup.LayoutParams.MATCH_PARENT,
            barHeight,
            WindowManager.LayoutParams.TYPE_STATUS_BAR,
            WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                    | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
                    | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
                    | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
                    | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
             PixelFormat.TRANSLUCENT);
     mLp.token = new Binder();
     mLp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
     mLp.gravity = Gravity.TOP;
     mLp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
     mLp.setTitle("StatusBar");
     mLp.packageName = mContext.getPackageName();
     mStatusBarView = statusBarView;
     mBarHeight = barHeight;
     mWindowManager.addView(mStatusBarView, mLp);   //mWindowManager
     mLpChanged = new WindowManager.LayoutParams();
     mLpChanged.copyFrom(mLp);
 }
 
//WindowManager繼承ViewManager,因此還是在ViewManager
@frameworks/base/core/java/android/view/WindowManager.java
/**
 * The interface that apps use to talk to the window manager.
 * </p><p>
@SystemService(Context.WINDOW_SERVICE)
public interface WindowManager extends ViewManager {
 

//包括addview update remove
@/frameworks/base/core/java/android/view/ViewManager.java
public interface ViewManager
    public void addView(View view, ViewGroup.LayoutParams params);
    public void updateViewLayout(View view, ViewGroup.LayoutParams params);
    public void removeView(View view);
}

 
//WindowManagerImpl具體實現addView
public final class WindowManagerImpl implements WindowManager {
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }

@frameworks/base/core/java/android/view/WindowManagerGlobal.java
public void addView(View view, ViewGroup.LayoutParams params,
          Display display, Window parentWindow) {
    ...//參數檢查
      final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
      if (parentWindow != null) {
          parentWindow.adjustLayoutParamsForSubWindow(wparams);//1
      } else {
      ...
      }

      ViewRootImpl root;
      View panelParentView = null;
       ...
          root = new ViewRootImpl(view.getContext(), display);//2
          view.setLayoutParams(wparams);
          mViews.add(view);
          mRoots.add(root);//3
          mParams.add(wparams);
      }
      try {
          root.setView(view, wparams, panelParentView);//4
      } catch (RuntimeException e) {
         ...
      }
  }
  
/**
 * We have one child
 */
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    synchronized (this) {
        try {
            mOrigWindowType = mWindowAttributes.type;
            mAttachInfo.mRecomputeGlobalAttributes = true;
            collectViewAttributes();
            res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                    getHostVisibility(), mDisplay.getDisplayId(),
                    mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                    mAttachInfo.mOutsets, mInputChannel);
        } 
}

@/frameworks/base/services/core/java/com/android/server/wm/Session.java
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
        int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
        Rect outOutsets, InputChannel outInputChannel) {
    return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,  //就調用到了wms::addWindow
            outContentInsets, outStableInsets, outOutsets, outInputChannel);
}

 

2.Window的添加過程(WMS部分)

frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java

public int addWindow(Session session, IWindow client, int seq,
            WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
            Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
            InputChannel outInputChannel) {

        int[] appOp = new int[1];
        int res = mPolicy.checkAddPermission(attrs, appOp);//1
        if (res != WindowManagerGlobal.ADD_OKAY) {
            return res;
        }
        ...
        synchronized(mWindowMap) {
            if (!mDisplayReady) {
                throw new IllegalStateException("Display has not been initialialized");
            }
            final DisplayContent displayContent = mRoot.getDisplayContentOrCreate(displayId);//2
            if (displayContent == null) {
                Slog.w(TAG_WM, "Attempted to add window to a display that does not exist: "
                        + displayId + ".  Aborting.");
                return WindowManagerGlobal.ADD_INVALID_DISPLAY;
            }
            ...
            if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {//3
                parentWindow = windowForClientLocked(null, attrs.token, false);//4
                if (parentWindow == null) {
                    Slog.w(TAG_WM, "Attempted to add window with token that is not a window: "
                          + attrs.token + ".  Aborting.");
                    return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
                }
                if (parentWindow.mAttrs.type >= FIRST_SUB_WINDOW
                        && parentWindow.mAttrs.type <= LAST_SUB_WINDOW) {
                    Slog.w(TAG_WM, "Attempted to add window with token that is a sub-window: "
                            + attrs.token + ".  Aborting.");
                    return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
                }
            }
           ...
}
...
}

WMS的addWindow返回的是addWindow的各種狀態,比如添加Window成功,無效的display等等,這些狀態被定義在WindowManagerGlobal中。 
註釋1處根據Window的屬性,調用WMP的checkAddPermission方法來檢查權限,具體的實現在PhoneWindowManager的checkAddPermission方法中,如果沒有權限則不會執行後續的代碼邏輯。註釋2處通過displayId來獲得窗口要添加到哪個DisplayContent上,如果沒有找到DisplayContent,則返回WindowManagerGlobal.ADD_INVALID_DISPLAY這一狀態,其中DisplayContent用來描述一塊屏幕。註釋3處,type代表一個窗口的類型,它的數值介於FIRST_SUB_WINDOW和LAST_SUB_WINDOW之間(1000~1999),這個數值定義在WindowManager中,說明這個窗口是一個子窗口,不瞭解窗口類型取值範圍的請閱讀Android解析WindowManager(二)Window的屬性這篇文章。註釋4處,attrs.token是IBinder類型的對象,windowForClientLocked方法內部會根據attrs.token作爲key值從mWindowMap中得到該子窗口的父窗口。接着對父窗口進行判斷,如果父窗口爲null或者type的取值範圍不正確則會返回錯誤的狀態。
 

 

   ...
            AppWindowToken atoken = null;
            final boolean hasParent = parentWindow != null;
            WindowToken token = displayContent.getWindowToken(
                    hasParent ? parentWindow.mAttrs.token : attrs.token);//1
            final int rootType = hasParent ? parentWindow.mAttrs.type : type;//2
            boolean addToastWindowRequiresToken = false;

            if (token == null) {
                if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {
                    Slog.w(TAG_WM, "Attempted to add application window with unknown token "
                          + attrs.token + ".  Aborting.");
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
                if (rootType == TYPE_INPUT_METHOD) {
                    Slog.w(TAG_WM, "Attempted to add input method window with unknown token "
                          + attrs.token + ".  Aborting.");
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
                if (rootType == TYPE_VOICE_INTERACTION) {
                    Slog.w(TAG_WM, "Attempted to add voice interaction window with unknown token "
                          + attrs.token + ".  Aborting.");
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
                if (rootType == TYPE_WALLPAPER) {
                    Slog.w(TAG_WM, "Attempted to add wallpaper window with unknown token "
                          + attrs.token + ".  Aborting.");
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
                ...
                if (type == TYPE_TOAST) {
                    // Apps targeting SDK above N MR1 cannot arbitrary add toast windows.
                    if (doesAddToastWindowRequireToken(attrs.packageName, callingUid,
                            parentWindow)) {
                        Slog.w(TAG_WM, "Attempted to add a toast window with unknown token "
                                + attrs.token + ".  Aborting.");
                        return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                    }
                }
                final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
                token = new WindowToken(this, binder, type, false, displayContent,
                        session.mCanAddInternalSystemWindow);//3
            } else if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {//4
                atoken = token.asAppWindowToken();//5
                if (atoken == null) {
                    Slog.w(TAG_WM, "Attempted to add window with non-application token "
                          + token + ".  Aborting.");
                    return WindowManagerGlobal.ADD_NOT_APP_TOKEN;
                } else if (atoken.removed) {
                    Slog.w(TAG_WM, "Attempted to add window with exiting application token "
                          + token + ".  Aborting.");
                    return WindowManagerGlobal.ADD_APP_EXITING;
                }
            } else if (rootType == TYPE_INPUT_METHOD) {
                if (token.windowType != TYPE_INPUT_METHOD) {
                    Slog.w(TAG_WM, "Attempted to add input method window with bad token "
                            + attrs.token + ".  Aborting.");
                      return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
            }
      ...      

 

addWindow方法總結
addWindow方法分了3個部分來進行講解,主要就是做了下面4件事: 
1. 對所要添加的窗口進行檢查,如果窗口不滿足一些條件,就不會再執行下面的代碼邏輯。 
2. WindowToken相關的處理,比如有的窗口類型需要提供WindowToken,沒有提供的話就不會執行下面的代碼邏輯,有的窗口類型則需要由WMS隱式創建WindowToken。 
3. WindowState的創建和相關處理,將WindowToken和WindowState相關聯。 
4. 創建和配置DisplayContent,完成窗口添加到系統前的準備工作。
 

 

參考鏈接:

Android懸浮窗TYPE_TOAST小結: 源碼分析

Android解析WindowManagerService(二)WMS的重要成員和Window的添加過程

Android解析WindowManager系列

http://androidxref.com/8.0.0_r4/

《深入理解Android:卷III》 

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