2018年5月10日 星期四

Android啟動篇— init原理(二)

原文 https://www.cnblogs.com/pepsimaxin/p/6740413.html

================================================== ====== ============================================ ============
= 【原創文章】:參考部分博客內容,學習之餘進行了大量的篩減細化分析= = 【特殊申明】:避諱抄襲侵權之嫌疑,特此說明,歡迎轉載!=    
================================================= ======= =========================================== =============
【前言】
  Android啟動篇— init原理(一)中講解分init進程分析init創建系統目錄並掛在相應系統文件、初始化屬性域、設置系統屬性、啟動配置屬性服務端等一系列複雜工作,很多工作和知識點跟Linux關係很大,所以沒有作過多介紹,而本此對於init.rc的解析則是重中之重,所以單獨拿出來進行詳細分析。
複製代碼
int main( int argc, char ** argv) {
     /* 01.創建文件系統目錄並掛載相關的文件系統*/
    /* 02. 屏蔽標準的輸入輸出/初始化內核log系統*/
    /* 03. 初始化屬性域*/
    /* 04. 完成SELinux相關工作*/•
    /* 05. 重新設置屬性*/
    /* 06. 創建epoll句柄*/
    /* 07. 裝載子進程信號處理器*/
    /* 08. 設置默認系統屬性*/
    /* 09. 啟動配置屬性的服務端*/
    /* 10.匹配命令和函數之間的對應關係*/ ----------------------------------- -------------------------------------------------- ------ // Android啟動篇— init原理(一)中講解
    /* 11.解析init.rc */ 
    Parser & parser = Parser::GetInstance();        // 構造解析文件用的parser對象
     / / 增加ServiceParser為一個section,對應name為service 
    parser.AddSectionParser( " service " ,std::make_unique ());
     // 增加ActionParser為一個section,對應name為action 
    parser.AddSectionParser( " on " , std::make_unique ());
     //增加ImportParser為一個section,對應name為service 
    parser.AddSectionParser( " import " , std::make_unique ());
    parser.ParseConfig( " /init.rc " );       // 開始實際的解析過程
複製代碼
【正文】
  init.rc是一個配置文件,內部由Android初始化語言編寫(Android Init Language)編寫的腳本,主要包含五種類型語句:Action、Command、Service、Option和Import,在分析代碼的過程中我們會詳細介紹。
  init.rc的配置代碼在:system/core/rootdir/init.rc 中
  init.rc文件是在init進程啟動後執行的啟動腳本,文件中記錄著init進程需執行的操作。
  init.rc文件大致分為兩大部分,一部分是以“on”關鍵字開頭的動作列表(action list):
on early- init       // Action類型語句 
    # Set init and its forked children ' s oom_adj.      // #:註釋符號 
    write /proc/ 1 /oom_score_adj - 1000
    ... ...
    start ueventd
  Action類型語句格式:
on  [&& ]*      // 設置觸發器  
     
          // 動作觸發之後要執行的命令
  另一部分是以“service”關鍵字開頭的服務列表(service list): 如Zygote
service ueventd /sbin/ ueventd
     class core
    critical
    seclabel u:r:ueventd:s0
  Service類型語句格式:
service   [  ]*    // <執行程序路徑><傳遞參數>   
   // option是service的修飾詞,影響什麼時候、如何啟動services   
     
   ...
  借助系統環境變量或Linux命令,動作列表用於創建所需目錄,以及為某些特定文件指定權限,而服務列表用來記錄init進程需要啟動的一些子進程。如上面代碼所示,service關鍵字後的第一個字符串表示服務(子進程)的名稱,第二個字符串表示服務的執行路徑。
  值得一提的是在Android 7.0中對init.rc文件進行了拆分,每個服務一個rc文件。我們要分析的zygote服務的啟動腳本則在init.zygoteXX.rc中定義。
  在init.rc的import段我們看到如下代碼:
import /init.${ro.zygote}.rc      //可以看出init.rc不再直接引入一個固定的文件,而是根據屬性ro.zygote的內容來引入不同的文件
  說明:
  從android5.0開始,android開始支持64位的編譯,zygote本身也就有了32位和64位的區別,所以在這裡用ro.zygote屬性來控制啟動不同版本的zygote進程。
  init.rc位於/system/core/rootdir下。在這個路徑下還包括四個關於zygote的rc文件。分別是Init.zygote32.rc,Init.zygote32_64.rc,Init.zygote64.rc,Init.zygote64_32.rc,由硬件決定調用哪個文件。
  這裡拿32位處理器為例,init.zygote32.rc的代碼如下所示:
複製代碼
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system- server
     class main         # class是一個option,指定zygote服務的類型為main 
    socket zygote stream 660 root system           # socket關鍵字表示一個option,創建一個名為dev/socket/zygote,類型為stream,權限為660的socket 
    onrestart write /sys/android_power/ request_state wake           # onrestart是一個option,說明在zygote重啟時需要執行的command 
    onrestart write /sys /power/ state on
    onrestart restart audioserver
    onrestart restart cameraserver
    onrestart restart media
    onrestart restart netd
    writepid /dev/cpuset/foreground/tasks
複製代碼
  “service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server”
  在Init.zygote32.rc中,定義了一個zygote服務:zygote,由關鍵字service告訴init進程創建一個名為zygote的進程,這個進程要執行的程序是:/system/bin/app_process,給這個進程四個參數:
    · -Xzygote:該參數將作為虛擬機啟動時所需的參數
    · /system/bin:代表虛擬機程序所在目錄
    · --zygote:指明以ZygoteInit.java類中的main函數作為虛擬機執行入口
    · --start-system-server:告訴Zygote進程啟動SystemServer進程
   接下來,我們回到源碼當中,繼續分析main函數:
複製代碼
    /* 11.解析init.rc */ 
    Parser & parser = Parser::GetInstance();        // 構造解析文件用的parser對象
     // 增加ServiceParser為一個section,對應name為service 
    parser.AddSectionParser( " service " , std::make_unique ());
     // 增加ActionParser為一個section,對應name為action 
    parser.AddSectionParser( " on " , std::make_unique ());
     // 增加ImportParser為一個section,對應name為service 
    parser.AddSectionParser( " import " , std::make_unique ());
    parser.ParseConfig( "/init.rc");       //開始實際的解析過程
複製代碼
  說明:
  上面在解析init.rc文件時使用了Parser類(在init目錄下的init_parser.h中定義), 初始化ServiceParser用來解析“service”塊,ActionParser用來解析"on"塊,ImportParser用來解析“import ”塊,“import”是用來引入一個init配置文件,來擴展當前配置的。
  /system/core/init/readme.txt 中對init文件中的所有關鍵字做了介紹,主要包含了Actions, Commands, Services, Options, and Imports等,可自行學習解讀。
  分析init.rc的解析過程:函數定義於system/core/init/ init_parser.cpp中
複製代碼
bool Parser:: ParseConfig ( const std:: string & path) {
     if (is_dir(path.c_str())) {           //判斷傳入參數是否為目錄地址
          return ParseConfigDir(path);      //遞歸目錄,最終還是靠ParseConfigFile來解析實際的文件 
    }
    return ParseConfigFile(path);         //傳入傳輸為文件地址 
} 
複製代碼
  繼續分析ParseConfigFile():
複製代碼
bool Parser:: ParseConfigFile ( const std:: string & path) {
    ... ...
    Timer t;
    std:: string data;
     if (!read_file(path.c_str(), &data)) {        // 讀取路徑指定文件中的內容,保存為字符串形式
        return  false ;
}
... ...
    ParseData(path, data);         // 解析獲取的字符串
    ... ...
}
複製代碼
  跟踪ParseData():
複製代碼
void Parser:: ParseData ( const std:: string & filename, const std:: string & data) {
    ... ...
    parse_state state;
    ... ...
    std::vector string > args;

    for (;;) {
         switch (next_token(&state)) {     // next_token以行為單位分割參數傳遞過來的字符串,最先走到T_TEXT分支
        case T_EOF:
             if (section_parser) {
                section_parser ->EndSection();     // 解析結束
            }
             return ;
         case T_NEWLINE:
            state.line ++ ;
             if (args.empty()) {
                 break ;
            }
            // 在前文創建parser時,我們為service,on,import定義了對應的parser 
             // 這裡就是根據第一個參數,判斷是否有對應的parser 
            if (section_parsers_.count(args[ 0 ])) {
                 if (section_parser) {
                     // 結束上一個parser的工作,將構造出的對象加入到對應的service_list與action_list中 
                    section_parser-> EndSection();
                }
                // 獲取參數對應的parser 
                section_parser = section_parsers_[args[ 0 ]]. get ();
                std:: string ret_err;
                 // 調用實際parser的ParseSection函數
                if (!section_parser->ParseSection(args, & ret_err)) {
                    parse_error( &state, " %s\n " , ret_err.c_str());
                    section_parser = nullptr;
                }
            } else  if (section_parser) {
                std:: string ret_err;
                 // 如果第一個參數不是service,on,import
                 // 則調用前一個parser的ParseLineSection函數
                 // 這里相當於解析一個參數塊的子項
                if (!section_parser-> ParseLineSection(args , state.filename,
                                                             state.line, & ret_err)) {
                    parse_error( &state, " %s\n " , ret_err.c_str());
                }
            }
            args.clear();        // 清空本次解析的數據
            break ;
         case T_TEXT:
            args.emplace_back(state.text);      // 將本次解析的內容寫入到args中
            break ;
        }
    }
}
複製代碼
  至此,init.rc解析完,接下來init會執行幾個重要的階段:
複製代碼
int main( int argc, char ** argv) {
    /* 01. 創建文件系統目錄並掛載相關的文件系統*/
    /* 02. 屏蔽標準的輸入輸出/初始化內核log系統*/
    /* 03. 初始化屬性域*/
    /* 04. 完成SELinux相關工作*/•
    /* 05. 重新設置屬性*/
    /* 06. 創建epoll句柄*/
    /* 07. 裝載子進程信號處理器*/
    /* 08. 設置默認系統屬性*/
    /* 09. 啟動配置屬性的服務端*/
    /* 10.匹配命令和函數之間的對應關係*/ 
    /* 11.解析init.rc*/ 
------------------------- -------------------------------------------------- - 
  /* 12.向執行隊列中添加其他action */
    // 獲取ActionManager對象,需要通過am對命令執行順序進行控制 
    ActionManager& am = ActionManager::GetInstance();
     // init執行命令觸發器主要分為early-init,init,late-init,boot等 
    am.QueueEventTrigger ( " early-init " );     // 添加觸發器early-init,執行on early-init內容

    // Queue an action that waits for coldboot done so we know ueventd has set up all of /dev... 
    am.QueueBuiltinAction(wait_for_coldboot_done_action, " wait_for_coldboot_done " );
     // ... so that we can start queuing up actions that require stuff from /dev. 
    am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, " mix_hwrng_into_linux_rng " );
    am.QueueBuiltinAction(keychord_init_action, " keychord_init " );
    am.QueueBuiltinAction(console_init_action, " console_init " );

    // Trigger all the boot actions to get us started. 
    am.QueueEventTrigger( " init " );         // 添加觸發器init,執行on init內容,主要包括創建/掛在一些目錄,以及symlink等

    // Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
     // wasn't ready immediately after wait_for_coldboot_done 
    am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, " mix_hwrng_into_linux_rng " );

    // Don't mount filesystems or start core system services in charger mode. 
    if (bootmode == " charger " ) {
    am.QueueEventTrigger( " charger " );     // on charger階段 
    } else  if (strncmp(bootmode.c_str(), " ffbm " , 4 ) == 0 ) {
    NOTICE( " Booting into ffbm mode\n " );
    am.QueueEventTrigger( " ffbm " );
    } else {
    am.QueueEventTrigger( " late-init " );           // 非充電模式添加觸發器last-init 
    }

    // Run all property triggers based on current state of the properties. 
    am.QueueBuiltinAction(queue_property_triggers_action, " queue_property_triggers " );
