---
# System prepended metadata

title: WindowManagerService
tags: [Android Framework]

---

---
title: 'WindowManagerService'
disqus: kyleAlien

---

WindowManagerService
===

## OverView of Content

[TOC]

## WMS 概述

WMS 全名是 WindowManagerService，它是客戶端 APP 的管理者，處於 SystemServer 進程，WMS 主要負責管理幾件事情

1. **窗口管理**
    負責 Window 添加、更新、刪除，核心管理成員有 **`DisplayContent`、`WindowToken`、`WindowState`**

2. **窗口動畫**
    Window 切換時，可以添加動畫，讓 Window 切換時更加自然，動畫由 WMS 的動畫子系統負責，該系統是 **`WindowAnimator`**

3. **輸入系統的中轉**
    當使用者對視窗觸控 or 輸入時，都會觸發 InputManagerService 會對觸摸事件進行處理，將事件通過 WMS 傳遞到合適的窗口進行處理 (這時 WMS 就是 **中轉站**)

4. **Surface 管理**
    WMS 並不具備 Window 繪製的功能，**每個 Window 都有一個 Surface 來繪製**，而 WMS 負責 Surface 的分配

## WMS 啟動 - [SystemServer](https://android.googlesource.com/platform/frameworks/base/+/master/services/java/com/android/server/SystemServer.java)

* WMS 是就是 WindowManagerService，它是系統服務，說到系統服務自然就會想到 SystemServer，那就讓我們來看看 WMS 是如何被 SystemServer 喚起的

  1. SystemServer 透過 WMS#main 方法啟動

  2. 把 WMS 以 Context#WINDOW_SERVICE (**window**) 設定到 ServiceManager 中 

  3. 把啟動的 WMS 對象設定給 AMS 服務

     ```java=
     // SystemServer.java
     
         private void run() {
     
             ... 省略部分
     
             // Start services.
             try {
                 t.traceBegin("StartServices");
                 startBootstrapServices(t);
                 startCoreServices(t);
     
                 // @ WMS 是在 OtherServices 啟動
                 startOtherServices(t);
             } catch (Throwable ex) {
     
                 ... 省略 Log
     
                 throw ex;
             } /* 省略 finally */
     
         }
     
         private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
     
             ... 省略部分
     
             try {
                 t.traceBegin("StartWindowManagerService");
                 // WMS needs sensor service ready
                 mSystemServiceManager.startBootPhase(t, SystemService.PHASE_WAIT_FOR_SENSOR_SERVICE);
     
                 // 1. 啟動 WMS
                 wm = WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore,
                         new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager);
     
                 // 2. 對 ServiceManager 添加服務 "window"
                 ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false,
                         DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO);
     
                 ServiceManager.addService(Context.INPUT_SERVICE, inputManager,
                         /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL);
                 t.traceEnd();

                    // 3. 與 AMS 產生關係
                  t.traceBegin("SetWindowManagerService");
                  mActivityManagerService.setWindowManager(wm);
                  t.traceEnd();
    
                } /* 省略 catch */
    
            }
        ```

* 在 WMS 啟動後會透過 `onInitReady`、`displayReady`、`systemReady` 來完成 WMS 的後續動作 (這些方法之後會介紹)

  ```java=
  // SystemServer.java
  
      private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
      
          ... 省略部分
          
          try {
  
              // 初始化準備
              wm.onInitReady();
              
              ... 省略部分
                  
              try {
                  wm.displayReady();
              } catch (Throwable e) {
                  reportWtf("making display ready", e);
              }
                  
              wm.systemReady();
          } /* 省略 catch */
      }                    
  
  ```


### [WMS](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/wm/WindowManagerService.java) - main 函數

* 透過 DisplayThread 中的 Handler#runWithScissors 創建 WMS 對象 (也就是創建 WMS 的線程與 SystemServer 線程不同)

