2017年12月20日 星期三

演算法筆記 - 經典河內塔

最近在看CNN的文章的時候, 提到了Hierarchy的概念
目的是以divide-and-conquer的概念來將大問題切割為小問題
以image recognition / computer vision來說, "直覺上"每個pixels只會跟鄰近的pixels有關聯性(correlated to local pixels)
也就是說在Machine Learning的領域上又回歸到了"自然的"、"直覺式"的算法 XD

有點扯遠了, 讓我們先回到第一個關鍵字 "divide-and-conquer"
這個詞被翻譯做分治法, 也就是分而治之
如果你熟悉於科技業, 一定在很多問題/系統上都已經看過了分治法解決問題的能力.
如果你是一個科技新鮮人, 那麽這個經典的河內塔也是你在人生的遞迴之路的重要基石.

河內塔 (Tower of Hanoi) 的故事
 - 引述自 Wiki 百科 <河內塔>

傳說
印度某間寺院有三根柱子,上串64個金盤。寺院裡的僧侶依照一個古老的預言,以一定的規則移動這些盤子;
規則: 有三根杆子A,B,C。A杆上有N個(N>1)穿孔圓盤,盤的尺寸由下到上依次變小。
要求按下列規則將所有圓盤移至C杆:
  1. 每次只能移動一個圓盤;
  2. 大盤不能疊在小盤上面。

預言說當這些盤子移動完畢,世界就會滅亡。這個傳說叫做
梵天寺之塔問題(Tower of Brahma puzzle)。
若傳說屬實,僧侶們需要264 − 1步才能完成這個任務;若他們每秒可完成一個盤子的移動,就需要5845億年才能完成。
(整個宇宙現在也不過137億年。)
這個傳說有若干變體:寺院換成修道院、僧侶換成修士等等。
寺院的地點眾說紛紜,其中一說是位於越南河內,所以被命名為「河內塔」。

移動河內塔
那麼, 要怎移動河內塔呢?
我們先來推一次, 當瓦片數為 3 的時候,  流程如下
移動次數\塔 A B C
1 2
3

1
2 3 2 1
3 3 1
2

4
1
2
3
5 1 2 3
6 1
2
3
7

1
2
3
* 最少需要移動 2^3 - 1 = 7步

接著, 讓我們簡化問題變成瓦片數為 2 的狀況:
移動次數\塔 A B C
1 2 1
2
1 2
3

1
2
看一下, 瓦片數為 2 的狀況跟 3 的狀況, 對於前 3 動的移動順序是不是非常相似
他們最大的差異, 就是將1號跟2號瓦片, 移動到C柱或者是B柱的差別
當N = 2的時候, 直接移動到C柱即可
當N = 3的時候, 你需要先將(1, 2)移動到B柱, 接著將(3)移到C柱, 最後將再將(1, 2)移到C柱

到這一步, 其實已經分治完成了 ヽ(∀゚)人(゚∀゚)人( ゚∀)人(∀゚)人(゚∀゚)人( ゚∀)ノ
河內塔的關鍵就在於, 要適時的把這些柱子當作暫存的buffer來使用

接著我們試著寫出虛擬碼:
moveDisk(N, from, to, buffer) {
    if (N == 0) {
         // 中止條件! 沒得動啦
        return;
    } else {
        // 把倒數第二片開始的盤子從A柱(from)移動到B柱(buffer)去
        move((N-1), from, buffer, to);
        // 把最底下的盤子N 
從A柱(from)移動到C柱(to)去
        print("Move Disk %d from %d to %d", N, from, to);
        // 把倒數第二片開始的盤子從B柱(buffer)移動到C柱(to)去
        move((N-1), buffer, to, from);
    }
}

以上!

2017年12月14日 星期四

Process/Thread name in Android native layer

看起來在 libc/bionic 裡面有個 pthread_setname_np.cpp 可以去設置 process name,
但是不曉得為什麼沒有提供 getname 方法 (security?)

setname 的使用方法如下:

pthread_setname_np(pthread_self(), "xxx");

他的實作如下:

int pthread_setname_np(pthread_t t, const char* thread_name) {
  ErrnoRestorer errno_restorer;

  size_t thread_name_len = strlen(thread_name);
  if (thread_name_len >= MAX_TASK_COMM_LEN) {
    return ERANGE;
  }

  // Changing our own name is an easy special case.
  if (t == pthread_self()) {
    return prctl(PR_SET_NAME, thread_name) ? errno : 0;
  }

  // We have to change another thread's name.
  pthread_internal_t* thread = __pthread_internal_find(t);
  if (thread == NULL) {
    return ENOENT;
  }
  pid_t tid = thread->tid;

  char comm_name[sizeof(TASK_COMM_FMT) + 8];
  snprintf(comm_name, sizeof(comm_name), TASK_COMM_FMT, tid);
  int fd = open(comm_name, O_CLOEXEC | O_WRONLY);
  if (fd == -1) {
    return errno;
  }
  ssize_t n = TEMP_FAILURE_RETRY(write(fd, thread_name, thread_name_len));
  close(fd);

  if (n < 0) {
    return errno;
  } else if (n != static_cast<ssize_t>(thread_name_len)) {
    return EIO;
  }
  return 0;
}