複製代碼
  在last-init最後階段有如下代碼:
複製代碼
# Mount filesystems and start core system services.
 on late - init 
    trigger early - fs

    # Mount fstab in init.{$device}.rc by mount_all command. Optional parameter
    # ' --early ' can be specified to skip entries with ' latemount ' .
    # /system and / vendor must be mounted by the end of the fs stage,
    # while /data is optional.
    trigger fs
    trigger post - fs

    # Load properties from /system/ + / factory after fs mount. Place
    # this  in another action so that the load will be scheduled after the prior
    # issued fs triggers have completed.
    trigger load_system_props_action

    # Mount fstab in init.{$device}.rc by mount_all with ' --late ' parameter
    # to only mount entries with ' latemount ' . This is needed if  ' --early '  is 
    # specified in the previous mount_all command on the fs stage.
    # With /system mounted and properties form /system + / factory available,
    # some services can be started.
    trigger late - fs

    # Now we can mount / data. File encryption requires keymaster to decrypt
    # /data, which in turn can only be loaded when system properties are present.
    trigger post -fs- data

    # Load persist properties and override properties ( if enabled) from / data.
    trigger load_persist_props_action

    # Remove a file to wake up anything waiting for firmware.
    trigger firmware_mounts_complete

    trigger early - boot
    trigger boot
