1. 程式人生 > >Android7.0解析Init.rc檔案

Android7.0解析Init.rc檔案

    在解析Init.rc之前需要對init.rc檔案有一個認識,init.rc是由一種被稱為"Android初始化語言"(Android Init Language,這裡簡稱為AIL)的指令碼寫成的檔案.該語言是由語句組成的,主要包含了五種型別的語句:

  1. Action
  2. Commands
  3. Services
  4. Options
  5. Import

 在init.rc檔案中一條語句通常佔用一行,單詞之間是用空格符來相隔的。如果一行寫不下,可以在行尾加上反斜槓,來連線下一行。也就是說,可以用反斜槓將多行程式碼連線成一行程式碼。並且使用#來進行註釋。在init.rc中分成三個部分(Section),而每一部分的開頭需要指定on(Actions)、service(Services)或import。也就是說,每一個Actions, import或 Services確定一個Section。而所有的Commands和Options只能屬於最近定義的Section。如果Commands和 Options在第一個Section之前被定義,它們將被忽略。Actions和Services的名稱必須唯一。如果有兩個或多個Actions或Services擁有同樣的名稱,那麼init在執行它們時將丟擲錯誤,並忽略這些Action和Service。

我們在之前分析init程序時講到過在init.cpp的main函式中將service, on, import設定為三個Section.

    Parser& parser = Parser::GetInstance();
    parser.AddSectionParser("service",std::make_unique<ServiceParser>());
    parser.AddSectionParser("on", std::make_unique<ActionParser>());
    parser.AddSectionParser("import", std::make_unique<ImportParser>());
下面來看一下Action,Service,Import都是該怎麼定義的.

在system/core/init/readme.txt中詳細說明

Action的格式如下:

on <trigger> [&& <trigger>]*     //設定觸發器
   <command>
   <command>                  //動作觸發之後要執行的命令
   <command>
Triggers                    //對trigger的詳細講解
--------
Triggers are strings which can be used to match certain kinds of
events and used to cause an action to occur.

Triggers are subdivided into event triggers and property triggers.

Event triggers are strings triggered by the 'trigger' command or by
the QueueEventTrigger() function within the init executable.  These
take the form of a simple string such as 'boot' or 'late-init'.

Property triggers are strings triggered when a named property changes
value to a given new value or when a named property changes value to
any new value.  These take the form of 'property:<name>=<value>' and
'property:<name>=*' respectively.  Property triggers are additionally
evaluated and triggered accordingly during the initial boot phase of
init.

An Action can have multiple property triggers but may only have one
event trigger.

For example:
'on boot && property:a=b' defines an action that is only executed when
the 'boot' event trigger happens and the property a equals b.

'on property:a=b && property:c=d' defines an action that is executed
at three times,
   1) During initial boot if property a=b and property c=d
   2) Any time that property a transitions to value b, while property
      c already equals d.
   3) Any time that property c transitions to value d, while property
      a already equals b.

在init.cpp中設定的trigger有early-init, init, late-init等, 當trigger被觸發時就執行command,

我們來看一個標準的Action:

on early-init     //trigger為early-init,在init.cpp的main函式中設定過
    # Set init and its forked children's oom_adj.
    write /proc/1/oom_score_adj -1000   //呼叫do_write函式, 寫入oom_score_adj為-1000

    # Disable sysrq from keyboard
    write /proc/sys/kernel/sysrq 0

    # Set the security context of /adb_keys if present.
    restorecon /adb_keys       //為adb_keys 重置安全上下文

    # Shouldn't be necessary, but sdcard won't start without it. http://b/22568628.
    mkdir /mnt 0775 root system    //建立mnt目錄

    # Set the security context of /postinstall if present.
    restorecon /postinstall

    start ueventd       //呼叫函式do_start, 啟動服務uevent,

下面對所有命令詳細講解.

bootchart_init    //初始化bootchart,用於獲取開機過程系統資訊
   Start bootcharting if configured (see below).
   This is included in the default init.rc.