我們可以如果是在當前的 thread name 進行更名的話,
就會直接透過 prctl(PR_SET_NAME, thread_name) 來完成

否則則會嘗試去開啟 /proc/self/task/%d/comm , 其中 %d 將會帶入目標 thread 的 tid,
並直接對他做 write 的動作.

這個 PR_SET_NAME 定義在 bionic/libc/kernel/uapi/linux/prctl.h
#define PR_SET_NAME 15
#define PR_GET_NAME 16
但沒有看到去呼叫 PR_GET_NAME 的方法

我們可以在 bionic/linker/debugger.cpp 找到一個較為接近的例子:

  char thread_name[MAX_TASK_NAME_LEN + 1]; // one more for termination
  if (prctl(PR_GET_NAME, reinterpret_cast<unsigned long>(thread_name), 0, 0, 0) != 0) {
    strcpy(thread_name, "<name unknown>");
  } else {
    // short names are null terminated by prctl, but the man page
    // implies that 16 byte names are not.
    thread_name[MAX_TASK_NAME_LEN] = 0;
  }

不過較奇妙的是他並沒有去 /proc/self/task 下面尋找對應的 tid
看起來是只要去 $cat /proc/<tid>/comm 就可以拿到 process name 了
(並且 /proc/<tid>/comm 這一路上的權限看起來都是有開啟的...)


2017年11月15日 星期三

Android 系統筆記 - Big/Little endian

雖然是很一般的東西但偶而腦筋還是會打結呢....(´・ω・`;)

Big Endian 就是把最高位元組存到地址的最低位元. (這句話hen難理解對吧)

直接上範例:
地址假設我們從 0x00000000 開始
假設你有一個 32 位元的 integer 如下
int32_t big = 0x12345678;

地址最低位元當然就是 0x0000000囉
那麼最高位元組指的是什麼呢? 就是你的 0x12啦
Mapping到記憶體之後會變成這樣:

Address Value
0x00000000 0x12
0x00000001 0x34
0x00000002 0x56
0x00000003 0x78

反過來說 Little-endian 就是會變成這樣囉: 

Address Value
0x00000000 0x78
0x00000001 0x56
0x00000002 0x34
0x00000003 0x12

那假設你是一個 array 會怎麼樣呢?
我們令 int32_t test[2] = {0x12345678, 0x11223344}

記憶體的排列會變成這樣唷!

Address Value
0x00000000 0x78
0x00000001 0x56
0x00000002 0x34
0x00000003 0x12
0x00000004 0x44
0x00000005 0x33
0x00000006 0x22
0x00000007 0x11

也就是 Big-Endian 或者 Little-Endian 只有影響到該變數在記憶體存放的位置而已  (*´∀`)ノ

2017年11月7日 星期二

Android系統筆記 - Java HandlerThread

Java 的 Thread 已經被封裝成了幾種易用的形式, 簡單介紹一下幾個重點:

Runnable 
可以理解為"任務內容"
我們會把要完成的一件工作寫在Runnable的物件裡面.
例如這樣:
Runnable task = new Runnable() {
    public void run() {
        Log.e("Start run!");
    }
};

Thread
單執行序, 負責處理一個 Runnable 的物件.
用法也很簡單, 我們可以直接把上面那個 task 直接丟進去:
Thread oneTimeThread = new Thread(task);
oneTimeThread.start();
這樣就會開另外一個執行序去執行一次 task

另外Thread也有一個建構子是沒有帶入Runnable物件的,
這樣子呼叫start()就會直接執行Thread裡面的run()方法,
一般使用在你的物件去繼承了 Thread 的時候, 例如這樣:

public class myThread extends Thread {
    @Override
    public void run() {
        System.out.println("Thread start running!");
        for (;;) {
            System.out.println("Thread keep running!");
        }
    }

    public static void main(String[] argv) {
        Thread t = new myThread();
        t.start();
        for (;;) {
            System.out.println("Main Thread");
        }
    }
}

執行以後就會交錯印出訊息, 可能像這樣:
Main Thread
Main Thread
Thread start running!
Thread keep running!
Thread keep running!
Main Thread
Thread keep running!
Main Thread
(因為執行序的關係, main通常會先開始跑)

你也可以嘗試將 infinite for loop 換成只跑10次來觀察看看.
另外需要一提的是, 這個 Thread 物件也可以直接呼叫 run() method,
這時就會在 main thread 執行, 而不會另外開一個執行序去跑, 所以執行結果會變成這樣:
(把 infinite for loop 換成只跑10次)

Thread start running!
Thread keep running!
Thread keep running!
Main Thread
Main Thread
=> 等到 run() 方法執行完之後才會往下跑