複製代碼
  可見出發了on early-boot和on boot兩個Action。
  我們看一下on boot:
複製代碼
on boot
    # basic network init
    ifup lo
    hostname localhost
    domainname localdomain
    ... ... class_start core
    
複製代碼
  在on boot 的最後class_start core 會啟動class為core的服務,這些服務包括ueventd、logd、healthd、adbd(disabled)、lmkd(LowMemoryKiller)、servicemanager、vold、debuggerd、surfaceflinger、bootanim(disabled)等。
  回到主題,分析trigger觸發器的代碼,QueueEventTrigger():位於system/core/init/action.cpp
void ActionManager:: QueueEventTrigger ( const std:: string & trigger) {
    trigger_queue_.push(std::make_unique  (trigger));
}
  此處QueueEventTrigger函數就是利用參數構造EventTrigger,然後加入到trigger_queue_中。後續init進程處理trigger事件時,將會觸發相應的操作。
  再看一下QueueBuiltinAction()函數:同樣位於system/core/init/action.cpp
複製代碼
void ActionManager::QueueBuiltinAction(BuiltinFunction func,
                                    const std:: string & name) {
     // 創建action 
    auto action = std::make_unique( true );
    std::vector string > name_vector{name};

    // 保證唯一性
    if (!action-> InitSingleTrigger(name)) {
         return ;
    }

    // 創建action的cmd,指定執行函數和參數 
    action-> AddCommand(func, name_vector);

    trigger_queue_.push(std::make_unique (action. get ()));
    actions_.emplace_back(std::move(action));
}
複製代碼
  QueueBuiltinAction函數中構造新的action加入到actions_中,第一個參數作為新建action攜帶cmd的執行函數;第二個參數既作為action的trigger name,也作為action攜帶cmd的參數。
  接下來繼續分析main函數:
複製代碼
int main( int argc, char ** argv) {
    /* 01. 創建文件系統目錄並掛載相關的文件系統*/
    /* 02. 屏蔽標準的輸入輸出/初始化內核log系統*/
    /* 03. 初始化屬性域*/
    /* 04. 完成SELinux相關工作*/•
    /* 05. 重新設置屬性*/
    /* 06. 創建epoll句柄*/
    /* 07. 裝載子進程信號處理器*/
    /* 08. 設置默認系統屬性*/
    /* 09. 啟動配置屬性的服務端*/
    /* 10.匹配命令和函數之間的對應關係*/ 
    /* 11.解析init.rc*/ 
    /* 12.向執行隊列中添加其他action */ 
------------ -------------------------------------------------- ----- 
    /* 13.處理添加到運行隊列的事件*/     while ( true ) {
    // 判斷是否有事件需要處理
        if (! waiting_for_exec) {
             // 依次執行每個action中攜帶command對應的執行函數
am. ExecuteOneCommand ();
         // 重啟一些掛掉的進程            restart_processes();     

        }

        // 以下決定timeout的時間,將影響while循環的間隔
        int timeout = - 1 ;
         // 有進程需要重啟時,等待該進程重啟
        if (process_needs_restart) {
            timeout = (process_needs_restart - gettime()) * 1000 ;
             if (timeout < 0 )
                timeout = 0 ;
        }

        // 有action待處理,不等待
        if (am.HasMoreCommands()) {
            timeout = 0 ;
        }

        // bootchart_sample應該是進行性能數據採樣 
        bootchart_sample(& timeout);

        epoll_event ev;
        // 沒有事件到來的話,最多阻塞timeout時間
        int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1 , timeout));
         if (nr == - 1 ) {
            ERROR( " epoll_wait failed: %s\n " , strerror(errno));
        } else  if (nr == 1 ) {
             // 有事件到來,執行對應處理函數
             // 根據上文知道,epoll句柄(即epoll_fd)主要監聽子進程結束,及其它進程設置系統屬性的請求 
            (( void (* )()) ev.data.ptr)();
        }
    }
    return  0 ;
} // end main
複製代碼
  看一下ExecuteOneComand()函數:同樣位於system/core/init/action.cpp