chmod <octal-mode> <path>  //改變檔案的許可權
   Change file access permissions.

chown <owner> <group> <path>  //改變檔案的群組
   Change file owner and group.

class_start <serviceclass>   //啟動所有具有特定class的services
   Start all services of the specified class if they are
   not already running.

class_stop <serviceclass>      //將具有特定class的所有執行中的services給停止或者diasble
   Stop and disable all services of the specified class if they are
   currently running.

class_reset <serviceclass>   //先將services stop掉, 之後可能會通過class_start再重新啟動起來
   Stop all services of the specified class if they are
   currently running, without disabling them. They can be restarted
   later using class_start.

copy <src> <dst>    //複製檔案
   Copies a file. Similar to write, but useful for binary/large
   amounts of data.

domainname <name>    
   Set the domain name.

enable <servicename>  //如果services沒有特定disable,就將他設為enable
   Turns a disabled service into an enabled one as if the service did not
   specify disabled.        
   If the service is supposed to be running, it will be started now.
   Typically used when the bootloader sets a variable that indicates a specific
   service should be started when needed. E.g.
     on property:ro.boot.myfancyhardware=1
        enable my_fancy_service_for_my_fancy_hardware

exec [ <seclabel> [ <user> [ <group> ]* ] ] -- <command> [ <argument> ]*    //建立執行程式.比較重要,後面啟動service要用到
   Fork and execute command with the given arguments. The command starts
   after "--" so that an optional security context, user, and supplementary
   groups can be provided. No other commands will be run until this one
export <name> <value>      //在全域性設定環境變數
   Set the environment variable <name> equal to <value> in the
   global environment (which will be inherited by all processes
   started after this command is executed)

hostname <name>       //設定主機名稱
   Set the host name.

ifup <interface>       //啟動網路介面
   Bring the network interface <interface> online.

insmod <path>    //在某個路徑安裝一個模組
   Install the module at <path>

load_all_props    //載入所有的配置
   Loads properties from /system, /vendor, et cetera.
   This is included in the default init.rc.

load_persist_props  //當data加密時載入一些配置
   Loads persistent properties when /data has been decrypted.
   This is included in the default init.rc.

loglevel <level>       //設定kernel log level
   Sets the kernel log level to level. Properties are expanded within <level>.

mkdir <path> [mode] [owner] [group]    //建立資料夾
   Create a directory at <path>, optionally with the given mode, owner, and
   group. If not provided, the directory is created with permissions 755 and
   owned by the root user and root group. If provided, the mode, owner and group
   will be updated if the directory exists already.

mount_all <fstab> [ <path> ]*      //掛載
   Calls fs_mgr_mount_all on the given fs_mgr-format fstab and imports .rc files
   at the specified paths (e.g., on the partitions just mounted). Refer to the
   section of "Init .rc Files" for detail.

mount <type> <device> <dir> [ <flag> ]* [<options>]   //在dir資料夾下面掛載裝置
   Attempt to mount the named device at the directory <dir>
   <device> may be of the form [email protected] to specify a mtd block
   device by name.
   <flag>s include "ro", "rw", "remount", "noatime", ...
   <options> include "barrier=1", "noauto_da_alloc", "discard", ... as
   a comma separated string, eg: barrier=1,noauto_da_alloc

powerctl
   Internal implementation detail used to respond to changes to the
   "sys.powerctl" system property, used to implement rebooting.
restart <service>          //重啟服務
   Like stop, but doesn't disable the service.

restorecon <path> [ <path> ]*      //重置檔案的安全上下文
   Restore the file named by <path> to the security context specified
   in the file_contexts configuration.
   Not required for directories created by the init.rc as these are
   automatically labeled correctly by init.

restorecon_recursive <path> [ <path> ]*//一般都是 selinux完成初始化之後又建立、或者改變的目錄
   Recursively restore the directory tree named by <path> to the
   security contexts specified in the file_contexts configuration.