HandlerThread
相較於上面的單執行序, 這個物件幫你封裝了MessageQueue在裏頭,
它的定位很像是 Native layer 的 Thread 物件, 啟動之後會自動運行 threadLoop(),
並且會等待事件進入時才開始工作 (可以想像成它呼叫了 pollOnce(-1))

handlerThread開始執行之後, 會開啟另外一個執行序來等待工作. (*這時候還沒有task放進去跑)
我會把它拿來處理asynchronous訊息的溝通, 用法如下:
HandlerThread workThread = new HandlerThread("WorkThread");
workThread.start(); // 開始等待事件進入

// 分發事件的方式要使用 Handler 物件來進行分配, 關於 Handler 請往下看
Handler workHandler = new Handler(workThread.getLooper());
workHandler.post(task);

// 除了 post 以外, 還有 postAtTime, postDelayed 等形式可以來控制 task 執行到的時間

* 因為這個執行序沒有所謂的執行結束, 所以不用的時候記得要關閉掉它
(例如, 放在 constructor 跟 destructor)

Handler
這個物件除了可以使用post()分配事件給HandlerThread,
還有一個用法是去override handleMessage(), 將事件預先定義好, 並使用 Message 來開始工作.
Handler workHandler = new Handler(workThread.getLooper()) {
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        switch (msg.what) {
            case 1:
                Log.e("Handler", "Received message");
                break;
            default:
                Log.e("Handler", "Unsupprted message");
                break;
        }
    }
}

workHandler.sendEmptyMessage(1);
(有興趣進去看 Handler.java的話, 你會發現 post() 函數也是將 Runnable 物件封裝為 Message)

2017年10月30日 星期一

公開金鑰加密簡述

常常會看到公開金鑰加密 (Public-key cryptography), 但是好像大家都寫得太複雜了 XD

看到 Public-key, 很自然的我們會想到那應該還有 Private-key 吧?
=> 是的沒錯, 公開金鑰加密的演算法中有一副成對的鑰匙, 公鑰(A)以及私鑰(B).
所以也稱作 非對稱式加密 (asymmetric cryptography) (鑰匙只有一把的狀況叫做對稱式)

它們的功能很簡單:

假設你現在有一份文件 X
Case. 1) 公鑰加密
  以公鑰加密文件得到 A(X)
  此時可以用私鑰解密得到 B(A(X)) = X
  但公鑰無法解密 A(A(X)) = ?_?

Case. 2) 私鑰加密
  以私鑰加密文件得到 B(X)
  此時可以用公鑰解密得到 A(B(X)) = X

看看 Case. 1 (公鑰加密) 的狀況:
  假設小明跟小花進行通訊
  小明把公鑰(A)給了小花, 小花用公鑰加密內容傳送回來給小明 A(X).
  => 因為私鑰(B)不會公開給別人, 所以別人傳送給小明的內容不會被解密.

再看看 Case. 2 (私鑰加密) 的狀況:
  小明現在用私鑰加密B(X)...結果發現:  咦? 怎麼大家都可以解開 @@!
  => 因為公鑰是公開的嘛, A(B(X)) = X
  所以私鑰不是這樣直接在通訊上面使用,
  而是小花用她自己的私鑰(D)來加密訊息, 然後再用小明的公鑰加密A(D(X))丟給小明.
  小明先用自己的私鑰(B)解開得到B(A(D(X))) = D(X)
  再用小花的公鑰(C)解開C(D(X)) = X

以上~

2017年10月19日 星期四

Android系統筆記 - EGL library 的接入口

對 Android 來說, 我們呼叫 eglXXX 的 API 時是呼叫到 libEGL 內部的實作

舉個 BootAnimation 的例子,  在創建 EGLSurface 會呼叫 eglCreateWindowSurface
frameworks/base/cmds/bootanimation/BootAnimation.cpp

status_t BootAnimation::readyToRun() {
    ...
    EGLSurface surface;
    surface = eglCreateWindowSurface(display, config, s.get(), NULL);
    ...
}

那我們可以看到 libEGL 最後又呼叫了 cnx->egl.eglCreateWindowSurface
frameworks/native/opengl/libs/EGL/eglApi.cpp


EGLSurface eglCreateWindowSurface(  EGLDisplay dpy, EGLConfig config,
                                    NativeWindowType window,
                                    const EGLint *attrib_list)
{
    ...
    EGLSurface surface = cnx->egl.eglCreateWindowSurface
}

那這個 cnx->egl 又是哪裡來的?
我們往前面看到開頭的地方呼叫的 validate_display_connection
EGLSurface eglCreateWindowSurface(  EGLDisplay dpy, EGLConfig config,
                                    NativeWindowType window,
                                    const EGLint *attrib_list)
{ egl_connection_t* cnx = NULL; const egl_display_ptr dp = validate_display_connection(dpy, cnx); ... }

跳轉進入 egl.cpp
frameworks/native/opengl/libs/EGL/egl.cpp