複製代碼
void ActionManager:: ExecuteOneCommand () {
     // Loop through the trigger queue until we have an action to execute
     // 當前的可執行action隊列為空, trigger_queue_隊列不為空
    while (current_executing_actions_.empty() && ! trigger_queue_. empty()) {
     // 循環遍歷action_隊列,包含了所有需要執行的命令,解析init.rc獲得
        for ( const auto& action : actions_) {
             // 獲取隊頭的trigger,檢查actions_列表中的action的trigger,對比是否相同
            if (trigger_queue_.front()->CheckTriggers(* action)) {
                 // 將所有具有同一trigger的action加入當前可執行action隊列
                current_executing_actions_.emplace(action. get ());
            }
        }
        // 將隊頭trigger出棧
        trigger_queue_.pop();
    }

    if (current_executing_actions_.empty()) {    // 當前可執行的actions隊列為空就返回
        return ;
    }

    auto action = current_executing_actions_.front(); // 獲取當前可執行actions隊列的首個action

    if (current_command_ == 0 ) {
        std:: string trigger_name = action-> BuildTriggersString();
        INFO( " processing action (%s)\n " , trigger_name.c_str());
    }

    action ->ExecuteOneCommand(current_command_);      // 執行當前的命令

    // If this was the last command in the current action, then remove
     // the action from the executing list.
     // If this action was oneshot, then also remove it from actions_. 
    ++current_command_;       // 不斷疊加,將action _中的所有命令取出
    if (current_command_ == action-> NumCommands()) {
        current_executing_actions_.pop();
        current_command_ = 0 ;
         if (action-> oneshot()) {
            auto eraser = [&action] (std::unique_ptr& a) {
                 return a. get () == action;
            };
            actions_.erase(std::remove_if(actions_.begin(), actions_.end(), eraser));
        }
    }
}
複製代碼
  我們來觀察一下init.rc的開頭部分:
import / init.environ.rc
import / init.usb.rc
import / init.${ro.hardware}.rc
import / init.usb.configfs.rc
 import /init.${ro.zygote}.rc       //後面我們即將重點分析zygote進程
  通過ro.zygote的屬性import對應的zygote的rc文件。
  
  我們查看init.zygote64_32.rc:
複製代碼
service  zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server --socket-name= zygote
     class main 
    socket zygote stream 660 root system
    onrestart write /sys/android_power/ request_state wake
    onrestart write /sys/power/ state on
    onrestart restart audioserver
    onrestart restart cameraserver
    onrestart restart media
    onrestart restart netd
    writepid /dev/cpuset/foreground/ tasks

service  zygote_secondary/system/bin/app_process32 -Xzygote /system/bin --zygote --socket-name=zygote_secondary
    class main 
    socket zygote_secondary stream660root system
    onrestart restart zygote
    writepid /dev/cpuset/foreground/tasks
複製代碼
  可以看到zygote的class是main,它是在on nonencrypted時被啟動的,如下:
複製代碼
on boot
    # basic network init
    ifup lo
    hostname localhost
    domainname localdomain
    ... ...
    class_start core

on nonencrypted 
    # A / B update verifier that marks a successful boot.
    exec - root cache -- /system/bin/ update_verifier nonencrypted
    class_start main 
    class_start late_start
複製代碼
  至此,Init.cpp的main函數分析完畢!init進程已經啟動完成,一些重要的服務如core服務和main服務也都啟動起來,並啟動了zygote(/system/bin/app_process64)進程,zygote初始化時會創建虛擬機,啟動systemserver等。

開放原碼架構設計:D-BUS 觀念小談

原文 http://www.jollen.org/blog/2007/05/talking_d-bus.html
jollen 發表於 May 24, 2007 11:04 AM
近期以來,因為工作上的需求,花費許多時間在開放原碼的架構設計上;最近已經將相關的作業都準備的差不多了,我想,可以跟大家分享一些架構設計的小觀念。
首先,先由 [D-BUS] 介紹起。使用 D-Bus 來取代傳統的 IPC,並與外部工具(eg. mjpegtools)做整合。
D-BUS 本身屬於 low-level 的 library(libdbus),實務上,我採用了 glib binding 來實作程式,以簡化工作:
D-BUS 是一種 'message bus',未來將取代傳統 IPC 的使用。傳統 IPC 實作,必須將程式架構成 monolithic process,因此在 process 的行為控制,以及 application 間的整合上,都很一定的難度,非常不易於實作「系統」。
引進 D-BUS 技術後,傳統的 monolithic process 被區分為「pieces of D-BUS services」,每個服務,都可以透過「D-BUS patch」來尋找並叫用(invoke);透過 D-BUS 所建立的框架(framework)或是架構,解決了以往難以整合各種應用程式的困境。此外,D-BUS 目前也透過 kobject 與 kernel 做整合,如此一來,D-BUS 便能輕易整合 kernel、application 與 desktop,真正解決以往「系統整合」所遇到的障礙。
例如,如何實作「按鍵觸發應用程式」的系統?
傳統的做法,會讓程式設計師進入「不斷使用 low-level C API 來 try-and-error」的可怕輪迴,但是使用 D-BUS 的架構後,我們只要修改 device driver,將按鍵的動作透過 kobject event layer 傳送(經由 netlink socket)給 D-BUS 即可,D-BUS 會根據我們所定義的路徑,來叫用 application。
D-BUS 所提供的 message bus 分為二種:
* system bus
* local bus
System bus 最大的用途便是整合 kernel 與 application;local bus 為 session bus,即訊息的傳遞只限於該 session。