rm <path>         //刪除檔案
   Calls unlink(2) on the given path. You might want to
   use "exec -- rm ..." instead (provided the system partition is
   already mounted).

rmdir <path>     //刪除資料夾
   Calls rmdir(2) on the given path.

setprop <name> <value>    //設定屬性
   Set system property <name> to <value>. Properties are expanded
   within <value>.

setrlimit <resource> <cur> <max>
   Set the rlimit for a resource.

start <service>     //如果service沒有啟動,就將他啟動起來
   Start a service running if it is not already running.

stop <service> //將執行的服務停掉
   Stop a service from running if it is currently running.

swapon_all <fstab>
   Calls fs_mgr_swapon_all on the given fstab file.

symlink <target> <path>
   Create a symbolic link at <path> with the value <target>

sysclktz <mins_west_of_gmt>
   Set the system clock base (0 if system clock ticks in GMT)
trigger <event>      //觸發一個事件
   Trigger an event.  Used to queue an action from another
   action.

verity_load_state
   Internal implementation detail used to load dm-verity state.

verity_update_state <mount_point>
   Internal implementation detail used to update dm-verity state and
   set the partition.<mount_point>.verified properties used by adb remount
   because fs_mgr can't set them directly itself.

wait <path> [ <timeout> ]     //等待
   Poll for the existence of the given file and return when found,
   or the timeout has been reached. If timeout is not specified it
   currently defaults to five seconds.

write <path> <content>   //寫檔案
   Open the file at <path> and write a string to it with write(2).
   If the file does not exist, it will be created. If it does exist,
   it will be truncated. Properties are expanded within <content>.

這些命令在執行時會通過在BuiltinFunctionMap中的對應關係找到自己對應的函式.

Services格式如下:

service <name> <pathname> [ <argument> ]*   //service的名字,啟動路徑,以及引數
   <option>       //option影響什麼時候,如何啟動services
   <option>
   ...
Options        //所有的options命令如下
-------
Options are modifiers to services.  They affect how and when init
runs the service.

critical     //表示該service非常重要,如果退出四次以上在4分鐘內,裝置就會重啟進入recovery模式
  This is a device-critical service. If it exits more than four times in
  four minutes, the device will reboot into recovery mode.

disabled    //該services不能通過class啟動,只能通過name將他啟動
  This service will not automatically start with its class.
  It must be explicitly started by name.

setenv <name> <value>   //設定環境變數
  Set the environment variable <name> to <value> in the launched process.

socket <name> <type> <perm> [ <user> [ <group> [ <seclabel> ] ] ]
  Create a unix domain socket named /dev/socket/<name> and pass
  its fd to the launched process.  <type> must be "dgram", "stream" or "seqpacket".
  User and group default to 0.
  'seclabel' is the SELinux security context for the socket.
  It defaults to the service security context, as specified by seclabel or
  computed based on the service executable file security context.

user <username>
  Change to username before exec'ing this service.
  Currently defaults to root.  (??? probably should default to nobody)
  As of Android M, processes should use this option even if they
  require linux capabilities.  Previously, to acquire linux
  capabilities, a process would need to run as root, request the
  capabilities, then drop to its desired uid.  There is a new
  mechanism through fs_config that allows device manufacturers to add
  linux capabilities to specific binaries on a file system that should
  be used instead. This mechanism is described on
  http://source.android.com/devices/tech/config/filesystem.html.  When
  using this new mechanism, processes can use the user option to
  select their desired uid without ever running as root.

group <groupname> [ <groupname> ]*
  Change to groupname before exec'ing this service.  Additional
  groupnames beyond the (required) first one are used to set the
  supplemental groups of the process (via setgroups()).
  Currently defaults to root.  (??? probably should default to nobody)

seclabel <seclabel>
  Change to 'seclabel' before exec'ing this service.
  Primarily for use by services run from the rootfs, e.g. ueventd, adbd.
  Services on the system partition can instead use policy-defined transitions
  based on their file security context.
  If not specified and no transition is defined in policy, defaults to the init context.