egl_display_ptr validate_display_connection(EGLDisplay dpy,
        egl_connection_t*& cnx) {
    cnx = NULL;
    egl_display_ptr dp = validate_display(dpy);
    if (!dp)
        return dp;
    cnx = &gEGLImpl;
    if (cnx->dso == 0) {
        return setError(EGL_BAD_CONFIG, egl_display_ptr(NULL));
    }
    return dp;
}

這邊把 cnx 指向了 gEGLImpl 的地址, 一般這種形式可以預期他指向一個 function pointer
搜尋一下, 可以發現到在 init 的時候確實做了這件事:
static EGLBoolean egl_init_drivers_locked() {
    if (sEarlyInitState) {
        // initialized by static ctor. should be set here.
        return EGL_FALSE;
    }

    // get our driver loader
    Loader& loader(Loader::getInstance());

    // dynamically load our EGL implementation
    egl_connection_t* cnx = &gEGLImpl;
    if (cnx->dso == 0) {
        cnx->hooks[egl_connection_t::GLESv1_INDEX] =
                &gHooks[egl_connection_t::GLESv1_INDEX];
        cnx->hooks[egl_connection_t::GLESv2_INDEX] =
                &gHooks[egl_connection_t::GLESv2_INDEX];
        cnx->dso = loader.open(cnx);
    }

    return cnx->dso ? EGL_TRUE : EGL_FALSE;
}

我們再看看 Loader::open 做了什麼
void* Loader::open(egl_connection_t* cnx)
{
    void* dso;
    driver_t* hnd = 0;

    dso = load_driver("GLES", cnx, EGL | GLESv1_CM | GLESv2);
    if (dso) {
        hnd = new driver_t(dso);
    } else {
        // Always load EGL first
        dso = load_driver("EGL", cnx, EGL);
        if (dso) {
            hnd = new driver_t(dso);
            hnd->set( load_driver("GLESv1_CM", cnx, GLESv1_CM), GLESv1_CM );
            hnd->set( load_driver("GLESv2",    cnx, GLESv2),    GLESv2 );
        }
    }
    ...
}

展開 load_driver...完畢!

void *Loader::load_driver(const char* kind,
        egl_connection_t* cnx, uint32_t mask)
{
    /* 定義了 MatchFile 的 class, 設定搜尋路徑是 /vendor/lib/egl 與 /system/lib/egl */
    ...

    // 假設已經找到了對應的 so
    void* dso = dlopen(driver_absolute_path, RTLD_NOW | RTLD_LOCAL);
    if (mask & EGL) {
        /*
          比較麻煩的是這個 getProcAddress 也是從 dlsym 拿出來的...所以如果你都是 prebuilt library 就找不到實作
          不過我們可以隨便找一個實作來看, 都是大同小異的.
          簡單來說就是對照 name, 取出 API 的型態
        */
        getProcAddress = (getProcAddressType)dlsym(dso, "eglGetProcAddress");

        // 這裡取出了 cnx->egl 的地址, 並轉型為 __eglMustCastToProperFunctionPointerType (其實就是void)
        egl_t* egl = &cnx->egl;
        __eglMustCastToProperFunctionPointerType* curr =
            (__eglMustCastToProperFunctionPointerType*)egl;

        /* 這個 elg_names 的定義是從 egl_entries.in 來的, 放在 frameworks/native/opengl/libs/EGL/egl_entries.in
  char const * const egl_names[] = {
      #include "egl_entries.in"
      NULL    
  };
        */
        char const * const * api = egl_names;
        while (*api) {
            char const * name = *api;
            __eglMustCastToProperFunctionPointerType f =
                (__eglMustCastToProperFunctionPointerType)dlsym(dso, name);
            // 如果 dlsym 找不到才會嘗試去使用 getProcAddress 來做對照, 應該是為了擴充性的考量.
            if (f == NULL) {
                // couldn't find the entry-point, use eglGetProcAddress()
                f = getProcAddress(name);
                if (f == NULL) {
                    f = (__eglMustCastToProperFunctionPointerType)0;
                }
            }
            // 把 function pointer 一個一個 mapping... 完畢
            *curr++ = f;
            api++;
        }
    }
    ...
}

另外, 由於你的 library 可能都是 vendor 封裝好的, 所以要看 EGL 的行為的話,
可以參考 Google 為了 Emulator 所寫的 Software OpenGL ES library
檔案放在這裡: frameworks/native/opengl/libagl/egl.cpp

2017年10月11日 星期三

Android系統筆記 - Surface simple flow (2) Surface Flinger 的創建

作為一個 Native 的 Service, 想當然爾是在某個 main_xxx 帶起來的.
讓我們看看 init.rc, 果然找到了服務如下:


service surfaceflinger /system/bin/surfaceflinger
    class core
    user system
    group graphics drmrpc
    onrestart restart zygote

surfaceflinger 的 Makefile 放在 frameworks/native/services/surfaceflinger 下面
=> main_surfaceflinger.cpp