* Handler#**runWithScissors** 方法是透過鎖來達成同步任務，保證該 Runnable 會先被 DisplayThread 的 Looper 先執行，執行完後才回到 SystemServer 的 Thread

  > 透過 synchronized、wait 鎖住 SystemServer Thread
  >
  > ![](https://i.imgur.com/J055pGW.png)

  ```java=
  // WindowManagerService.java
  
   public static WindowManagerService main(final Context context, final InputManagerService im,
          final boolean showBootMsgs, final boolean onlyCore, WindowManagerPolicy policy,
          ActivityTaskManagerService atm) {

      // @ 查看 main 方法
      return main(context, im, showBootMsgs, onlyCore, policy, atm,
              new DisplayWindowSettingsProvider(), SurfaceControl.Transaction::new, Surface::new,
              SurfaceControl.Builder::new);
  }


    @VisibleForTesting
    public static WindowManagerService main(final Context context, final InputManagerService im,
            final boolean showBootMsgs, final boolean onlyCore, WindowManagerPolicy policy,
            ActivityTaskManagerService atm, ... 省略部分參數) {

        // 創建新 Thread
        DisplayThread.getHandler().runWithScissors(() ->
                sInstance = new WindowManagerService(context, im, showBootMsgs, onlyCore, policy,
                        atm, displayWindowSettingsProvider, transactionFactory, surfaceFactory,
                        surfaceControlFactory), 0);
        // 執行完 runWithScissors 中的 Runnable 任務後才會返回
        return sInstance;
    }
    ```

## [HandlerThread](https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/os/HandlerThread.java) - 創建 Looper

這裡特別介紹了 HandlerThread 這個類，^1^ 它繼承於 Thread 並且會在 run 方法中自動 ，**^2^ 建立該 Thread 對象專屬的 Looper 對象，但並 ++不主動創建 Handler 對象++ (在這創建的 Handler 並不安全)**

```java=
// HandlerThread.java

public class HandlerThread extends Thread {
    
    ... 省略部分方法
    
    @Override
    public void run() {
        mTid = Process.myTid();
        
        // 準備該 Thread 專用的 Looper & MessageQueue 對象
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            
            // 通知所有等待的 Thread， Looper 已經創建完成
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        
        // Looper 準備好
        onLooperPrepared();
        // 準備開始運行 Loop 不斷循環處理 Message 任務
        Looper.loop();
        
        mTid = -1;
    }
    
    public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }
        
        // If the thread has been started, wait until the looper has been created.
        synchronized (this) {    // 以當前對象作為鎖
            while (isAlive() && mLooper == null) {
                try {
                    wait();    // 直到 Looper 創建
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }
    
    
    // 在需要時才創建 (但線程不安全)
    public Handler getThreadHandler() {
        if (mHandler == null) {
            mHandler = new Handler(getLooper());
        }
        return mHandler;
    }
}
```

> ![](https://i.imgur.com/g1GxcvF.png)


### [DisplayThread](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/DisplayThread.java) - 創建 WMS 對象

:::success
WMS 就是在 DisplayThread 類中被啟動，**DisplayThread 可以說是 WMS 的 MainThread**
:::

* DisplayThread 可以創建安全 Handler，其主要功能是：處理 **低延遲顯示** 的相關操作，並只能由 WindowManager、DisplayManager、InputManager 執行快速操作

* 從上面可以看到 DisplayThread 繼承於 ServiceThread (而它繼承於 HandlerThread)，所以 **它會在執行 run 方法時自動創建 Looper 對象**，鑒於 HandlerThread 創建的 Handler 並不安全，所以 DisplayThread 這裡自己創建 Handler (使用類鎖)

  ```java=
  // DisplayThread.java
  
  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_SYSTEM_SERVER);
              sHandler = new Handler(sInstance.getLooper());
          }
      }
  
      public static DisplayThread get() {
          // 類鎖
          synchronized (DisplayThread.class) {
              // 創建 DisplayThread 對象
              ensureThreadLocked();
              return sInstance;
          }
      }
  
      // 透過 DisplayThread.class 類鎖來達成安全創建
      public static Handler getHandler() {
          // 類鎖
          synchronized (DisplayThread.class) {
              // 創建 DisplayThread 對象
              ensureThreadLocked();
              return sHandler;
          }
      }
  }
  ```

    :::success

    * 在取得 DisplayThread 類時，該類的 Thread 就已經啟動 (透過#ensureThreadLocked 方法啟動 Thread)，這時再對該類放置任務就會開始執行
    
    * **Binder Thread 傳遞的訊息可以透過 DisplayThread 傳遞給 WMS**
    :::

* 傳遞事件給 WMS 有兩種方式

    1. 直接呼叫 WMS function，即時傳送

        ```java=
        // WindowManagerService.java

            @Override
            public boolean isKeyguardLocked() {
                // mPolicy 類型 WindowManagerPolicy 
                return mPolicy.isKeyguardLocked();
            }
        ```
    2. 透過 DisplayThread 的 Looper 將事件丟入 WMS Thread (DisplayThread) 中

        ```java=
        // WindowManagerService.java

            final H mH = new H();

            final class H extends android.os.Handler {
                ...
            }

            @Override
            public void setAnimationScale(int which, float scale) {
                if (!checkCallingPermission(android.Manifest.permission.SET_ANIMATION_SCALE,
                        "setAnimationScale()")) {
                    throw new SecurityException("Requires SET_ANIMATION_SCALE permission");
                }

                scale = fixScale(scale);
                switch (which) {
                    case 0: mWindowAnimationScaleSetting = scale; break;
                    case 1: mTransitionAnimationScaleSetting = scale; break;
                    case 2: mAnimatorDurationScaleSetting = scale; break;
                }

                // Persist setting
                mH.sendEmptyMessage(H.PERSIST_ANIMATION_SCALE);
            }
        ```
        > ![](https://i.imgur.com/kSRGq94.png)


### [UiThread](https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/services/core/java/com/android/server/UiThread.java) 類

* UiThread 與 DisplayThread 差不多，但多添加了一個 `dispose` 方法，來停止 Looper，並結束該對象 (置為 null)，在 `initPolicy()`、`systemReady()` 會使用到

  ```java=
  // UiThread.java
  public final class UiThread extends ServiceThread {
      private static final long SLOW_DISPATCH_THRESHOLD_MS = 100;
      private static final long SLOW_DELIVERY_THRESHOLD_MS = 200;
      private static UiThread sInstance;
      private static Handler sHandler;
      private UiThread() {
          super("android.ui", Process.THREAD_PRIORITY_FOREGROUND, false /*allowIo*/);
      }
      @Override
      public void run() {
          // Make sure UiThread is in the fg stune boost group
          Process.setThreadGroup(Process.myTid(), Process.THREAD_GROUP_TOP_APP);
          super.run();
      }
      private static void ensureThreadLocked() {
          if (sInstance == null) {
              sInstance = new UiThread();
              sInstance.start();
              final Looper looper = sInstance.getLooper();
              looper.setTraceTag(Trace.TRACE_TAG_SYSTEM_SERVER);
              looper.setSlowLogThresholdMs(
                      SLOW_DISPATCH_THRESHOLD_MS, SLOW_DELIVERY_THRESHOLD_MS);
              sHandler = new Handler(sInstance.getLooper());
          }
      }
      public static UiThread get() {
          synchronized (UiThread.class) {
              ensureThreadLocked();
              return sInstance;
          }
      }
      public static Handler getHandler() {
          synchronized (UiThread.class) {
              ensureThreadLocked();
              return sHandler;
          }
      }
      /**
       * Disposes current ui thread if it's initialized. Should only be used in tests to set up a
       * new environment.
       */
      @VisibleForTesting
      public static void dispose() {
          synchronized (UiThread.class) {
              if (sInstance == null) {
                  return;
              }
              // 執行完 runWithScissors 才會往下執行
              getHandler().runWithScissors(sInstance::quit, 0 /* timeout */);
              sInstance = null;
          }
      }
  }
  ```

### [AnimationThread](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/AnimationThread.java) 類

* AnimationThread 與 DisplayThread 差不多，但多添加了一個 dispose 方法，來停止 Looper，並結束該對象 (置為 null)

  ```java=
  // AnimationThread.java
  
  public final class AnimationThread extends ServiceThread {
      private static AnimationThread sInstance;
      private static Handler sHandler;
      
      private AnimationThread() {
          super("android.anim", THREAD_PRIORITY_DISPLAY, false /*allowIo*/);
      }
      
      private static void ensureThreadLocked() {
          if (sInstance == null) {
              sInstance = new AnimationThread();
              sInstance.start();
              sInstance.getLooper().setTraceTag(Trace.TRACE_TAG_WINDOW_MANAGER);
              sHandler = new Handler(sInstance.getLooper());
          }
      }
      public static AnimationThread get() {
          synchronized (AnimationThread.class) {
              ensureThreadLocked();
              return sInstance;
          }
      }
      public static Handler getHandler() {
          synchronized (AnimationThread.class) {
              ensureThreadLocked();
              return sHandler;
          }
      }
      
      // 使用到 HandlerThread#quit 方法，停止 Looper
      @VisibleForTesting
      public static void dispose() {
          synchronized (AnimationThread.class) {
              if (sInstance == null) {
                  return;
              }
              getHandler().runWithScissors(() -> sInstance.quit(), 0 /* timeout */);
              sInstance = null;
          }
      }
  }
  ```
## SystemServer - WMS 啟動後續

複習一下：在 SystemService 啟動 WMS 後會隨之調用到 WMS#**`onInitReady` 方法** -> `displayReady` 方法 -> `systemReady` 方法

```java=
// SystemServer.java

private void startOtherServices(@NonNull TimingsTraceAndSlog t) {

    ... 省略部分

    try {

        // 初始化準備
        wm.onInitReady();

        ... 省略部分

        try {
            wm.displayReady();
        } catch (Throwable e) {
            reportWtf("making display ready", e);
        }

        wm.systemReady();
    } /* 省略 catch */
}                    
```

### [WMS](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/wm/WindowManagerService.java#1209) 創建

* 前面有提到 WindowManagerService#`main` 方法會透過 DisplayThread (Thread) 創建 WMS 的實例，在這裡就會創建 **H 類**，而它的 **Looper 就是當前 Thread 也就是 DisplayThread**

  ```java=
  // WindowManagerService.java
  
      // 當沒有指定 Looper 就會依現在 Thread 的 Looper 
      final H mH = new H();        // 也就是 DisplayThread 的 Looper
  
      WindowManagerPolicy mPolicy;
      final WindowAnimator mAnimator;
  
      RootWindowContainer mRoot;
  
      final class H extends android.os.Handler {
          ...
      }
  
      private WindowManagerService(Context context, InputManagerService inputManager,
              boolean showBootMsgs, boolean onlyCore, WindowManagerPolicy policy,
              ActivityTaskManagerService atm, DisplayWindowSettingsProvider
              displayWindowSettingsProvider, Supplier<SurfaceControl.Transaction> transactionFactory,
              Supplier<Surface> surfaceFactory,
              Function<SurfaceSession, SurfaceControl.Builder> surfaceControlFactory) {
  
          ... 省略部分

          // WMS 持有 InputManagerService 引用
          mInputManager = inputManager; 
          
          ...
          
          // 取得 DisplayManagerService 代理
          mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
  
        
          mPolicy = policy;        // ++ SystemServer 傳進來的實例是 PhoneWindowManager 類 ++
        
          // 取得 WMS 代理
          mActivityManager = ActivityManager.getService();
          mActivityTaskManager = ActivityTaskManager.getService();
          
          // Window 動畫
          mAnimator = new WindowAnimator(this);
          
          // 所有 Window 的根節點 (RootWindowContainer 對象)
          mRoot = new RootWindowContainer(this);    
      }
  ```

### [WMS](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/wm/WindowManagerService.java)#onInitReady - 初始化 WindowManagerPolicy

* WMS#**onInitReady** 函數：**取得 UiThread (另外一個 Thread) 的 Handler，並 ==初始化 [WindowManagerPolicy](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/services/core/java/com/android/server/policy/WindowManagerPolicy.java)== (實作呼叫 PhoneWindowManager#init 函數)**

  ```java=
  // WindowManagerService.java
  
  public class WindowManagerService extends IWindowManager.Stub
          implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
  
      WindowManagerPolicy mPolicy;    // 實作是 ++ PhoneWindowManager 類 ++
  
      public void onInitReady() {
          // @ 重點分析 initPolicy 函數
          initPolicy();
          
          ... 省略部分
      }
  
      private void initPolicy() {
          UiThread.getHandler().runWithScissors(new Runnable() {
              @Override
              public void run() {
                  WindowManagerPolicyThread.set(Thread.currentThread(), Looper.myLooper());
                  mPolicy.init(mContext, WindowManagerService.this, WindowManagerService.this);
              }
          }, 0);
      }
  
  }
  
  // ---------------------------------------------
  // WindowManagerPolicy.java
  
  public interface WindowManagerPolicy extends WindowManagerPolicyConstants {
      
      ... 省略部分
          
      public void init(Context context, IWindowManager windowManager,
          WindowManagerFuncs windowManagerFuncs);
  
      
  // ----------------------------------------------
  // PhoneWindowManager.java

  public class PhoneWindowManager implements WindowManagerPolicy {
      
      ... 省略部分
      
      private class PolicyHandler extends Handler {
          ... 省略部分
      }
      
      public void init(Context context, IWindowManager windowManager,
          WindowManagerFuncs windowManagerFuncs) {
          
          // 省略部分 ... 後面介紹
          
          mHandler = new PolicyHandler();    // 該 Handler 使用 UiThread's Looper
          
      }
  }
  ```

  :::info

  * 從這裡可以知道，`WindowManagerPolicy` & `WMS` & `SystemServer` 都在不同 Thread 中運作
    :::

### [WMS](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/wm/WindowManagerService.java)#displayReady - 準備顯示

1. 標示 WindowAnimator 初始化完成

2. 重新配置設定，保證 DisplayWindowSettings 設定都有被正常使用

3. 透過 ActivityTaskManager 來更新設置

   ```java=
   // WindowManagerService.java
   
       WindowManagerPolicy mPolicy;
       final WindowAnimator mAnimator;
   
       // 在 WMS construct 被初始化
       RootWindowContainer mRoot;
   
       public void displayReady() {
           synchronized (mGlobalLock) {    // ActivityTaskManagerService 中的對象
               if (mMaxUiWidth > 0) {
                   mRoot.forAllDisplays(displayContent -> displayContent.setMaxUiWidth(mMaxUiWidth));
               }
               applyForcedPropertiesForDefaultDisplay();
               
               // 1. 標示初始化完成
               mAnimator.ready();
               mDisplayReady = true;
               // Reconfigure all displays to make sure that forced properties and
               // DisplayWindowSettings are applied.
             
               // 2. 重新配置設定，保證 DisplayWindowSettings 設定都有被正常使用
               mRoot.forAllDisplays(DisplayContent::reconfigureDisplayLocked);
               
               // 硬體是否是可觸控螢幕
               mIsTouchDevice = mContext.getPackageManager().hasSystemFeature(
                       PackageManager.FEATURE_TOUCHSCREEN);
               
               // 硬體是否是 假的可觸控螢幕
               mIsFakeTouchDevice = mContext.getPackageManager().hasSystemFeature(
                       PackageManager.FEATURE_FAKETOUCH);
           }
           
           try {
               // 3. 透過 ActivityTaskManager 來更新設置
               mActivityTaskManager.updateConfiguration(null);
           } catch (RemoteException e) {
           }
       }
   ```

### [WMS](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/wm/WindowManagerService.java)#systemReady - 服務準備完成

* WMS 服務準備完成

  ```java=
  // WindowManagerService.java
  
      public void systemReady() {
          mSystemReady = true;
          mPolicy.systemReady();
          mRoot.forAllDisplayPolicies(DisplayPolicy::systemReady);
          mTaskSnapshotController.systemReady();
          mHasWideColorGamutSupport = queryWideColorGamutSupport();
          mHasHdrSupport = queryHdrSupport();
          mPrimaryDisplayOrientation = queryPrimaryDisplayOrientation();
          mPrimaryDisplayPhysicalAddress =
              DisplayAddress.fromPhysicalDisplayId(SurfaceControl.getPrimaryPhysicalDisplayId());
          UiThread.getHandler().post(mSettingsObserver::loadSettings);
          IVrManager vrManager = IVrManager.Stub.asInterface(
                  ServiceManager.getService(Context.VR_SERVICE));
          if (vrManager != null) {
              try {
                  final boolean vrModeEnabled = vrManager.getVrModeState();
                  synchronized (mGlobalLock) {
                      vrManager.registerListener(mVrStateCallbacks);
                      if (vrModeEnabled) {
                          mVrModeEnabled = vrModeEnabled;
                          mVrStateCallbacks.onVrStateChanged(vrModeEnabled);
                      }
                  }
              } catch (RemoteException e) {
                  // Ignore, we cannot do anything if we failed to register VR mode listener
              }
          }
      }
  ```

### WMS 啟動流程圖

> ![](https://i.imgur.com/5oB1XUK.png)


## WMS 重點

### WMS IPC - Binder 通訊

* 這裡涉及三個對象 `WMS`、`Activity`、`AMS`，其關係如下圖

    1. **`WMS`、`AMS` 其實是在同一個 Process (SystemServer)**
    
    2. **`Session` 是 WMS 中的匿名 Binder，用來處理每個 View 的小需求**

        > 下圖紅代表 Binder Server、綠代表 Binder Client
        > 
        > ![](https://i.imgur.com/R6njadR.png)


### WMS 組織 - ActivityRecord、WindowState

* App 開啟一個 Activity
    1. Activity 在 **AMS 對應的對象是 ActivityRecord**，**^\*^ ActivityRecord 在 WMS 中對應的是 AppWindowToken**

        > * WMS 也需要控制 AMS 中的 ActivityRecord

    2. Activity 在 **WMS 對應的對象是 WindowState**


        > ![](https://i.imgur.com/BDNjnvZ.png)

### 窗口屬性 - Layer Type

* Android 支援多種窗口，並且會將窗口分為三類，定義在 [**WindowManager#LayoutParams**](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/view/WindowManager.java) 中

    1. **Application Window**：普通 App 應用就是使用以下窗口類型 (列出幾種舉例)，區間為 **1 ~ 99** 之間

        | Value | 名稱 | 說明 |
        | -------- | -------- | -------- |
        | 1 | `FIRST_APPLICATION_WINDOW` | 應用類視窗的起始值 |
        | 1 | `TYPE_BASE_APPLICATION` | 其他應用都可覆蓋在該 Window 之上 |
        | 2 | `TYPE_APPLICATION` | 一般的應用窗口 |
        | 3 | `TYPE_APPLICATION_STARTING` | 應用程式啟動前的畫面 (用在加載某個畫面之前的預前畫面) |
        | 4 | `TYPE_DRAWN_APPLICATION` | 在繪畫完成前等待 |
        | 99 | `LAST_APPLICATION_WINDOW` | 應用視窗的結束值 |

        
        ```java=
        // WindowManager#LayoutParams

        public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable {
            
            public static final int FIRST_APPLICATION_WINDOW = 1;
            public static final int TYPE_BASE_APPLICATION   = 1;
            public static final int TYPE_APPLICATION        = 2;
            public static final int TYPE_APPLICATION_STARTING = 3;
            public static final int TYPE_DRAWN_APPLICATION = 4;
            public static final int LAST_APPLICATION_WINDOW = 99;
            
            ...

        }
        ```
        
    2. **Sub Window**：附加在應用之上的視窗，一般稱為 **子窗口**，其值區間為 **1000 ~ 1999** 之間
        
        | Value | 名稱 | 說明 |
        | -------- | -------- | -------- |
        | 1000 | `FIRST_SUB_WINDOW` | SubWindow 的起始值 |
        | 1000 | `TYPE_APPLICATION_PANEL` | 該 SubWindow 顯示在依附的 App 之上 |
        | 1001 | `TYPE_APPLICATION_MEDIA` | 顯示有關 video 的 SubWindow |
        | 1002 | `TYPE_APPLICATION_SUB_PANEL` | 顯示在依附的 app 之上，**也在其他 SubWinow 之上** |
        | 1003 | `TYPE_APPLICATION_ATTACHED_DIALOG` | Dialog 類型的 Window |
        | 1004 | `TYPE_APPLICATION_MEDIA_OVERLAY` | 顯示在 `TYPE_APPLICATION_MEDIA ` 還有底下的應用視窗之間 |
        | 1005 | `TYPE_APPLICATION_ABOVE_SUB_PANEL` | 比 `TYPE_APPLICATION_SUB_PANEL` 更上層的 Window |
        | 1999 | `LAST_SUB_WINDOW` | SubWindow 的結束值 |

        ```java=
        // WindowManager#LayoutParams

        public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable {
            
            public static final int FIRST_SUB_WINDOW = 1000;
            public static final int TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW;
            public static final int TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW + 1;
            public static final int TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW + 2;
            public static final int TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW + 3;
            public static final int TYPE_APPLICATION_MEDIA_OVERLAY  = FIRST_SUB_WINDOW + 4;
            public static final int TYPE_APPLICATION_ABOVE_SUB_PANEL = FIRST_SUB_WINDOW + 5;
                ...
            public static final int LAST_SUB_WINDOW = 1999;

        }
        ```
    3. **System Window**：系統使用的 Window 視窗 (以下列出幾種)，區間為 **2000 ~ 2999** 之間
        
        | Value | 名稱 | 說明 |
        | -------- | -------- | -------- |
        | 2000 | `FIRST_SYSTEM_WINDOW` | System Window 的起始值 |
        | 2001 | `TYPE_STATUS_BAR` | Status bar 的 Window |
        | 2002 | `TYPE_SEARCH_BAR` | 搜尋 Bar 的 Window |
        | 2038 | `TYPE_APPLICATION_OVERLAY` | 覆蓋於其他 Activity 視窗 |
        | 2999 | `LAST_SYSTEM_WINDOW` | System Window 的結束值 |

        ```java=
        // WindowManager#LayoutParams

        public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable {
            
            public static final int FIRST_SYSTEM_WINDOW     = 2000;
            public static final int TYPE_STATUS_BAR         = FIRST_SYSTEM_WINDOW;
            public static final int TYPE_SEARCH_BAR         = FIRST_SYSTEM_WINDOW+1;

            ...

            public static final int TYPE_APPLICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 38;

            ...

            public static final int LAST_SYSTEM_WINDOW      = 2999;
            
        }
        ```
    :::warning
    * 透過該值來決定 Window 應該要放置的層級 (Layer)，但 **如果一個視窗中有多個相同層級的應用又該如何決定呢** ?
        > 看下一小節
    :::

### [WindowState](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/services/core/java/com/android/server/wm/WindowState.java) 初始化 - 決定 Base Layer

* 從 WindState 建構函數可以知道，Base Layer 數值可以從 WindowManagerPolicy#`getWindowLayerLw` 方法取得
    
    :::info
    * 這邊也可以看到 
        1. Stub Window 的 Base Layer 取決於 Parent Window
        
        2. BaseLayer 決定出來後要加上 10000 & 篇移值 1000
        
        3. mSubLayer 決定子窗口應該要在父窗口的偏移植 
    :::
    
    ```java=
    // WindowManagerPolicyConstants.java

    // 保留給多 Window (相同 Layer)
    int TYPE_LAYER_MULTIPLIER = 10000;

    // 保留給多 Window (相同 Layer)，Z 軸偏移量
    int TYPE_LAYER_OFFSET = 1000


    // ---------------------------------------------------------------
    // WindowState.java

    final WindowManagerPolicy mPolicy;

    WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
            WindowState parentWindow, int appOp, WindowManager.LayoutParams a, 
           ... 省略部分入參) {

        // 複製 `WindowManager.LayoutParams`
        mAttrs.copyFrom(a);

        ...

        mPolicy = mWmService.mPolicy;

        ...

        // 判斷目前 Window 是否是 SubWindow
        if (mAttrs.type >= FIRST_SUB_WINDOW && mAttrs.type <= LAST_SUB_WINDOW) {
            // 保留空間給多個相同 Layer 等級的 Window

            // @ 查看 getWindowLayerLw 方法
            mBaseLayer = mPolicy.getWindowLayerLw(parentWindow)    // 1.
                    * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;   // 2.

            // SubWindow 
            mSubLayer = mPolicy.getSubWindowLayerFromTypeLw(a.type);
            ...
        } else {
            // @ 查看 getWindowLayerLw 方法
            mBaseLayer = mPolicy.getWindowLayerLw(this)
                    * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
            mSubLayer = 0;
            ...
        }

    }
    ```
    
    1. [**WindowManagerPolicy**](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/services/core/java/com/android/server/policy/WindowManagerPolicy.java) 透過傳入的 `WindowState` 決定 Window 基礎 Layer 層級 (數值越高，越接近使用者 & 上層)
    
        ```java=
        // WindowManagerPolicy.java

            default int getWindowLayerLw(WindowState win) {
                return getWindowLayerFromTypeLw(win.getBaseType(), win.canAddInternalSystemWindow());
            }

            default int getWindowLayerFromTypeLw(int type, boolean canAddInternalSystemWindow) {
                return getWindowLayerFromTypeLw(type, canAddInternalSystemWindow,
                        false /* roundedCornerOverlay */);
            }

            default int getWindowLayerFromTypeLw(int type, boolean canAddInternalSystemWindow,
                    boolean roundedCornerOverlay) {
                // Always put the rounded corner layer to the top most.
                if (roundedCornerOverlay && canAddInternalSystemWindow) {
                    return getMaxWindowLayer();
                }

                // 1 ~ 99 之間
                if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {
                    return APPLICATION_LAYER;    // APPLICATION_LAYER 是 2
                }

                switch (type) {
                    case TYPE_WALLPAPER:
                        // wallpaper is at the bottom, though the window manager may move it.
                        return  1;
                    case TYPE_PRESENTATION:
                    case TYPE_PRIVATE_PRESENTATION:
                    case TYPE_DOCK_DIVIDER:
                    case TYPE_QS_DIALOG:
                    case TYPE_PHONE:
                        return  3;
                    case TYPE_SEARCH_BAR:
                        return  4;
                    case TYPE_INPUT_CONSUMER:
                        return  5;
                    case TYPE_SYSTEM_DIALOG:
                        return  6;
                    case TYPE_TOAST:
                        // toasts and the plugged-in battery thing
                        return  7;
                    case TYPE_PRIORITY_PHONE:
                        // SIM errors and unlock.  Not sure if this really should be in a high layer.
                        return  8;
                    case TYPE_SYSTEM_ALERT:
                        // like the ANR / app crashed dialogs
                        // Type is deprecated for non-system apps. For system apps, this type should be
                        // in a higher layer than TYPE_APPLICATION_OVERLAY.
                        return  canAddInternalSystemWindow ? 12 : 9;
                    case TYPE_APPLICATION_OVERLAY:
                        return  11;
                    case TYPE_INPUT_METHOD:
                        // on-screen keyboards and other such input method user interfaces go here.
                        return  13;
                    case TYPE_INPUT_METHOD_DIALOG:
                        // on-screen keyboards and other such input method user interfaces go here.
                        return  14;
                    case TYPE_STATUS_BAR:
                        return  15;

                    ... 省略部分 case

                    default:
                        Slog.e("WindowManager", "Unknown window type: " + type);
                        return 3;
                }
            }
        ```
    3. [**WindowManagerPolicy**](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/services/core/java/com/android/server/policy/WindowManagerPolicy.java)#`getSubWindowLayerFromTypeLw` 方法：決定子窗口在父窗口中的偏移量
        :::info
        從這裡可以看出來子窗口可能在 父窗口的上面、也能在下面
        :::

        ```java=
        // WindowManagerPolicy.java

            default int getSubWindowLayerFromTypeLw(int type) {
                switch (type) {
                    case TYPE_APPLICATION_PANEL:
                    case TYPE_APPLICATION_ATTACHED_DIALOG:
                        return APPLICATION_PANEL_SUBLAYER;    // 1
                        
                    case TYPE_APPLICATION_MEDIA:
                        return APPLICATION_MEDIA_SUBLAYER;    // -2
                        
                    case TYPE_APPLICATION_MEDIA_OVERLAY:
                        return APPLICATION_MEDIA_OVERLAY_SUBLAYER;    // -1
                        
                    case TYPE_APPLICATION_SUB_PANEL:
                        return APPLICATION_SUB_PANEL_SUBLAYER;    // 2
                        
                    case TYPE_APPLICATION_ABOVE_SUB_PANEL:
                        return APPLICATION_ABOVE_SUB_PANEL_SUBLAYER;    // 3
                }
                Slog.e("WindowManager", "Unknown sub-window type: " + type);
                return 0;
            }
        ```

### 窗口屬性 - 決定最終 Layer 層級

* 詳細的 Window 視窗層級會在 WMS 中決定，**窗口數值越高，越接近 User** (有可能視窗中有 3 個 Window Type 是 TYPE_APPLICATION)

    ```java=
    // WindowManagerService.java

    public int relayoutWindow(Session session, IWindow client, LayoutParams attrs,
                    int requestedWidth, int requestedHeight, int viewVisibility, int flags,
                    ... 省略部分入參) {
        ...

        int result = 0;
        boolean configChanged;
        final int pid = Binder.getCallingPid();
        final int uid = Binder.getCallingUid();
        final long origId = Binder.clearCallingIdentity();

        synchronized (mGlobalLock) {
            // WindowState 是 Activity 在 WMS 的代表
            final WindowState win = windowForClientLocked(session, client, false);
            if (win == null) {
                return 0;
            }

            final DisplayContent displayContent = win.getDisplayContent();
            final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();

            ...

            if (imMayMove) {
                displayContent.computeImeTarget(true /* updateImeTarget */);

                if (toBeDisplayed) {
                    // 重新計算 Layer 層級
                    // @ 追蹤 assignWindowLayers 方法
                    displayContent.assignWindowLayers(false /* setLayoutNeeded */);
                }
            }


            return result;
        }
    ```

    [**DisplayContent**](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java)#**assignWindowLayers** 方法：準備重新計算該 Window 真正的 Layer 層級

    ```java=
    // DisplayContent.java

    void assignWindowLayers(boolean setLayoutNeeded) {
        ...
    
        // @ 追蹤 assignChildLayers 方法 
        assignChildLayers(getSyncTransaction());
        
        if (setLayoutNeeded) {
            setLayoutNeeded();
        }
        ...
    }


    @Override
    void assignChildLayers(SurfaceControl.Transaction t) {
        assignRelativeLayerForIme(t, false /* forceUpdate */);
        
        // @ 追中 super.assignChildLayers
        super.assignChildLayers(t);
    }
    ```
    最終會呼叫到 [**WindowState**](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/services/core/java/com/android/server/wm/WindowState.java)#assignChildLayers 方法：WindowState 繼承於 [**WindowContainer**](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java) 而其中會儲存該視窗的所有 Window，所以 `assignChildLayers` 行為會迭代所有的 Window 並重新賦予 layer 數值
    ```java=
    // WindowContainer.java

    // 以 Z 軸儲存所有 Window 
    protected final WindowList<E> mChildren = new WindowList<E>();

    // -----------------------------------------------------------------
    // WindowState.java

    public void assignChildLayers(Transaction t) {
        // Main Window 已經計算並保存好，而呼叫該 Function，代表該 View 應該要在 Main Window 之上
        // `PRESERVED_SURFACE_LAYER ` 為 1
        int layer = PRESERVED_SURFACE_LAYER + 1;
        
        // 迭代所有 Window 視窗
        for (int i = 0; i < mChildren.size(); i++) {
            final WindowState w = mChildren.get(i);

            // `APPLICATION_MEDIA_OVERLAY` 比起 `APPLICATION_MEDIA` 還上層
            if (w.mAttrs.type == TYPE_APPLICATION_MEDIA) {
                ...
            } else if (w.mAttrs.type == TYPE_APPLICATION_MEDIA_OVERLAY) {
                ...
            } else {
                // @ 追蹤 WindowState#assignLayer
                w.assignLayer(t, layer);
            }
            // 遞迴呼叫 assignChildLayers 方法
            w.assignChildLayers(t);
            
            // layer 層級 + 1
            layer++;
        }
    }
    ```
    UML 關係如下
    > ![](https://i.imgur.com/284qxeE.png)

    Struct 結構如下
    > ![](https://i.imgur.com/PJdqrWA.png)
    
* WindowContainer 就會直接透過 SurfaceControler 設定 Layer 層級
    
    ```java=
    // WindowState.java  
    
    @Override
    void assignLayer(Transaction t, int layer) {
        if (mStartingData != null) {
            // The starting window should cover the task.
            t.setLayer(mSurfaceControl, Integer.MAX_VALUE);
            return;
        }
        // See comment in assignRelativeLayerForImeTargetChild
        if (needsRelativeLayeringToIme()) {
            getDisplayContent().assignRelativeLayerForImeTargetChild(t, this);
            return;
        }
        // @ 追蹤 super.assignLayer
        super.assignLayer(t, layer);
    }


    // ---------------------------------------------------------
    // WindowContainer.java

    void assignLayer(Transaction t, int layer) {
        // 正在顯示則不調整
        if (mTransitionController.isPlaying()) return;
        
        final boolean changed = layer != mLastLayer || mLastRelativeToLayer != null;
        
        if (mSurfaceControl != null && changed) {
            // @ 查看 setLayer
            setLayer(t, layer);
            mLastLayer = layer;
            mLastRelativeToLayer = null;
        }
    }

    protected void setLayer(Transaction t, int layer) {
        if (mSurfaceFreezer.hasLeash()) {
            // When the freezer has created animation leash parent for the window, set the layer
            // there instead.
            mSurfaceFreezer.setLayer(t, layer);
        } else {
            // Route through surface animator to accommodate that our surface control might be
            // attached to the leash, and leash is attached to parent container.
            mSurfaceAnimator.setLayer(t, layer);
        }
    }


    // ---------------------------------------------------------
    // SurfaceFreezer.java

    void setLayer(SurfaceControl.Transaction t, int layer) {
        if (mLeash != null) {
            // 最終設定 Layer
            t.setLayer(mLeash, layer);
        }
    }
    ```

    > ![](https://i.imgur.com/XqmmPgL.png)

### 窗口屬性 - LayoutParams

* Window 的統一屬性設定在 [**WindowManager#LayoutParams**](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/view/WindowManager.java) 中，並且有分為 `type`、`Flags` 兩種設定
    1. **Type**：上面已經說過，主要有分為 `Application Window`、`Sub Window`、`System Window` 三種

    2. **Flag**：莫認為 0，以下列出幾個 (有滿多的)

        | Value | Flag | 說明 |
        | - | -------- | -------- |
        | 0x00000001 | `FLAG_ALLOW_LOCK_WHILE_SCREEN_ON` | 只要該窗口可見，就允許鎖住屏幕 (可配合 `FLAG_KEEP_SCREEN_ON` 使用) |
        | 0x00000002 | `FLAG_DIM_BEHIND` | 窗口後的 Window 都變淡 |
        | 0x00000004 | `FLAG_BLUR_BEHIND` | 窗口後的 Window 變模糊 |
        | 0x00000008 | `FLAG_NOT_FOCUSABLE` | 該 Window 視窗不可事件，事件會直接傳遞給後方 Window |
        | 0x00000010 | `FLAG_NOT_TOUCHABLE` | 該窗口不接受任何事件 |
        | 0x00000080 | `FLAG_KEEP_SCREEN_ON` | 只要該窗口可見，就保持螢幕亮起 |
        
        使用 Window#setFlags 就可以設置
        ```java=
        Window w = activity.getWindow();
        w.setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON,
                   WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
        ```
        
* System UI 控制：System UI 的設定定義在 [**View**](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/view/View.java) 中，針對每個 View 個別設置

    | Value | Flag | 說明 |
    | - | -------- | -------- |
    | 0 | `SYSTEM_UI_FLAG_VISIBLE` | 設定 System UI 可見 |
    | 0x00000002 | `SYSTEM_UI_FLAG_HIDE_NAVIGATION` | 設定 Navigtion Bar 不可見 |
    | 0x00000004 | `SYSTEM_UI_FLAG_FULLSCREEN` | 螢幕全屏 |
    | 0x00000100 | `SYSTEM_UI_FLAG_LAYOUT_STABLE` | 盡量保持 UI 布局的穩定性 |
    | 0x00000400 | `SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN` | 從螢幕 (0, 0) 開始繪製，也就是會從 Status Bar 開始繪製 |



## Activity 啟動

這裡用一張簡易的流程圖來回憶一下 Acitivty 的啟動流程 (僅列出重點)，並從 ActivityRecord#**showStartingWindow** 繼續往下分析就可以看到 AMS 與 WMS 之間的關係

![](https://i.imgur.com/IC3Y38C.png)

### [ActivityRecord](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/wm/ActivityRecord.java) - showStartingWindow 啟動動畫 AnimationThread 使用

* 先來看一下 ActivityRecord 類的繼承關係，可以看到 **ActivityRecord 也是 WindowContainer 的子類** (WindowContainer 是泛型，可以包含多個 WindowContainer)

  ```java=
  // WindowContainer.java
  
  class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<E>
          implements Comparable<WindowContainer>, Animatable, SurfaceFreezer.Freezable {
  
      // 以 z 軸來儲存 Window 元素
      protected final WindowList<E> mChildren = new WindowList<E>();
  
      ...
  }
  ```

* 接這來看 Window 相關類的關係圖

  > ![](https://i.imgur.com/qbummQT.png)

* 在回到 Activity 啟動的過程中會使用中到 ActivityRecord#**showStartingWindow** 方法顯示 Window 啟動畫面

  | Window 類型                        | 說明                                                         |
  | ---------------------------------- | ------------------------------------------------------------ |
  | `STARTING_WINDOW_TYPE_NONE`          | 不添加 Starting Window                                       |
  | `STARTING_WINDOW_TYPE_SNAPSHOT`      | 當 APP 啟動後並切換到後台，Android 會替 APP 快照(類似截圖)，當 APP 再次回到前景 (使用) 就會使用這個 |
  | `STARTING_WINDOW_TYPE_SPLASH_SCREEN` | 默認行為，與 APP 主題有關                                    |

  ```java=
  // ActivityRecord.java
  
  // 啟動視窗
  StartingData mStartingData;

  void showStartingWindow(ActivityRecord prev, boolean newTask, boolean taskSwitch,
          boolean startActivity, ActivityRecord sourceRecord) {

      ... 省略部分

      // @ 分析 addStartingWindow 函數
      final boolean scheduled = addStartingWindow(packageName, resolvedTheme,
              compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
              prev, newTask || newSingleActivity, taskSwitch, isProcessRunning(),
              allowTaskSnapshot(), activityCreated, mSplashScreenStyleEmpty);

      ... debug 訊息
  }

  boolean addStartingWindow(/* 省略參數 */) {
      ... 省略部分

      // 若已經找 (啟動視窗) 到則不再尋找
      if (mStartingData != null) {
          return false;
      }

      // 從 WindowContainer 中找到啟動視窗
      // @ 查看 findMainWindow 類
      final WindowState mainWin = findMainWindow();

      if (mainWin != null && mainWin.mWinAnimator.getShown()) {
          // App already has a visible window...why would you want a starting window?
          return false;
      }

      // @ 查看 getStartingWindowType 方法
      final int type = getStartingWindowType(newTask, taskSwitch, processRunning,
          allowTaskSnapshot, activityCreated, snapshot);

      ... 省略部分

      // SplashScreenStartingData 實作 StartingData 抽象類
      mStartingData = new SplashScreenStartingData(mWmService, pkg,
              resolvedTheme, compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
              getMergedOverrideConfiguration(), typeParameter);

      // @ 繼續分析 scheduleAddStartingWindow 方法
      scheduleAddStartingWindow();
      return true;

    }

    // 透過 ActivityRecord 決定啟動 Window 類型
    private int getStartingWindowType(boolean newTask, boolean taskSwitch, boolean processRunning,
        boolean allowTaskSnapshot, boolean activityCreated,
        TaskSnapshot snapshot) {

        // 對照上面的表
        if ((newTask || !processRunning || (taskSwitch && !activityCreated))
                && !isActivityTypeHome()) {
            // 第一次啟動...
            return STARTING_WINDOW_TYPE_SPLASH_SCREEN;
        } else if (taskSwitch && allowTaskSnapshot) {
            if (isSnapshotCompatible(snapshot)) {
                return STARTING_WINDOW_TYPE_SNAPSHOT;
            }
            if (!isActivityTypeHome()) {
                return STARTING_WINDOW_TYPE_SPLASH_SCREEN;
            }
            return STARTING_WINDOW_TYPE_NONE;
        } else {
            return STARTING_WINDOW_TYPE_NONE;
        }
    }

    WindowState findMainWindow(boolean includeStartingApp) {
        WindowState candidate = null;
        for (int j = mChildren.size() - 1; j >= 0; --j) {
            final WindowState win = mChildren.get(j);
            final int type = win.mAttrs.type;
            // No need to loop through child window as base application and starting types can't be
            // child windows.
            if (type == TYPE_BASE_APPLICATION
                    || (includeStartingApp && type == TYPE_APPLICATION_STARTING)) {

                if (win.mAnimatingExit) {
                    candidate = win;
                } else {
                    return win;
                }
            }
        }
        return candidate;
    }

    void scheduleAddStartingWindow() {
        if (StartingSurfaceController.DEBUG_ENABLE_SHELL_DRAWER) {
            // Debug 用
            mAddStartingWindow.run();
        } else {
            // 注意：我們真的想做 sendMessageAtFrontOfQueue() 
            // 因為我們想在任何其他排隊的消息之前盡快處理消息。
            if (!mWmService.mAnimationHandler.hasCallbacks(mAddStartingWindow)) {
                ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Enqueueing ADD_STARTING");


             // mWmService 就是 WindowManagerService，而 mAnimationHandler 就是 Handler
             // @ mAnimationHandler 準備分析
                mWmService.mAnimationHandler.postAtFrontOfQueue(mAddStartingWindow);
            }
        }
    }
    ```

* 在這裡可以看到 ActivityRecord 會將自己的 Runnable 透過 WMS#mAnimationHandler (就是 Handler) 推到 **AnimationThread 的線程**
    
    > `mAddStartingWindow` 下個小節說明
    
    ```java=
    // WindowManagerService.java

    final Handler mAnimationHandler = new Handler(
      AnimationThread.getHandler().getLooper()
    );
    ```

    > ![](https://i.imgur.com/coRLsig.png)


### [ActivityRecord](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/wm/ActivityRecord.java) - AddStartingWindow 添加啟動視窗

* 先來查看一下 ActivityRecord 推送到 WMS#**mAnimationHandler** 的 Runnable 任務

  ```java=
  // ActivityRecord.java
  
      private final AddStartingWindow mAddStartingWindow = new AddStartingWindow();
  
      // 複習一下~
      void scheduleAddStartingWindow() {
          if (StartingSurfaceController.DEBUG_ENABLE_SHELL_DRAWER) {
              ... 省略
          } else {
              // 注意：我們真的想做 sendMessageAtFrontOfQueue() 
              // 因為我們想在任何其他排隊的消息之前盡快處理消息。
              if (!mWmService.mAnimationHandler.hasCallbacks(mAddStartingWindow)) {
                  ... Log 訊息
                  mWmService.mAnimationHandler.postAtFrontOfQueue(mAddStartingWindow);
              }
          }
      }
  
      // @ 分析 AddStartingWindow 類
      private class AddStartingWindow implements Runnable {
  
          // run 會被 WMS#AnimationHandler 線程呼叫
          @Override
          public void run() {
              // Can be accessed without holding the global lock
              final StartingData startingData;
  
              synchronized (mWmService.mGlobalLock) {
                  // There can only be one adding request, silly caller!
                  if (!StartingSurfaceController.DEBUG_ENABLE_SHELL_DRAWER) {
                      // Debug 用
                      mWmService.mAnimationHandler.removeCallbacks(this);
                  }
                  if (mStartingData == null) {
                      // 動畫被取消
                      ... 省略 Log 訊息
                      return;
                  }
                  startingData = mStartingData;
              }
  
              ... 省略 Log 訊息
  
              WindowManagerPolicy.StartingSurface surface = null;
  
              try {
                  // createStartingSurface 是抽象方法，具體邏輯由子類完成
                  // 目前來說就是使用 ++SplashScreenStartingData 類++
                  surface = startingData.createStartingSurface(ActivityRecord.this);
  
              } catch (Exception e) {
                  Slog.w(TAG, "Exception when adding starting window", e);
              }
  
              if (surface != null) {
                  boolean abort = false;
                  synchronized (mWmService.mGlobalLock) {
                      // If the window was successfully added, then we need to remove it.
                      if (mStartingData == null) {
                          ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Aborted starting %s: startingData=%s",
                                  ActivityRecord.this, mStartingData);
                          mStartingWindow = null;
                          mStartingData = null;
                          abort = true;
                      } else {
                          mStartingSurface = surface;
                      }
                      if (!abort) {
                          ProtoLog.v(WM_DEBUG_STARTING_WINDOW,
                                  "Added starting %s: startingWindow=%s startingView=%s",
                                  ActivityRecord.this, mStartingWindow, mStartingSurface);
                      }
                  }
                  if (abort) {
                      surface.remove(false /* prepareAnimation */);
                  }
              } else {
                  ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Surface returned was null: %s",
                          ActivityRecord.this);
              }
          }
      }
  ```

* StartingData 是抽象類，而在目前的狀況，實作者是 [**SplashScreenStartingData 類**](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/wm/SplashScreenStartingData.java) (addStartingWindow 內創建的對象就是 SplashScreenStartingData)

  ```java=
  // SplashScreenStartingData.java
  
  class SplashScreenStartingData extends StartingData {
      ... 省略部分
      
      SplashScreenStartingData(WindowManagerService service, String pkg, int theme,
              CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, int icon,
              int logo, int windowFlags, Configuration mergedOverrideConfiguration, int typeParams) {
          super(service, typeParams);
          
          ... 省略部分
      }
      @Override
      StartingSurface createStartingSurface(ActivityRecord activity) {
          // 1. mService 就是 WindowManagerService 
          
          // 2. mStartingSurfaceController 就是 StartingSurfaceController
          return mService.mStartingSurfaceController.createSplashScreenStartingSurface(
                  activity, mPkg, mTheme, mCompatInfo, mNonLocalizedLabel, mLabelRes, mIcon,
                  mLogo, mWindowFlags, mMergedOverrideConfiguration,
                  activity.getDisplayContent().getDisplayId());
      }
      @Override
      boolean needRevealAnimation() {
          return true;
      }
  }
  ```

* 呼叫 [**StartingSurfaceController**](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/wm/StartingSurfaceController.java) 類的 `createSplashScreenStartingSurface`  方法，這個方法會呼叫到 WMS 中的 mPolicy (**也就是 [PhoneWindowManager 類](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/policy/PhoneWindowManager.java)**)

  ```java=
  // StartingSurfaceController.java 
  
      StartingSurface createSplashScreenStartingSurface(ActivityRecord activity, String packageName,
              int theme, CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes,
              int icon, int logo, int windowFlags, Configuration overrideConfig, int displayId) {
          
          if (!DEBUG_ENABLE_SHELL_DRAWER) {
              // WMS 中的 mPolicy 實作類是 PhoneWindowManager
              return mService.mPolicy.addSplashScreen(activity.token, activity.mUserId, packageName,
                      theme, compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
                      overrideConfig, displayId);
          }
          synchronized (mService.mGlobalLock) {
              final Task task = activity.getTask();
              if (task != null && mService.mAtmService.mTaskOrganizerController.addStartingWindow(
                      task, activity, theme, null /* taskSnapshot */)) {
                  return new ShellStartingSurface(task);
              }
          }
          return null;
      }
  ```

  > ![](https://i.imgur.com/kUEkRmR.png)


### ViewRootImpl - 繪製 View

* 回顧一下：我們目前是在添加 Activity 啟動 Window 的路上，前面幾個步驟介紹了 AMS 對 WMS 添加 Activity 的啟動畫面 **showStartingWindow** ~ 

* 透過 [**PhoneWindowManager**](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/policy/PhoneWindowManager.java)#`addSplashScreen` 會創建 PhoneWindow 對象，並設定 Window 的 WindowManager#LayoutParams 屬性，之後在取得 WindowManager 後呼叫 addView
    1. 創建 PhoneWindow 對象 (Window 的實作類)
    
    2. 設定 Window 對象的 WindowManager#LayoutParams (預設 type 類型是 Application)
    
    3. **將 Activity#appToken ==也就是 App 的 ApplicationThread==** 賦予給 WMS，方便之後調用
    
    4. 透過 context#getSystemService 取得 WMS 的代理類，並呼叫 WMS#addView 方法 (其實是透過 Session 通知 WMS 添加 Window)

          ```java=
          // PhoneWindowManager.java

              @Override
              public StartingSurface addSplashScreen(IBinder appToken, int userId, String packageName,
                      int theme, CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes,
                      int icon, int logo, int windowFlags, Configuration overrideConfig, int displayId) {

                  ... 省略部分

                  WindowManager wm = null;
                  View view = null;
                  try {

                      ... 省略部分

                      // 484 很孰悉 ~ ? 在分析 xml 創建 View 的時候也會看到它
                      final PhoneWindow win = new PhoneWindow(context);        // 創建 PhoneWindow

                      ... 省略部分

                      // 設定 Window 用的 LayoutParams
                      win.setLayout(WindowManager.LayoutParams.MATCH_PARENT,
                              WindowManager.LayoutParams.MATCH_PARENT);
                      final WindowManager.LayoutParams params = win.getAttributes();        // 設定 LayoutParams

                      // appToken 是 App 的 ApplicationThread
                      params.token = appToken;

                      ... 省略部分

                      addSplashscreenContent(win, context);
                    
                      // 取得 WindowManager
                      wm = (WindowManager) context.getSystemService(WINDOW_SERVICE);
                      // 取得 Decor View (沒有的話就創建)
                      view = win.getDecorView();

                      ... 省略 Log

                      wm.addView(view, params);        // 呼叫 WMS#addView !!

                      return view.getParent() != null ? new SplashScreenSurface(view, appToken) : null;
                  } /* catch、finally */
                  return null;
              }
          ```

* 而 WindowManager 是 interface，它的實作類是 [**WindowManagerImpl**](https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/view/WindowManagerImpl.java)，在其中有一個 WindowManagerGlobal 對象

    > WindowManagerGlobal 是一個進程單例對象，WindowManagerGlobal 就是一個 **橋接模式設計**，也是透過它來與 WMS 通訊

  ```java=
  // WindowManagerImpl.java
  
  public final class WindowManagerImpl implements WindowManager {
  
      private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
  
      @Override
      public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
          applyTokens(params);
          
          // @ 查看 addView 方法
          mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,
                  mContext.getUserId());
      }
  }
  ```

  > ![](https://i.imgur.com/Wxcf8rB.png)

* 最後呼叫到 [**WindowManagerGlobal**](https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/view/WindowManagerGlobal.java)#addView 方法，WindowManagerGlobal 類會管理該 ++進程中所有的 ViewRootImpl & Window & LayoutParams++

  ```java=
  // WindowManagerGlobal.java
  
      private final Object mLock = new Object();
  
      // 進程中所有的 View
      private final ArrayList<View> mViews = new ArrayList<View>();
  
      private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
  
      public void addView(View view, ViewGroup.LayoutParams params,
              Display display, Window parentWindow, int userId) {
          
          // 基礎判空
          if (view == null) {
              throw new IllegalArgumentException("view must not be null");
          }
          if (display == null) {
              throw new IllegalArgumentException("display must not be null");
          }
          if (!(params instanceof WindowManager.LayoutParams)) {
              throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
          }
          
          final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
          
          // 父 Window 判斷
          if (parentWindow != null) {
              parentWindow.adjustLayoutParamsForSubWindow(wparams);
          } else {
              // If there's no parent, then hardware acceleration for this view is
              // set from the application's hardware acceleration setting.
              final Context context = view.getContext();
              if (context != null
                      && (context.getApplicationInfo().flags
                              & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
                  wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
              }
          }
          
          ViewRootImpl root;
          View panelParentView = null;
          
          // 添加 View 的流程使用 鎖
          synchronized (mLock) {
              
              ... 省略部分
              
              // 創建 ViewRootImpl 對象
              root = new ViewRootImpl(view.getContext(), display);
              view.setLayoutParams(wparams);
              
              
              mViews.add(view);
              mRoots.add(root);
              mParams.add(wparams);
              // do this last because it fires off messages to start doing things
              try {
                  
                  // 準備繪製
                  root.setView(view, wparams, panelParentView, userId);
              } /* 省略 catch */
          }
      }
  ```

  > ![](https://i.imgur.com/Xx31zKa.png)

* 步驟到 [**ViewRootImpl**](https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/view/ViewRootImpl.java) 後有兩個重點

  1. 透過 ViewRootImpl#mWindow (內部類 W) 與 WMS 通訊 WindowSession 也是一個系統服務

     成員 mWindow 在 ViewRootImpl 創建時被賦予，並且 mWindow 是 ViewRootImpl 的內部類，它繼承 IWindow.Stub (作為 Binder 的 Server 端)
     > **W 它是 WMS 調用客戶端的接口** (之後當 Window 產生變化時會通知IWindow.Stub)

     ```java=
     // ViewRootImpl.java

     final W mWindow;
     
     public ViewRootImpl(@UiContext Context context, Display display, IWindowSession session,
                 boolean useSfChoreographer) {
     
         ... 省略
     
         mWindow = new W(this);
     }
     
     static class W extends IWindow.Stub {
     
         W(ViewRootImpl viewAncestor) {
             mViewAncestor = new WeakReference<ViewRootImpl>(viewAncestor);
             
             mWindowSession = viewAncestor.mWindowSession;
         }
     
         ... 省略
     }
     ```

  2. 與 WMS 通訊成功後，就會進入 View 的繪製流程 (執行 onMeasure、onLayout、onDraw)

     ```java=
     // ViewRootImpl.java
     
         public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
             setView(view, attrs, panelParentView, UserHandle.myUserId());
         }
     
         public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
                 int userId) {
             synchronized (this) {
                 if (mView == null) {
                     mView = view;
     
                     ... 省略部分
     
                     requestLayout();
     
                     ... 省略部分
                 }
             }
         }
     
         @Override
         public void requestLayout() {
             if (!mHandlingLayoutInLayoutRequest) {
                 checkThread();
                 mLayoutRequested = true;
     
                 // 分析 
                 scheduleTraversals();
             }
         }
     
         final class TraversalRunnable implements Runnable {
             @Override
             public void run() {
                 doTraversal();
             }
         }
     
         final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
     
         void scheduleTraversals() {
             if (!mTraversalScheduled) {
                 mTraversalScheduled = true;
     
                 // 將 mTraversalRunnable 任務添加到 Handler 中 
                 mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
                 mChoreographer.postCallback(
                         Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
                 notifyRendererOfFramePending();
                 pokeDrawLockIfNeeded();
             }
         }
     
         void doTraversal() {
             if (mTraversalScheduled) {
                 mTraversalScheduled = false;
     
                 ... 省略部分
     
                 performTraversals();
     
                 ... 省略部分
             }
         }


        // 該函數很長，只會列出幾個我們常見的要點 (其中包含了本地的繪製，WMS 通訊...)
        private void performTraversals() {
            ... 省略部分

            try {
                ... 省略部分

                // 參數放入 mWindow，之後讓 WMS 作回調 ~ !!!
                // WindowSession 也是一個系統服務
                // @ 分析 addToDisplayAsUser
                res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes,
                        getHostVisibility(), mDisplay.getDisplayId(), userId,
                        mInsetsController.getRequestedVisibilities(), inputChannel, mTempInsets,
                        mTempControls);
                ... 省略部分
            } /* 省略 catch、finally */

            if (mFirst || windowShouldResize || insetsChanged ||
                    viewVisibilityChanged || params != null || mForceNextWindowRelayout) {

                ... 省略部分

                if (!mStopped || wasReportNextDraw) {
                    // 會執行 onMeasure
                    performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
                    ... 省略部分
                }  
            } 

            ... 省略部分

            if (didLayout) {

                // 會執行 onLayout
                performLayout(lp, mWidth, mHeight);
            }

            ... 省略部分

            if (!cancelDraw) {
                performDraw();
            }

            ... 省略部分
        }
    ```
:::info
* WindowSession 也是一個系統服務
    不過它是一個匿名服務，必須透過 WMS 才可以取得 WindowSession 對象
:::
> ![](https://i.imgur.com/F50hDuH.png)

### 透過 WMS 取得 [Session](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/wm/Session.java) - addToDisplayAsUser


* 先來看一下 ViewRootImpl 的 Session 是如何取得

  > 透過 WindowManagerGlobal 取得 Session

  ```java=
  // ViewRootImpl.java
  
      public ViewRootImpl(Context context, Display display) {

          // 透過 WindowManagerGlobal 取得 Session (Session 的代理類)
          // @ 追 getWindowSession 方法
          mWindowSession = WindowManagerGlobal.getWindowSession();

          ... 省略部分
      }
  
      // 複習一下上面的調用 performTraversals 方法的重點
      private void performTraversals() {
          ... 省略部分
  
          try {
              ... 省略部分
  
              // 透過 mWindow 與 WMS 通訊
              // WindowSession 也是一個系統服務
              res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes,
                      getHostVisibility(), mDisplay.getDisplayId(), userId,
                      mInsetsController.getRequestedVisibilities(), inputChannel, mTempInsets,
                      mTempControls);
              ... 省略部分
          } /* 省略 catch、finally */
  
          ... 省略部分
      }
  ```

* 從上面看到 Session 的取得是透過 WindowManagerGlobal 類：而 WindowManagerGlobal 則是透過 ^1.^ ServiceManager 取得 WMS，^2.^ 再透過創建 Session 給客戶端

  ```java=
  // WindowManagerGlobal.java

    public static IWindowSession getWindowSession() {
      synchronized (WindowManagerGlobal.class) {
          if (sWindowSession == null) {
              try {
                  InputMethodManager imm = InputMethodManager.getInstance();

                  // 追 getWindowManagerService 方法
                  IWindowManager windowManager = getWindowManagerService();
                  // 使用 WMS 間接取得 Session 服務，查看 openSession 方法
                  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;
      }
    }


    public static IWindowManager getWindowManagerService() {
    
        synchronized (WindowManagerGlobal.class) {
            if (sWindowManagerService == null) {
                // 1. 透過 ServiceManager 取得
                sWindowManagerService = IWindowManager.Stub.asInterface(
                        ServiceManager.getService("window"));
                try {
                    if (sWindowManagerService != null) {
                        ValueAnimator.setDurationScale(
                                sWindowManagerService.getCurrentAnimatorScale());
                    }
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
            return sWindowManagerService;
        }
    }
    ```

* 在這邊就可以看到，**Session 是一個 ++匿名服務++**，它不能透過 ServiceManger 直接取得，**==Session　必須透過 WMS 取得該服務==，每個 ViewRootImpl 對應一個 Session 服務**
  > 同時一個 View 也對應了一個 ViewRootImpl，也就是說一個 Session 對應一個 View
  ```java=
  // WindowManagerService.java
      @Override
      public IWindowSession openSession(IWindowSessionCallback callback) {
          // 每個 ViewRootImpl 對應一個 Session 服務
          return new Session(this, callback);
      }
  ```

  > ![](https://i.imgur.com/fuatshg.png)


* 最後透過 Session#addToDisplayAsUser 來訪問 WMS#addWindow 方法

  ```java=
  class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
      
      final WindowManagerService mService;
      
          @Override
      public int addToDisplayAsUser(IWindow window, WindowManager.LayoutParams attrs,
              int viewVisibility, int displayId, int userId, InsetsVisibilities requestedVisibilities,
              InputChannel outInputChannel, InsetsState outInsetsState,
              InsetsSourceControl[] outActiveControls) {
          
          // @ 呼叫到 WindowManagerService#addWindow 方法
          // window -> ViewRootImpl 中的 W 類
          return mService.addWindow(this, window, attrs, viewVisibility, displayId, userId,
                  requestedVisibilities, outInputChannel, outInsetsState, outActiveControls);
      }
      
  }
  ```

  > ![](https://i.imgur.com/iqAP4Kp.png)


## [WindowManagerService](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/wm/WindowManagerService.java)

WindowManagerService 簡單理解起來是掌管該裝置所有的 Window 的上下關係 (z 軸的管理)、傳輸繪製任務到底層

> 先介紹 [**WindowManagerService**](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/wm/WindowManagerService.java) 幾個比較重要的成員變量
    

| 變量 | 說明 | 補充 |
| - | - | - |
| mPolicy : WindowManagerPolicy                  | 定義一個窗口策略所須遵循的 **++通用規範++**，並提供了 WindowManger 特定的 UI 行為 | WindowManager 是 interface，它的實作是 **PhoneWindowManager** |
| mSessions : ArraySet<Session\>                 | 它主要用於進程通訊，**其他的應用進程的 View 想要和 WMS 通訊，都要通過 Session 與 WMS 通訊** | 每個應用進程是透過 **WindowManagerGlobal 取得 Session**      |
| **mWindowMap** : WindowHashMap                     | WindowHashMap 繼承 HashMap，**用來保存所有 ViewImpl**；Key: IBinder、Value: WindowState | Key 其實是 IWindow (ViewRootImpl 的內部類 W)、Value 是保存窗口訊息 |
| mFinishedStarting : ArrayList<AppWindowToken\> | AppWindowToken 的父類是 WindowToken 當 APP 要向 WMS 申請一個新窗口時就要給予這個 token 讓 WMS 驗證 | **每個 Activity 對應一個 AppWindowToken (一個 AppWindowToken 管理多個 WindowToken 視窗)** |
| mResizingWindows : ArrayList<WindowState\>     | 用來儲存正在調整大小的視窗 |                                                              |
| mAnimator : WindowAnimator | 管理 Window 動畫 & 特效 |  |
| mH : H | H 是 Handler 類型，它是屬於 **==DisplayThread== Looper** | 將消息給 WMS |
| mInputManager : InputManagerService | IMS 系統服務，當使用者有輸入訊息時，WMS 會給予一個適合的視窗 |  |

> ![](https://i.imgur.com/YEG17Yp.png)

### [WMS](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/wm/WindowManagerService.java) - addWindow 方法

* 上面有提及，APP 在添加視窗時會透過 WindowManagerGlobal#Session，而系統 Session 又會呼叫 WMS#**addWindow** 方法，由於該方法較長，以下分為 3 個階段來看

  1. 檢查、取得 **DisplayContent**、判斷是否是子視窗

     1. 檢查啟動權限：是否是系統窗口 & 使用者設定的權限... 等等
     
     2. 取得 DisplayContent (該 WindowState 應該放置的視窗)，判斷是否重複添加
     
     3. 判斷是否是子視窗　（`FIRST_SUB_WINDOW` <= type <= `LAST_SUB_WINDOW`）
     
     4. 從 mWindowMap 嘗試取得父窗口，如果目前視窗是子窗口，但 ParentWindow 取出的也是子窗口，就會返回錯誤 (ADD_BAD_SUBWINDOW_TOKEN)
     
     5. 判斷 WindowManager 的各種 Type 類型，做相應的檢查處理

         ```java=
         // WindowManagerService.java

             public int addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility,
                     int displayId, /*省略部分參數*/ ) {

                 ... 省略部分

                 // 1. 檢查啟動權限：是否是系統窗口 & 使用者設定的權限... 等等 (傳入 type... 判斷)
                 int res = mPolicy.checkAddPermission(attrs.type, isRoundedCornerOverlay, attrs.packageName,
                         appOp);
                 if (res != ADD_OKAY) {
                     return res;
                 }

                 WindowState parentWindow = null;

                 ... 省略部分

                 synchronized (mGlobalLock) {
                     if (!mDisplayReady) {
                         throw new IllegalStateException("Display has not been initialialized");
                     }

                     //  2. 取得 DisplayContent (該 WindowState 應該放置的視窗)
                     final DisplayContent displayContent = getDisplayContentOrCreate(displayId, attrs.token);
                     if (displayContent == null) {
                         ProtoLog.w(WM_ERROR, "Attempted to add window to a display that does "
                                 + "not exist: %d. Aborting.", displayId);
                         return WindowManagerGlobal.ADD_INVALID_DISPLAY;
                     }

                     ... 省略部分

                     // 3. 判斷窗口類型：當前是判斷是否是子視窗
                     if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {
                         // 4. 如果是子窗口，嘗試取得父窗口 (attrs 是 LayoutParams)
                         parentWindow = windowForClientLocked(null, attrs.token, false);
                         // 判斷是否有 ParentWindow
                         if (parentWindow == null) {
                             return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
                         }

                         // 並且 Parent 視窗不可以是子視窗
                         if (parentWindow.mAttrs.type >= FIRST_SUB_WINDOW
                                 && parentWindow.mAttrs.type <= LAST_SUB_WINDOW) {
                             return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
                         }
                     }

                     ... 省略部分
                 }
             }
         ```

         > ![](https://i.imgur.com/QdMP943.png)

    2. **取得 Activity 的 AppWindowToken**

        1. 若有 Parent 窗口，那它會與子窗口有相同的 Token (IBinder)，也就是說 ParentWindow & SubWindow 有相同的 Token
        2. 設定窗口類型 (如果有 ParentWindow，則使用 ParentWindow 的 type)
        3. 依照狀況 **取得 WindowToken**，細節請看註解，這裡有多個 token 相關關鍵字容易混淆，我們以下表區分

            | Token | 說明 | 補充 |
            | -------- | -------- | - |
            | `token` | 宣告為 WindowToken 類型 |  |
            | `attrs.token` | 類型為 IBinder，代表該窗口的主人 | 如果有 Parent Window 窗口主人就轉為 Parent |
            |  |  |  |
            |  |  |  |

            > ![](https://i.imgur.com/LBWtRTN.png)


            ```java=
            // WindowManagerService.java

            public int addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility,
                 int displayId, /*省略部分參數*/ ) {

                ... 省略部分

                synchronized (mGlobalLock) {

                    ActivityRecord activity = null;
                    final boolean hasParent = parentWindow != null;

                    // 1. 若有 Parent 窗口，那它會與子窗口有相同的 Token
                    WindowToken token = displayContent.getWindowToken(
                            hasParent ? parentWindow.mAttrs.token : attrs.token);

                    // 2. 設定窗口類型 (如果有父窗口，則使用父窗口的 type)
                    final int rootType = hasParent ? parentWindow.mAttrs.type : type;

                    boolean addToastWindowRequiresToken = false;

                    final IBinder windowContextToken = attrs.mWindowContextToken;

                    // 若是沒有 token 會依照情況做創建
                    if (token == null) {
                        if (!unprivilegedAppCanCreateTokenWith(parentWindow, callingUid, type,
                                rootType, attrs.token, attrs.packageName)) {
                            return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                        }

                        // 如果有 Parent 就優先使用 Parent 的 token
                        if (hasParent) {
                            // Use existing parent window token for child windows.
                            token = parentWindow.mToken;
                        } else if (mWindowContextListenerController.hasListener(windowContextToken)) {
                            ... 省略部分

                        } else {
                            final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();

                            // 如果 AMS 中沒有 Token 就會創建一個
                            // 創建 WindowToken
                            token = new WindowToken.Builder(this, binder, type)
                                    .setDisplayContent(displayContent)
                                    .setOwnerCanManageAppTokens(session.mCanAddInternalSystemWindow)
                                     .setRoundedCornerOverlay(isRoundedCornerOverlay)
                                    .build();
                        }
                    } else if (rootType >= FIRST_APPLICATION_WINDOW
                        && rootType <= LAST_APPLICATION_WINDOW) {    //應用類型窗口
                        // 從 token 中取得 ActivityRecord
                        activity = token.asActivityRecord();

                        ... 判斷是否可取到 Activity 等等，無法取得則返回錯誤

                    } else if (...) {
                        ... 省略其他判斷
                    }

                ... 省略部分
                }
                
            }
            ```

        > ![](https://i.imgur.com/Uqjv4bB.png)

        如果是應用窗口，可以發現 WMS 儲存的 Token 與 AMS 中的 AcitivtyRecord 有關係

        ```java=
         // 從 token 中取得 ActivityRecord

         activity = token.asActivityRecord();
        ```
        > ![](https://i.imgur.com/GCvRLS7.png)
        
    3. 保存 Window 狀態、Client 訊息、判斷顯示視窗、做調整
        1. **==創建 WindowState==**，保存 Window 的所有訊息狀態
        
        2. 判斷 Client 視窗狀態
        
        3. 檢查顯示視窗
        
        4. 依照情況修正 LayoutParams
        
        5. 緩存資料在 WMS 中
            * `mWindowMap` 集合添加 IWindow 視窗
            * `WindowToken` 集合添加到 WindowStete 內
        
        6. 透過 DisplayPolicy#**addWindowLw** 添加 Window 到系統中
        
        7. **使用 `assignChildLayers` 方法，==重新分配 Layer 層級==**
            ```java=
            // WindowManagerService.java

            public int addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility,
                    int displayId, /*省略部分參數*/ ) {

                ... 省略部分

                synchronized (mGlobalLock) {

                    ... 省略部分

                    // 1. 保存 Window 的所有訊息狀態 (View 的狀態)
                    // 存入相對應的 token (WindowToken)
                    final WindowState win = new WindowState(this, session, client, token, parentWindow,
                            appOp[0], attrs, viewVisibility, session.mUid, userId,
                            session.mCanAddInternalSystemWindow);

                    if (win.mDeathRecipient == null) {
                        // 2. 客戶端已經死亡，不必繼續往下執行
                        return WindowManagerGlobal.ADD_APP_EXITING;
                    }

                    // 3. 檢查顯示視窗
                    if (win.getDisplayContent() == null) {
                        ... log msg
                        return WindowManagerGlobal.ADD_INVALID_DISPLAY;
                    }

                    final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
                    // 4. 依照情況修正 LayoutParams
                    displayPolicy.adjustWindowParamsLw(win, win.mAttrs);
                    win.setRequestedVisibilities(requestedVisibilities);
                    attrs.flags = sanitizeFlagSlippery(attrs.flags, win.getName(), callingUid, callingPid);

                    // 檢查 Type 是否合法
                    res = displayPolicy.validateAddingWindowLw(attrs, callingPid, callingUid);
                    if (res != ADD_OKAY) {
                        return res;
                    }

                    ... 省略部分

                    win.attach();     // WindowState#attach

                    // 5. mWindowMap 添加 IWindow 視窗
                    mWindowMap.put(client.asBinder(), win); // View Binder(ViewRootImpl#W) 作為 Key，存入 State

                    ... 省略部分

                    // 5. WindowStete 添加到 WindowToken 內
                    win.mToken.addWindow(win);

                    // 6. 將 Window 加入系統中
                    displayPolicy.addWindowLw(win, attrs);

                    ... 省略部分
                    
                    // 7. 重新分配 Layer 層級
                    win.getParent().assignChildLayers();
                }
                Binder.restoreCallingIdentity(origId);
                return res;
            }
            ```
            > ![](https://i.imgur.com/loS8xQ4.png)

* 總結一下 addWindow 方法
  1. 對添加的 Window 進行檢查
  
  2. WindowToken 相關處理，如果可以創建的話就由 WMS 創建，或是有 ParentWindow 也可以直接使用
      > 應用視窗 ActivityRecord 也可以看做 Token
  
  3. WindowState 保存每個 IWindow (ViewRootImpl)，並將 WindowState 存入 WindowToken 
  
  4. 創建 & 配置 DisplayContent，完成窗口添加到系統的準備工作

### WMS 刪除 Window
// TODO:

## Appendix & FAQ

:::info
參考

https://ljd1996.github.io/2020/08/27/Android-Window%E6%9C%BA%E5%88%B6%E5%8E%9F%E7%90%86/
:::

###### tags: `Android Framework`