Understanding the Device Tree

Source https://forum.xda-developers.com/showthread.php?t=2620389

Note - This is by no means complete, and there will be omissions as have explained all this top of my head and copied pasted certain bits that I have here on my own device tree.

Android.mk - this will tell the build system to include and to build sources specifically for your device. I have explained a bit of it above as well. 

AndroidBoard.mk - this is for the kernel, the build system uses that to drop the kernel image in place.

AndroidProducts.mk - specifies the appropriate device's make file, to use for building. i.e. device/htc/pico/pico.mk(In my case), This is device-specific as well.

device_codename.mk - Every device has a codename, and there is a file named as the codename of device. It specifies the properties and extras to copy over into the final output, in this case, it could be for example, pico.mk

BoardConfig.mk - This is the meat of it all, this is where compiler conditional flags are set, partition layouts, boot addresses, ramdisk size, and so on.

Android.mk
Code:
LOCAL_PATH := $(my-dir)

ifeq ($(TARGET_DEVICE),pico)
include $(call all-makefiles-under,$(LOCAL_PATH))
endif
^This is how the build will use that to build recovery, sensors, lights and camera (of course there will be more), its saying 'Yo Builder, go into each of the directories specified, and build the respective sources' 

AndroidBoard.mk:
Code:
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

ALL_PREBUILT += $(INSTALLED_KERNEL_TARGET)

# include the non-open-source counterpart to this file
-include vendor/htc/pico/AndroidBoardVendor.mk
^ This one is pretty simple and tells the build system to go to the vendor tree of the device and include AndroidBoardVendor.mk for building.

AndroidProducts.mk
Code:
PRODUCT_MAKEFILES := \
    $(LOCAL_DIR)/cm.mk \
    $(LOCAL_DIR)/pico.mk
^Just specifying the makefiles of the device. 

BoardConfig.mk
Now in this one, I'm taking a BoardConfig.mk file that I found over the internet with a suitable explanation of it. Credits to the post over stackoverflow for the valuable efforts.
Code:
LOCAL_PATH:= $(call my-dir)

TARGET_NO_BOOTLOADER := true
TARGET_PREBUILT_KERNEL := device/lg/gt540/kernel
TARGET_PREBUILT_RECOVERY_KERNEL := device/lg/gt540/recovery_kernel

# This will vary from device!
TARGET_BOARD_PLATFORM := msm7k
TARGET_ARCH_VARIANT := armv6-vfp
TARGET_CPU_ABI := armeabi
TARGET_CPU_ABI := armeabi-v6l
TARGET_CPU_ABI2 := armeabi

# OpenGL drivers config file path
BOARD_EGL_CFG := device/lg/gt540/egl.cfg

# Dependant, not to be taken literally!
BOARD_GLOBAL_CFLAGS += -DHAVE_FM_RADIO

# Dependant, not to be taken literally!
BOARD_KERNEL_BASE := 0x02600000