int main(int, char**) {
    ...
    // instantiate surfaceflinger
    sp<SurfaceFlinger> flinger = new SurfaceFlinger();

    // initialize before clients can connect
    flinger->init();

    // publish surface flinger
    sm->addService(String16(SurfaceFlinger::getServiceName()), flinger, false);
    ...

    // run in this thread
    flinger->run();
}

1.) new SurfaceFlinger();
=> 建構子讀了一些 property 並設置對應的 debug 訊息, 暫時跳過.
     這邊可以看到它繼承了 BnSurfaceComposer

SurfaceFlinger::SurfaceFlinger()
    :   BnSurfaceComposer(), xxx

=> 第一次建立 SurfaceFlinger 時, 呼叫 MessageQueue 的 init
void SurfaceFlinger::onFirstRef()
{
    mEventQueue.init(this);
}

=> 這裡保存了 mFlinger, 並生出 Looper & Handler
void MessageQueue::init(const sp<SurfaceFlinger>& flinger)  
{  
    mFlinger = flinger;  
    mLooper = new Looper(true);  
    mHandler = new Handler(*this);  
}

# mHandler 是定義在 MessageQueue 裡面的 subClass,
  它繼承了 MessageHandler, 並在 mQueue 保存了自己這個 MessageQueue

    class Handler : public MessageHandler {
 enum {
     eventMaskInvalidate     = 0x1,
     eventMaskRefresh        = 0x2,
     eventMaskTransaction    = 0x4
 };
 MessageQueue& mQueue;
 int32_t mEventMask;
    public:
 Handler(MessageQueue& queue) : mQueue(queue), mEventMask(0) { }
 virtual void handleMessage(const Message& message);
 void dispatchRefresh();
 void dispatchInvalidate();
 void dispatchTransaction();
    };

2.) flinger->init();

這邊先貼上完整的代碼, 下面一步一步分析

void SurfaceFlinger::init() {
    ALOGI(  "SurfaceFlinger's main thread ready to run. "
            "Initializing graphics H/W...");

    Mutex::Autolock _l(mStateLock);

    // initialize EGL for the default display
    // EGL 是 OpenGL ES 的接點, 主要功能是完成 OpenGL 創建 Context, 繪製Surface, 配置Framebuffer属性、Swap提交繪製結果
    mEGLDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
    eglInitialize(mEGLDisplay, NULL, NULL);

    // start the EventThread
    sp<VSyncSource> vsyncSrc = new DispSyncSource(&mPrimaryDispSync,
            vsyncPhaseOffsetNs, true, "app");
    mEventThread = new EventThread(vsyncSrc);
    sp<VSyncSource> sfVsyncSrc = new DispSyncSource(&mPrimaryDispSync,
            sfVsyncPhaseOffsetNs, true, "sf");
    mSFEventThread = new EventThread(sfVsyncSrc);
    mEventQueue.setEventThread(mSFEventThread);

    // Initialize the H/W composer object.  There may or may not be an
    // actual hardware composer underneath.
    mHwc = new HWComposer(this,
            *static_cast<HWComposer::EventHandler *>(this));

    // get a RenderEngine for the given display / config (can't fail)
    mRenderEngine = RenderEngine::create(mEGLDisplay, mHwc->getVisualID());

    // retrieve the EGL context that was selected/created
    mEGLContext = mRenderEngine->getEGLContext();

    LOG_ALWAYS_FATAL_IF(mEGLContext == EGL_NO_CONTEXT,
            "couldn't create EGLContext");

    // initialize our non-virtual displays
    for (size_t i=0 ; i<DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES ; i++) {
        DisplayDevice::DisplayType type((DisplayDevice::DisplayType)i);
        // set-up the displays that are already connected
        if (mHwc->isConnected(i) || type==DisplayDevice::DISPLAY_PRIMARY) {
            // All non-virtual displays are currently considered secure.
            bool isSecure = true;
            createBuiltinDisplayLocked(type);
            wp<IBinder> token = mBuiltinDisplays[i];

            sp<IGraphicBufferProducer> producer;
            sp<IGraphicBufferConsumer> consumer;
            BufferQueue::createBufferQueue(&producer, &consumer,
                    new GraphicBufferAlloc());
            // 為什麼這裡要自己 new GraphicBufferAlloc() 而不是跟 Layer 一樣只有呼叫 producer & consumer ?

            sp<FramebufferSurface> fbs = new FramebufferSurface(*mHwc, i,
                    consumer);
            int32_t hwcId = allocateHwcDisplayId(type);
            sp<DisplayDevice> hw = new DisplayDevice(this,
                    type, hwcId, mHwc->getFormat(hwcId), isSecure, token,
                    fbs, producer,
                    mRenderEngine->getEGLConfig());
            if (i > DisplayDevice::DISPLAY_PRIMARY) {
                // FIXME: currently we don't get blank/unblank requests
                // for displays other than the main display, so we always
                // assume a connected display is unblanked.
                ALOGD("marking display %zu as acquired/unblanked", i);
                hw->setPowerMode(HWC_POWER_MODE_NORMAL);
            }
            mDisplays.add(token, hw);
        }
    }

    // make the GLContext current so that we can create textures when creating Layers
    // (which may happens before we render something)
    getDefaultDisplayDevice()->makeCurrent(mEGLDisplay, mEGLContext);

    mEventControlThread = new EventControlThread(this);
    mEventControlThread->run("EventControl", PRIORITY_URGENT_DISPLAY);

    // set a fake vsync period if there is no HWComposer
    if (mHwc->initCheck() != NO_ERROR) {
        mPrimaryDispSync.setPeriod(16666667);
    }

    // initialize our drawing state
    mDrawingState = mCurrentState;

    // set initial conditions (e.g. unblank default device)
    initializeDisplays();

    // start boot animation
    startBootAnim();
}