oneshot 
  Do not restart the service when it exits.

class <name>   //一個特定的name, 所有有這個name的service統一管理,一起啟動,一起stop
  Specify a class name for the service.  All services in a
  named class may be started or stopped together.  A service
  is in the class "default" if one is not specified via the
  class option.

onrestart      //當服務重啟時執行
  Execute a Command (see below) when service restarts.

writepid <file...>
  Write the child's pid to the given files when it forks. Meant for
  cgroup/cpuset usage.
下面看一下zygote服務示例程式碼位置system/core/rootdir/init.zygote64.rc;

service的名字為zygote, 啟動路徑為手機中/system/bin/app_process64後面的都是啟動引數.

service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server 
    class main      //zygote的name為main,和class name為main的一塊被啟動
    socket zygote stream 660 root system   //為zygote建立socket
    onrestart write /sys/android_power/request_state wake  //當zygote重啟時執行下面動作
    onrestart write /sys/power/state on
    onrestart restart audioserver
    onrestart restart cameraserver
    onrestart restart media
    onrestart restart netd
    writepid /dev/cpuset/foreground/tasks /dev/stune/foreground/tasks
對Action與Services瞭解之後就可以開始解析init.rc檔案了.

在分析init程序時知道解析init.rc檔案的入口在init.cpp的main函式中

    parser.ParseConfig("/init.rc");