# this will be device specific, and by doing cat /proc/mtd will give you the correct sizes
BOARD_BOOTIMAGE_PARTITION_SIZE     := 0x00480000
BOARD_RECOVERYIMAGE_PARTITION_SIZE := 0x00480000
BOARD_SYSTEMIMAGE_PARTITION_SIZE   := 0x0cf80000
BOARD_USERDATAIMAGE_PARTITION_SIZE := 0x0d020000
BOARD_FLASH_BLOCK_SIZE := 131072
^That is an excerpt, notice how we specify kernel's base address, this is how the boot.img gets generated after compilation is done and yet again, gets dropped into out/target/product/lg/gt540/boot.img. Also, more importantly, we're telling the build system to use the target platform for cross-compiling the sources (*TARGET_BOARD_PLATFORM*/*TARGET_CPU_ABI*) There will be more information in there such as conditional flags to pass to the compiler, for an example. we specified the directive HAVE_FM_RADIO to tell it, when it comes to handling the source for the FM radio system, to conditionally compile parts of the source. Again, this is hardware specific and mileage will vary, also this applies to the address for boot. In a nutshell, this is saying 'Yo Builder, read the damn variables and remember them and apply them when cross-compiling those source files!'

Now that the internals of each of those Android build make-files are shown.

Now, onto the vendor/ part of it, in AOSP, simply, once again, correlation and corresponds with the device/ tree, as in continuing with this example, vendor/lg/gt540/ which gets picked up by the lunch. There's more make files in there but the general consensus is there's a directory called proprietary which contains the proprietary libs (due to close-source etc) that gets copied over. The copying over of the libraries gets specified in the file device-vendor-blobs.mk, in this case, gt540-vendor-blobs.mk.

Compiling specific parts of a ROM

Source https://forum.xda-developers.com/showthread.php?t=2620389

To compile an app-
Code:
make app_name.apk -j4
Wait, there's some more things you must know for compiling a *specific app* ! Thanks to @thewisenerd head over to this post. 

To compile other parts you also need to enter the folder name, here are some examples from /frameworks/base [Credits-XDA University Article]

Code:
make android.policy        (the power menu and lockscreen)
make framework             (the initial framework files)
make framework-res         (the initial framework resources)
make services              (the services.jar file)
More examples:

Code:
make sdk                   (builds the android sdk)
make modules               (builds all modules)
make installclean          (removes all staging directories, such as out/target/product/boardname/system)
make clean                 (removes the whole /out directory)
make recoveryimage         (builds the recovery from /bootable/recovery, customizable if wanted!)
Configuring a new Product for the AOSP.
Again, I'd recommend you to have a look at this great document.

A detail information about BoardConfig.mk
Well, I believe that BoardConfig.mk is one of the most important files for the device tree. Now, here are few parameters of the file explained-
  • TARGET_ARCH: set to arm for almost all current Android devices.
  • BOARD_KERNEL_CMDLINE: not all devices pass boot parameters however if your device does this must be filled out properly in order to bootsuccessfully.
  • BOARD_KERNEL_PAGESIZE: the pagesize of the stock boot.img and must be set properly in order to boot. Typical values for this are 2048 and 4096 and this information can be extracted from the stock kernel.
  • BOARD_BOOTIMAGE_PARTITION_SIZE: the number of bytes allocated to the kernel image partition.
  • BOARD_RECOVERYIMAGE_PARTITION_SIZE: the number of bytes allocated to the recovery image partition.
  • BOARD_SYSTEMIMAGE_PARTITION_SIZE: the number of bytes allocated to the Android system filesystem partition.
  • BOARD_USERDATAIMAGE_PARTITION_SIZE: the number of bytes allocated to the Android data filesystem partition.
    ^The above information can be gathered by multiplying the size from /proc/partitions by the block size, typically 1024.
  • BOARD_HAS_NO_SELECT_BUTTON: (optional), use this if your device needs to use its Power button to confirm selections in recovery.
  • BOARD_FORCE_RAMDISK_ADDRESS / BOARD_MKBOOTIMG_ARGS: (optional), use these to force a specific address for the ramdisk. This is usually needed on larger partitions in order for the ramdisk to be loaded properly where it's expected to exist. This value can be obtained from the stock kernel. The former is deprecated as of Android 4.2.x and the latter will now be used in 4.2.x and beyond.

Describing the Android Source Code Folders

Source https://forum.xda-developers.com/showthread.php?t=2620389

1. abi - This folder contains a sub folder called cpp which actually contains many C++ files linked to many places.

2. android Remember this?
Code:
repo init -u git://github.com/CyanogenMod/android.git
Yes, it's that android.git folder.
Have a look at this also. 

3. art - Yeah, it is the folder that deals with the compilation of the latest android ART runtime. If you're looking into source directories of some other androidversions, you won't fins it obviously. 

4. bionic - Bionic is mainly a port of the BSD C library to our Linux kernel with the following additions/changes:
- No support for locales.
- No support for wide chars (i.e. multi-byte characters).
- its own smallish implementation of pthreads based on Linux futexes.
- Support for x86, ARM and ARM thumb CPU instruction sets and kernel interfaces.

5. bootable - Boot and startup related code. Some of it is legacy, the fastboot protocol info could be interesting since it is implemented by boot loaders in a number of devices such as the Nexus ones.

6. build - The main entry point of the build system resides here - envsetup.sh, if you follow the instruction in source.android.com you will see that the first step before you do anything to build Android is to use the command source build/envsetup.sh
The script will check few things to make sure all the needed application available in the local machine. It also setup the devices that can be built, which is extracted from the directory device.

7. cts - the compatability tests. The test suite to ensure that a build complies with the Android specification.

8. dalvik - This is the folder responsible for the compilation of the Dalvik runtime for the Android devices.
*Have a look at the difference between the two(art and dalvik) folders and you'll have an idea of how things work in that case*

9. Development - This directory contains application that are not part of the deployed app in the OS. There are some useful application such as widgetbuilder, etc

10. Device - It contains the device specific configurations for many devices.
Note - Many people ask me what, the folders like 'common' and 'generic are for, so here - 

> common - This directory contains gps information and also a script that allows you to extract proprietary binary files from your phone to be part of the build process.(You can try to have a look at your device's device tree and then the cm.mk file, where you could find relations of these files. In my case, it shows like this:
Code:
# Include GSM stuff
$(call inherit-product, vendor/cm/config/gsm.mk)
-and-
# Inherit some common cyanogenmod stuff.
$(call inherit-product, device/common/gps/gps_eu_supl.mk)
> generic - This directory contains the generic device configuration that is called ‘goldfish’. This is the device classification used when building the emulator.

> Google - This directory contains the Android Accessories Kit code. It contains a demokit Android app that allows you to control the ADK board. The ADK firmware can be check out here http://code.google.com/p/microbridge/. There is a good article about this here.


> sample - This directory contains a full example of writing your own Android platform shared library, without changing the Android framework. It also shows how to write JNI code for incorporating native code into the library, and a client application that uses the library. This example is ONLY for people working with the open source platform to create a system image that will be delivered on a device which will include a custom library as shown here. It can not be used to create a third party shared library, which is not currently supported in Android.

11. docs - I contains an important sub-folder called source.android.com. Contains tutorials, references, and miscellaneous information relating to the AndroidOpen Source Project (AOSP). The current iteration of this site is fully static HTML (notably lacking in javascript and doxygen content), and is and/or was maintained by skyler (illustrious intern under Dan Morrill and assistant to the almighty JBQ).

12. external - This directory contains source code for all external open source projects such as SQLite, Freetype, webkit and webview.

13. frameworks - Ah, one of the most important directories. it contains the sources for the framework. Here you will find the implementation of key services such as the System Server with the Package- and Activity managers. A lot of the mapping between the java application APIs and the native libraries is also done here.
A special note on this one - I'd recommend new users to not to play with any file/folder inside the frameworks folder, maybe your ROM doesn't boots then. 

14. hardware - Hardware related source code such as the Android hardware abstraction layer specification and implementation. This folder also contains the reference Radio Interface Layer(RIL - To communicate with the modem side) implementation.

15. Kernel - It's not a default folder in the source code, but it's a part of device configuration set-up. It contains the kernel source of your device.

16. libcore - I'll explain this one with the important folders inside this, since every folder performs a different function.
  • dalvik - DalvikVM runtime for Android
  • dom - Java test classes for DOM
  • expectations - Contains information about the test cases
  • include - Some C/C++ include files that used for Array and String handling
  • json - JSON based Java implementation
  • luni - Contains test source code for loading .jar and .dex files
  • support - Contains support class file for testing Dalvik
  • xml - XML pull and push implementation
17. libnativehelper - I have no idea on this one. If someone knows, share your knowledge.  

18. ndk - Contains build scripts and helper files for building the NDK

19. out(Everyone's favorite directory ) - The build output will be placed here after you run make. The folder structure is out/target/product/. In the default build for the emulator the output will be placed in out/target/product/generic. This is where you will find the images used by the emulator to start (or to be downloaded and flashed to a device if you are building for a hardware target).

20. packages - Standard Android application that are available as part of the AOSP - Camera, SMS, Dialer, Launcher, etc

21. pdk - I believe that 'pdk' is the Platform Development Kit, it's basically an SDK/set of tools that Google sends to OEMs to evaluate their framework ahead of each major Android upgrade since Android 4.1.
(Thanks to @yowanvista  )

22. prebuilt - Contains files that are distributed in binary form for convenience. Examples include the cross compilations toolchains for different development machines.

23. sdk - This directory contains lots of apps that are not part of operating system. There are quite useful apps that developers can leverage on and can be enhanced further as part of the operating system. 

24. system - Source code files for the core Android system. That is the minimal Linux system that is started before the Dalvik VM and any java based services are enabled. This includes the source code for the init process and the default init.rc script that provide the dynamic configuration of the platform.

25. tools - Some external important tools that help in compiling. Not sure though. :/

26. vendor - This directory contains vendors specific libraries. Most of the proprietary binary libraries from non-open source projects are stored here when building AOSP.

One thing - Beyond the above you also have the hidden .repo directory that contains the source for the repo utility. It also holds the manifest specifying what git repositories you want to track for this Android source project. If you have your own additions you could automatically track them by adding a local manifest here. For modifications of the platform framework there are some instructions available in the device/sample folder of the source code tree. That will show you how to add APIs to Android without having to modify the core framework.