=> 先跳過EGL, 來看 DispSyncSource,
     它繼承了 VSyncSource 跟 DispSync::Callback, 並傳入了 mPrimaryDispSync 的 address.
     這個 mPrimaryDispSync 的類型是 DispSync, 它會啟動一個 DispSyncThread
DispSync::DispSync() :
 mRefreshSkipCount(0),
 mThread(new DispSyncThread()) {
    
    mThread->run("DispSync", PRIORITY_URGENT_DISPLAY + PRIORITY_MORE_FAVORABLE);
  
    reset();
    beginResync();
}

=> 這個 threadLoop 會進行計時, 並在計時結束時,
             把所有需要觸發 callback 的 listener 蒐集到 callbackInvocations 裡面一起觸發
        while (true) {
            Vector<CallbackInvocation> callbackInvocations;

            nsecs_t targetTime = 0;

     // Scope for lock
            {
                if (mPeriod == 0) {
                    err = mCond.wait(mMutex);
                    continue;
                }

                nextEventTime = computeNextEventTimeLocked(now);
                targetTime = nextEventTime;

                bool isWakeup = false;

                if (now < targetTime) {
                    err = mCond.waitRelative(mMutex, targetTime - now);

                    if (err == TIMED_OUT) {
                        isWakeup = true;
                    }
                }
                now = systemTime(SYSTEM_TIME_MONOTONIC);

                if (isWakeup) {
                    mWakeupLatency = ((mWakeupLatency * 63) +
                            (now - targetTime)) / 64;
                    if (mWakeupLatency > 500000) {
                        // Don't correct by more than 500 us
                        mWakeupLatency = 500000;
                    }
                }

                callbackInvocations = gatherCallbackInvocationsLocked(now);
            }

            if (callbackInvocations.size() > 0) {
                fireCallbackInvocations(callbackInvocations);
            }
        }

=> 觸發就是呼叫 onDispSyncEvent
void fireCallbackInvocations(const Vector<CallbackInvocation>& callbacks) {
   for (size_t i = 0; i < callbacks.size(); i++) {
       callbacks[i].mCallback->onDispSyncEvent(callbacks[i].mEventTime);
   }
}

=> 回到一開始 DispSyncSource 繼承的 DispSync::Callback 如下, 需要 implement onDispSyncEvent
class Callback: public virtual RefBase {
public:
    virtual ~Callback() {};
    virtual void onDispSyncEvent(nsecs_t when) = 0;
};

=> 可以看到在內部的實作如下
class DispSyncSource : public VSyncSource, private DispSync::Callback {
private:
    virtual void onDispSyncEvent(nsecs_t when) {
        sp<VSyncSource::Callback> callback;
        {
            Mutex::Autolock lock(mCallbackMutex);
            callback = mCallback;
            if (mTraceVsync) {
                mValue = (mValue + 1) % 2;
                ATRACE_INT(mVsyncEventLabel.string(), mValue);
            }
        }
        if (callback != NULL) {
            callback->onVSyncEvent(when);
        }
    }
}

=> 那麼 mCallback 又是從哪來的?
=> 答案是透過 setCallback 來的,
                   下一行的 mEventThread = new EventThread(vsyncSrc);
  透過它的 EventThread::enableVSyncLocked function 來呼叫,
   他的 Callback class 如下:
class Callback: public virtual RefBase {
    public:
        virtual ~Callback() {}
        virtual void onVSyncEvent(nsecs_t when) = 0;
};

=> 而實作的形式為
void EventThread::onVSyncEvent(nsecs_t timestamp) {
    Mutex::Autolock _l(mLock);
    mVSyncEvent[0].header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC;
    mVSyncEvent[0].header.id = 0;
    mVSyncEvent[0].header.timestamp = timestamp;
    mVSyncEvent[0].vsync.count++;
    mCondition.broadcast();
}
所以當一個 DispSync 的 Event 來時, 會透過 onDispSyncEvent => onVSyncEvent

=> 下一行的 mSFEventThread 也做了差不多的事情,
   只是這邊多呼叫了 mEventQueue.setEventThread(mSFEventThread),
   將 mSFEventThread 保存到 MessageQueue 裡面的 mEventThread 變量
   (這邊看不出來要幹嘛...好像沒用到?)