解析init.rc的核心在system/core/init/init_parse.cpp檔案中
bool Parser::ParseConfig(const std::string& path) {
    if (is_dir(path.c_str())) {       //如果路徑為資料夾,就呼叫解析資料夾的函式
        return ParseConfigDir(path);   
    }
    return ParseConfigFile(path);  //解析init.rc檔案
}
bool Parser::ParseConfigFile(const std::string& path) {
    INFO("Parsing file %s...\n", path.c_str());  //根據之前講解klog一文,可知該log打印不出
    Timer t;      //利用Timer計時,解析檔案耗時多長時間
    std::string data;
    if (!read_file(path.c_str(), &data)) {   //讀取檔案
        return false;
    }

    data.push_back('\n'); // TODO: fix parse_config.
    ParseData(path, data);     //解析內容
    for (const auto& sp : section_parsers_) {
        sp.second->EndFile(path);       //解析完init.rc檔案後, 呼叫Import_parse.cpp的EndFile函式,解析引用的rc檔案
    }

    // Turning this on and letting the INFO logging be discarded adds 0.2s to
    // Nexus 9 boot time, so it's disabled by default.
    if (false) DumpState();

    NOTICE("(Parsing %s took %.2fs.)\n", path.c_str(), t.duration());  //打印出解析哪個檔案,花費多長時間, 來查詢耗時點
    return true;
}
void Parser::ParseData(const std::string& filename, const std::string& data) {
    //TODO: Use a parser with const input and remove this copy
    std::vector<char> data_copy(data.begin(), data.end());     //資料copy
    data_copy.push_back('\0');

    parse_state state;
    state.filename = filename.c_str();
    state.line = 0;
    state.ptr = &data_copy[0];
    state.nexttoken = 0;

    SectionParser* section_parser = nullptr;
    std::vector<std::string> args;

    for (;;) {        //迴圈解析init.rc檔案
        switch (next_token(&state)) {  //通過nextToken函式獲得需要解析的這一行是文字內容還是action,services
        case T_EOF:             //檔案解析結束
            if (section_parser) {
                section_parser->EndSection();   
            }
            return;
        case T_NEWLINE:      //解析新的一行, 可能是一個action,services或者import
            state.line++;
            if (args.empty()) {
                break;
            }
            if (section_parsers_.count(args[0])) {
                if (section_parser) {
                    section_parser->EndSection();   //section解析結束
                }
                section_parser = section_parsers_[args[0]].get();
                std::string ret_err;
                if (!section_parser->ParseSection(args, &ret_err)) {  //解析Action,Service, Import 三個Section
                    parse_error(&state, "%s\n", ret_err.c_str());
                    section_parser = nullptr;
                }
            } else if (section_parser) {
                std::string ret_err;      //解析section的內容
                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;
        }
    }
}

nextToken函式在/system/core/init/parse.cpp中實現

int next_token(struct parse_state *state)
{
    char *x = state->ptr;
    char *s;

    if (state->nexttoken) {
        int t = state->nexttoken;
        state->nexttoken = 0;
        return t;
    }

    for (;;) { 
        switch (*x) {
        case 0:
            state->ptr = x;
            return T_EOF;
        case '\n':
            x++;
            state->ptr = x;
            return T_NEWLINE;
        case ' ':
        case '\t':
        case '\r':
            x++;
            continue;
        case '#': 
            while (*x && (*x != '\n')) x++;
            if (*x == '\n') {
                state->ptr = x+1;
                return T_NEWLINE;
            } else {
                state->ptr = x;
                return T_EOF;
            }
        default: 
            goto text;
        }
    }

textdone:
    state->ptr = x;
    *s = 0;
    return T_TEXT;
text:
    state->text = s = x;
textresume:
    for (;;) {
        switch (*x) {
        case 0:
            goto textdone;
        case ' ':
        case '\t':
        case '\r':
            x++;
            goto textdone;
        case '\n':
            state->nexttoken = T_NEWLINE;
            x++;
            goto textdone;
        case '"':
            x++;
            for (;;) {
                switch (*x) {
                case 0:
                        /* unterminated quoted thing */
                    state->ptr = x;
                    return T_EOF;
                case '"':
                    x++;
                    goto textresume;
                default:
                    *s++ = *x++;
                }
            }
            break;
        case '\\':
            x++;
            switch (*x) {
            case 0:
                goto textdone;
            case 'n':
                *s++ = '\n';
                break;
            case 'r':
                *s++ = '\r';
                break;
            case 't':
                *s++ = '\t';
                break;
            case '\\':
                *s++ = '\\';
                break;
            case '\r':
                    /* \ <cr> <lf> -> line continuation */
                if (x[1] != '\n') {
                    x++;
                    continue;
                }
            case '\n':
                    /* \ <lf> -> line continuation */
                state->line++;
                x++;
                    /* eat any extra whitespace */
                while((*x == ' ') || (*x == '\t')) x++;
                continue;
            default:
                    /* unknown escape -- just copy */
                *s++ = *x++;
            }
            continue;
        default:
            *s++ = *x++;
        }
    }
    return T_EOF;

解析Action

呼叫ActionParser的ParseSection函式,程式碼位置system/core/init/action.cpp

bool ActionParser::ParseSection(const std::vector<std::string>& args,
                                std::string* err) {
    std::vector<std::string> triggers(args.begin() + 1, args.end());  //獲取trigger, 擷取on後面的字串
    if (triggers.size() < 1) {
        *err = "actions must have a trigger";    //檢查是否存在trigger
        return false;
    }

    auto action = std::make_unique<Action>(false);
    if (!action->InitTriggers(triggers, err)) {   //將triggers放入event_trigger_
        return false;
    }

    action_ = std::move(action);   //賦值action_
    return true;
}
bool ActionParser::ParseLineSection(const std::vector<std::string>& args,
                                    const std::string& filename, int line,
                                    std::string* err) const {
    return action_ ? action_->AddCommand(args, filename, line, err) : false;  //action_為true ,已經賦過值
}
bool Action::AddCommand(const std::vector<std::string>& args,
                        const std::string& filename, int line, std::string* err) {
    if (!function_map_) {        //function_map在分析init程序時有提到, 為true
        *err = "no function map available";
        return false;
    }

    if (args.empty()) {
        *err = "command needed, but not provided";
        return false;
    }

    auto function = function_map_->FindFunction(args[0], args.size() - 1, err);   //根據命令找到對應的函式
    if (!function) {
        return false;
    }

    AddCommand(function, args, filename, line);
    return true;
}

void Action::AddCommand(BuiltinFunction f,
                        const std::vector<std::string>& args,
                        const std::string& filename, int line) {
    commands_.emplace_back(f, args, filename, line);  //將對應函式, 引數,檔名放入commans_中
}
void ActionParser::EndSection() {
    if (action_ && action_->NumCommands() > 0) {
        ActionManager::GetInstance().AddAction(std::move(action_));  //將一個action的所有command都加入action_後,將action_加入action列表
    }
}
void ActionManager::AddAction(std::unique_ptr<Action> action) {
    auto old_action_it =
        std::find_if(actions_.begin(), actions_.end(),
                     [&action] (std::unique_ptr<Action>& a) {
                         return action->TriggersEqual(*a);
                     });

    if (old_action_it != actions_.end()) {
        (*old_action_it)->CombineAction(*action);
    } else {
        actions_.emplace_back(std::move(action));   //將所有的action加入actions_列表
    }
}
解析Services
呼叫ServiceParser的ParseSection函式,程式碼位置system/core/init/service.cpp
bool ServiceParser::ParseSection(const std::vector<std::string>& args,
                                 std::string* err) {
    if (args.size() < 3) {    //判斷service是否有name與可執行程式
        *err = "services must have a name and a program";
        return false;
    }

    const std::string& name = args[1];
    if (!IsValidName(name)) {    //檢查name是否可用
        *err = StringPrintf("invalid service name '%s'", name.c_str());
        return false;
    }

    std::vector<std::string> str_args(args.begin() + 2, args.end()); //獲取執行程式與引數
    service_ = std::make_unique<Service>(name, "default", str_args);   //給service_賦值
    return true;
}
bool ServiceParser::ParseLineSection(const std::vector<std::string>& args,
                                     const std::string& filename, int line,
                                     std::string* err) const {
    return service_ ? service_->HandleLine(args, err) : false;  //service_為true, 呼叫HandleLine
}
Service::OptionHandlerMap::Map& Service::OptionHandlerMap::map() const {
    constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
    static const Map option_handlers = {   //option對應的函式
        {"class",       {1,     1,    &Service::HandleClass}},
        {"console",     {0,     0,    &Service::HandleConsole}},
        {"critical",    {0,     0,    &Service::HandleCritical}},
        {"disabled",    {0,     0,    &Service::HandleDisabled}},
        {"group",       {1,     NR_SVC_SUPP_GIDS + 1, &Service::HandleGroup}},
        {"ioprio",      {2,     2,    &Service::HandleIoprio}},
        {"keycodes",    {1,     kMax, &Service::HandleKeycodes}},
        {"oneshot",     {0,     0,    &Service::HandleOneshot}},
        {"onrestart",   {1,     kMax, &Service::HandleOnrestart}},
        {"seclabel",    {1,     1,    &Service::HandleSeclabel}},
        {"setenv",      {2,     2,    &Service::HandleSetenv}},
        {"socket",      {3,     6,    &Service::HandleSocket}},
        {"user",        {1,     1,    &Service::HandleUser}},
        {"writepid",    {1,     kMax, &Service::HandleWritepid}},
    };
    return option_handlers;
}

bool Service::HandleLine(const std::vector<std::string>& args, std::string* err) {
    if (args.empty()) {
        *err = "option needed, but not provided";
        return false;
    }

    static const OptionHandlerMap handler_map;   //獲得option對應的函式表
    auto handler = handler_map.FindFunction(args[0], args.size() - 1, err); //根據option獲取對應的函式名

    if (!handler) {
        return false;
    }

    return (this->*handler)(args, err);   
}
void ServiceParser::EndSection() {
    if (service_) {
        ServiceManager::GetInstance().AddService(std::move(service_));
    }
}
void ServiceManager::AddService(std::unique_ptr<Service> service) {
    Service* old_service = FindServiceByName(service->name());
    if (old_service) {    //service已經被定義過了就拋棄
        ERROR("ignored duplicate definition of service '%s'",
              service->name().c_str());
        return;
    }
    services_.emplace_back(std::move(service));  //將service新增services_列表
}
解析Import
呼叫ImportParser的ParseSection函式,程式碼位置system/core/init/import_parser.cpp
bool ImportParser::ParseSection(const std::vector<std::string>& args,
                                std::string* err) {
    if (args.size() != 2) {
        *err = "single argument needed for import\n";
        return false;
    }

    std::string conf_file;
    bool ret = expand_props(args[1], &conf_file); //獲取引用的conf_file檔案, 
    if (!ret) {
        *err = "error while expanding import";
        return false;
    }

    INFO("Added '%s' to import list\n", conf_file.c_str());
    imports_.emplace_back(std::move(conf_file));   //將所有的conf_file新增到imports_列表
    return true;
}
前面講過當呼叫完ParseData函式後,會遍歷所有section執行endFile函式
void ImportParser::EndFile(const std::string& filename) {
    auto current_imports = std::move(imports_);  //獲取imports_
    imports_.clear();   //將imports_列表清空
    for (const auto& s : current_imports) {  //遍歷列表
        if (!Parser::GetInstance().ParseConfig(s)) {   //呼叫ParseConfig函式,對其他配置進行解析, 流程遇上面的相同
            ERROR("could not import file '%s' from '%s': %s\n",
                  s.c_str(), filename.c_str(), strerror(errno));
        }
    }
}

到此init.rc檔案就解析完了, 那麼大家就想知道了解析完的actions與services又是如何執行的呢? 

相關推薦

Android7.0解析Init.rc檔案

    在解析Init.rc之前需要對init.rc檔案有一個認識,init.rc是由一種被稱為"Android初始化語言"(Android Init Language,這裡簡稱為AIL)的指令碼寫成的檔案.該語言是由語句組成的,主要包含了五種型別的語句:ActionComm

Android7.1.2原始碼解析系列】實戰分析init.rc檔案

實戰分析init.rc檔案 前言:經過上一篇的/system/core/init/readme.txt檔案的翻譯,對於init.rc的語法也有了一定的瞭解,這一篇就對/system/core/rootdir/init.rc檔案進行一個分析,希望能夠藉此對android的開

Android 的 init.rc 檔案簡介

init.rc由許多的Action和Service組成。每一個語句佔據一行,並且各個關鍵字被空格分開. 由 # (前面允許有空格)開始的行都是註釋行(comment) 一個actions 或 services 的開始隱含聲明瞭一個新的段,所有commands 或 option

Android啟動流程分析(九) 解析init.rc的service

############################################# 本文為極度寒冰原創,轉載請註明出處 ############################################# 在分析完解析init.rc的action之後,剩

android init.rc檔案語法詳解

初始化語言包含了四種類型的宣告: Actions(行動)、 Commands(命令)、Services(服務)和Options(選項)。 基本語法規定 1 所有型別的語句都是基於行的,一個語句包含若干個tokens,token之間通過空格字元分隔. 如果一個token中需

Android init.rc檔案淺析

本文主要來自$ANDROID_SOURCE/system/init/readme.txt的翻譯. 1 簡述 Android init.rc檔案由系統第一個啟動的init程式解析,此檔案由語句組成,主要包含了四種類型的語句:Action,Commands,Services,O

Android 8.0 系統啟動流程之init.rc解析與service流程(七)

1、概述     上一篇文章中我們介紹了一下init.rc檔案中的語法規則,而本文將分析如何解析rc檔案,並對rc檔案中的某一service啟動過程進行分析。 2、解析.rc檔案 之前我們在文件中看到.rc檔案主要有根目錄下的 /init.rc ,以及

Android 7.0 init.rc 執行shell指令碼 ---- 製作一初始化配置檔案

最近在一個Android 7.0 PDA專案中遇到如下需求:初始化一配置檔案,此配置檔案需要儲存到data分割槽供系統服務和第三方應用進行讀寫操作,另外此配置檔案在系統重啟後保持檔案內容不變,除非恢復出廠才可以恢復成預設配置引數 。因為初始化的配置引數為了方便其它同事修改

Android7.0以上安裝時出現“解析軟體包錯誤”

vivox23在Android studio上除錯軟體出現“解析軟體包錯誤”,不管是專案工程還是自己新建的hellowrold工程都會出現這個問題,取消下圖第一個選項 Android studio的2.0新版本出了一個革命性的功能就是Instant Run(即時執行)!新的即時執行功能可以

Android7.0+安裝apk檔案之後不彈出安裝完成的介面解決辦法

在Android7.0+手上,版本升級完成,發現手機安裝完成,不啟動安裝完成頁面,而是直接關閉了,小編也是一頭霧水。琢磨了很久,下面小編把解決辦法show出來。 第一步:在資原始檔下面新建 xml資料夾,新建file_paths.xml檔案,程式碼如下: <?xml version="

Android7.1 [Camera] cam_board.xml 檔案解析原始碼分析(一)

        原始碼平臺:rk3399           RK支援了很多個攝像頭,在驅動目錄hardware/rockchip/camer

Android7.0適配:關於Android 7.0 在應用間共享檔案的適配

前言 隨安卓版越來越高,對隱私保護力度亦越來越大。從Android6.0動態許可權控制(Runtime Permissions)到Android7.0私有目錄限訪、StrictMode API政策等的更改,這些更改在為使用者帶來更加安全的作業系統的同時也為開發者

Android 8.0 系統啟動流程之init.rc語法規則(六)

1、概述     init經過前兩個階段後,已經建立了屬性系統和SELinux系統,但是init程序還需要執行很多其他的操作,還要啟動許多關鍵的系統服務,但是如果都是像屬性系統和SELinux系統那樣一行行程式碼去做,顯得有點雜亂繁瑣,而且不容易擴充套件,所以

適配Android7.0應用間檔案共享FileProvider

android編譯版本升級到7.0以後,會出現很多適配方面的工作,從android官方文件對於android7.0行為變更可以瞭解到,android7.0的應用禁止傳遞類似file:// URI這樣的連結,否則應用會丟擲FileUriExposedException異常,比較典型的場景就是我

Android7.0以上安裝時出現“解析軟體包錯誤”

工作中,vivoX9s,版本號:7.1.2,在Android studio上除錯軟體出現“解析軟體包錯誤”,不管是專案工程還是自己新建的hellowrold工程都會出現這個問題,如圖: eclipse: Android studio: 問題解決辦法: 1.ec

Android7.0安裝apk檔案之後不彈出安裝介面的問題

Android7.0以下的版本,別忘了加上: intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);   Android7.0以上的版本,還需要加上許可權: <uses-permission android:name="an

Android init原始碼分析(2)init.rc解析

action_for_each_trigger("early-init", action_add_queue_tail); queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done"); q

init rc中建立檔案

android的init rc目前不支援touch: touch /data/misc/logd/kmsg.log log中會報錯:  init: /init.rc: 83: invalid keyword 'touch' 可以用copy和write命令建立檔案 writ

Android啟動流程分析(七) init.rc解析

############################################# 本文為極度寒冰原創,轉載請註明出處 ############################################# Init.rc的解析過程是筆者認為在andro

Android7.0檔案訪問許可權

在Android N之後大家會發現一些奇奇怪怪的問題,這裡也是自己在開發的時候遇到的,做一個記錄: 情況出現在,把自己的手機刷成了Android N,然後,坑就來了,發現呼叫相簿的地方崩潰了,提示FileUriExposedException,後面在下載應用然