=> 再來生成了一個 HW Composer
    mHwc = new HWComposer(this,
            *static_cast<HWComposer::EventHandler *>(this));

=> 這裡會跟 VSync 掛勾 (mCBContext->procs.vsync = &hook_vsync,
              把Callback function註冊給HWC)

HWComposer::HWComposer(const sp<SurfaceFlinger>& flinger, EventHandler& handler)
{
    ...
    // Note: some devices may insist that the FB HAL be opened before HWC.
    int fberr = loadFbHalModule();
    loadHwcModule();
    if (mHwc) {
        ALOGI("Using %s version %u.%u", HWC_HARDWARE_COMPOSER,
           (hwcApiVersion(mHwc) >> 24) & 0xff,
           (hwcApiVersion(mHwc) >> 16) & 0xff);
        if (mHwc->registerProcs) {
             mCBContext->hwc = this;
             mCBContext->procs.invalidate = &hook_invalidate;
             mCBContext->procs.vsync = &hook_vsync;
             if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1))
                 mCBContext->procs.hotplug = &hook_hotplug;
             else
                 mCBContext->procs.hotplug = NULL;
             memset(mCBContext->procs.zero, 0, sizeof(mCBContext->procs.zero));
             mHwc->registerProcs(mHwc, &mCBContext->procs);
        }

        // don't need a vsync thread if we have a hardware composer
        needVSyncThread = false;
        // always turn vsync off when we start
        eventControl(HWC_DISPLAY_PRIMARY, HWC_EVENT_VSYNC, 0);
        // the number of displays we actually have depends on the
        // hw composer version
        if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_3)) {
            // 1.3 adds support for virtual displays
            mNumDisplays = MAX_HWC_DISPLAYS;
        }
    }
}

=> 呼叫 RenderEngine::create(), 將 EGLDisplay 與 hwc format 放入

 RenderEngine* RenderEngine::create(EGLDisplay display, int hwcFormat) {
     RenderEngine* engine = NULL;
     switch (version) {
        engine = new GLESXXRenderEngine();
     }
     engine->setEGLHandles(config, ctxt);

     return engine;
 }

=> 對所有 non-virtual diplay 進行初始化

    // initialize our non-virtual displays
    for (size_t i=0 ; i<DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES ; i++) {
        DisplayDevice::DisplayType type((DisplayDevice::DisplayType)i);

        // set-up the displays that are already connected
        if (mHwc->isConnected(i) || type==DisplayDevice::DISPLAY_PRIMARY) {
            // All non-virtual displays are currently considered secure.
            bool isSecure = true;
            createBuiltinDisplayLocked(type);
            wp<IBinder> token = mBuiltinDisplays[i];

            sp<IGraphicBufferProducer> producer;
            sp<IGraphicBufferConsumer> consumer;
            BufferQueue::createBufferQueue(&producer, &consumer,
                    new GraphicBufferAlloc());
            /* 不曉得為什麼這裡要自己 new GraphicBufferAlloc(),
               而不是跟 Layer 一樣只有呼叫 producer & consumer ? */

            sp<FramebufferSurface> fbs = new FramebufferSurface(*mHwc, i,
                    consumer);
            int32_t hwcId = allocateHwcDisplayId(type);
            sp<DisplayDevice> hw = new DisplayDevice(this,
                    type, hwcId, mHwc->getFormat(hwcId), isSecure, token,
                    fbs, producer,
                    mRenderEngine->getEGLConfig());
            if (i > DisplayDevice::DISPLAY_PRIMARY) {
                // FIXME: currently we don't get blank/unblank requests
                // for displays other than the main display, so we always
                // assume a connected display is unblanked.
                ALOGD("marking display %zu as acquired/unblanked", i);
                hw->setPowerMode(HWC_POWER_MODE_NORMAL);
            }
            mDisplays.add(token, hw);
        }
    }

=> createBufferQueue 之前會先 new 一個 allocator 出來 (new GraphicBufferAlloc())
           不過在 createBufferQueue 內部也會檢查 allocator 是否為 NULL ... 然後最終還是會呼叫到 new GraphicBufferAlloc()

=> 之後在 BufferQueueProducer 呼叫 dequeueBuffer / allocateBuffers 時會透過這個 allocator 生成 GraphicBuffer
  透過 GraphicBuffer 與 GraphicBufferAlloctor 也就是 gralloc module 進行交互 (注意是 GraphicBufferAlloctor 而不是 GraphicBufferAlloc ...)
                   主要是透過 gralloc module 去進行緩衝區的分配 (Either on RAM or Framebuffer)

=> createBufferQueue 對 producer 跟 consumer 進行了配置, 分別對應到 BufferQueueProducer 跟 BufferQueueConsumer
  他們藉由 BufferQueueCore(allocator) 進行交互. (另外雖然 allocator 也帶入了 core, 但好像沒有拿來用)

=> 接下來生成一個 FramebufferSurface, 並帶入了 HWComposer 與剛剛生成的 consumer
=> consumer 作為 FramebufferSurface 繼承的父類別的 ConsumerBase 內部的 mConsumer 對象被保存起來
  hwc 則保存在 mHwc 對象中, 稍微爬了一下代碼, 裡面會呼叫 Hwc 對於 fence 的操作等功能

=> 後面在 BufferQueueProducer 的內部, 我們會看到它取出了 frameAvailableListener = mCore->mConsumerListener;
  並去呼叫 frameAvailableListener->onFrameAvailable(item);
  mCore->mConsumerListener = consumerListener; 則是在 BufferQueueConsumer::connect() 賦值的, 這個我們後面會再看到.

=> 再來, 生成 DisplayDevice, 這裡將 producer 帶入

  DisplayDevice::DisplayDevice()
  {
      mNativeWindow = new Surface(producer, false);
      ANativeWindow* const window = mNativeWindow.get();

      /*
       * Create our display's surface
       */

      EGLSurface surface;
      EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
      if (config == EGL_NO_CONFIG) {
   config = RenderEngine::chooseEglConfig(display, format);
      }
      surface = eglCreateWindowSurface(display, config, window, NULL);

      ...

      if (mType >= DisplayDevice::DISPLAY_VIRTUAL) {
   // reset virtual display buffer format.
   // because it is changed by eglCreateWindowSurface().
   native_window_set_buffers_format(window, format);
   window->setSwapInterval(window, 0);
      }
  }


=> 可以看到這邊又生成了一個 Surface 對象,
     把 producer 帶入, 保存在 mGraphicBufferProducer 內

   Surface::Surface(
    const sp<IGraphicBufferProducer>& bufferProducer,
    bool controlledByApp)
       : mGraphicBufferProducer(bufferProducer),
         mGenerationNumber(0)
   {
       // Initialize the ANativeWindow function pointers.
       ANativeWindow::setSwapInterval  = hook_setSwapInterval;
       ANativeWindow::dequeueBuffer    = hook_dequeueBuffer;
       ANativeWindow::cancelBuffer     = hook_cancelBuffer;
       ...
   }
=> 之後會透過 Surface 對象來呼叫 BufferQueueProducer 的 allocateBuffers / dequeueBuffer 等動作

=> 取得剛剛設定好的 Surface 對象, 在 Constructor 裡面已經完成了一些指定 ANativeWindow* const window = mNativeWindow.get();
  生成 EGL 用的 surface, display 等對象

      /*
       * Create our display's surface
       */
      EGLSurface surface;
      EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
      if (config == EGL_NO_CONFIG) {
   config = RenderEngine::chooseEglConfig(display, format);
      }
      surface = eglCreateWindowSurface(display, config, window, NULL);

      mSurface = surface;
      mDisplay = display;

=> 先總結一下, 現在的 producer 藉由 DisplayDevice 的 mNativeWindow 傳遞給了 EGL
  而 consumer 則是給了 FramebufferSurface, 等收到了 onFrameAvailable 之後, 就會呼叫 HWComposer 的 fbPost 函數開始輸出

void FramebufferSurface::onFrameAvailable(const BufferItem& /* item */) {  
    sp<GraphicBuffer> buf;  
    sp<Fence> acquireFence;  
    status_t err = nextBuffer(buf, acquireFence);  
    if (err != NO_ERROR) {  
        ALOGE("error latching nnext FramebufferSurface buffer: %s (%d)",  
            strerror(-err), err);  
        return;  
    }  
    err = mHwc.fbPost(mDisplayType, acquireFence, buf);  
    if (err != NO_ERROR) {  
        ALOGE("error posting framebuffer: %d", err);  
    }
}

=> 啟動 EventControlThread, 並呼叫一次 eventControl (一開始 mVsyncEnabled 是 false)
    mFlinger->eventControl(HWC_DISPLAY_PRIMARY, SurfaceFlinger::EVENT_VSYNC,
            mVsyncEnabled);

=> 這個 eventControl 最終會呼叫到 HWComposer 的 HAL 的 eventControl, 以 mx6 為例,
  它會呼叫 hwc_eventControl, 並設定 VSyncThread 的 mEnabled 值
ctx->m_vsync_thread->setEnabled(enabled);

=> mEnabled = false 的時候會讓 threadLoop 卡在這邊等 mCondition


bool VSyncThread::threadLoop()
{
    { // scope for lock
        Mutex::Autolock _l(mLock);
        while (!mEnabled) {
            mCondition.wait(mLock);
        }
    }
    if (mFakeVSync) {
        performFakeVSync();
    }
    else {
        performVSync();
    }
    return true;
}


=> 呼叫 initializeDisplays(); 送出一個 Async 的 command (MessageScreenInitialized)
=> 前面追過了, 收到之後會執行 handler, 所以會呼叫 onInitializeDisplays

=> SurfaceFlinger 啟動完成, 呼叫 property_set 來啟動 bootAnimation

不定參數印 log

From the UNIXProcess_md.c #ifdef DEBUG_PROCESS   /* Debugging process code is difficult; where to write debug output? */ static void deb...