1. 程式人生 > >Android OTA升級包製作指令碼詳解(一,引數解析)

Android OTA升級包製作指令碼詳解(一,引數解析)

寫在前面:

    “build/tools/releasetools/ota_from_target_files  -u lk.bin  -n target.zip update.zip”這是製作整包的命令,很顯然這裡支援lk升級。本系列博文主要對該命令的執行流程及原理進行一個系統的分析,涉及到/build/tools/releasetools/目錄下多個模組如ota_from_target_files、common等。由於本人對python瞭解粗淺,文中所涉及到的python語法大都做了註釋,以便幫助部分對python有興趣的同學能夠讀懂相應的語句。

    整個流程看起來有些繁瑣冗雜,希望有興趣的同學能夠耐心的去看,已經比較熟悉python語法的同學可以挑選感興趣的部分作為參考。

一,執行指令碼

    那麼首先,我們從命令中可以看到整包的製作流程是從ota_from_target_files模組開始的。而ota_from_target_files模組最先執行的方法是從下面這段語句開始的,如下:

    #__name__作為模組的內建屬性,指.py檔案的呼叫方式;.py檔案有兩種使用方式:作為模組被呼叫和直接使用。如果它等於"__main__"就表示是直接執行。也就是說在if __name__ == "__main__":之後的語句作為模組被呼叫的時候不執行;直接使用的時候,語句之後的程式碼執行。而這裡很顯然是直接使用。

if __name__ == '__main__':
  try:
    common.CloseInheritedPipes()"""MAC系統中卻是一些描述檔案(PIPE),這裡在開始之前關閉fds;這裡主要是根據當前所使用的作業系統做的一些調整"""
    main(sys.argv[1:])"""這是編譯指令碼主要的方法"""
  except common.ExternalError, e:
    print
    print "   ERROR: %s" % (e,)
    print
    sys.exit(1)
    下面是指令碼的入口函式main(arg)
def main(argv):
  """將使用者設定的 Option 存入 OPTIONS 變數中。OPTIONS是一個Python Class, 我們將其理解為一個C Struct或者一個java的封裝即可"""
  def option_handler(o, a):
    if o in ("-b", "--board_config"):#in是一個布林操作符,用來測試左邊的運算元是否包含於右邊的元祖,這裡用來判斷引數o所傳入的值是否包含在("-b", "--board_config")中;
      pass   # deprecated
    elif o in ("-k", "--package_key"):
      OPTIONS.package_key = a
    elif o in ("-i", "--incremental_from"):
      OPTIONS.incremental_source = a
    elif o in ("-w", "--wipe_user_data"):
      OPTIONS.wipe_user_data = True
    elif o in ("-n", "--no_prereq"):
      OPTIONS.omit_prereq = True
    elif o in ("-e", "--extra_script"):
      OPTIONS.extra_script = a
    elif o in ("-a", "--aslr_mode"):
      if a in ("on", "On", "true", "True", "yes", "Yes"):
        OPTIONS.aslr_mode = True
      else:
        OPTIONS.aslr_mode = False
    elif o in ("--worker_threads"):
      OPTIONS.worker_threads = int(a)
    elif o in ("-2", "--two_step"):
      OPTIONS.two_step = True
    elif o in ("-r", "--preloader"):
      OPTIONS.preloader = a
    elif o in ("-l", "--logo"):
      OPTIONS.logo = a
    elif o in ("-u", "--uboot"):
      OPTIONS.uboot = a
    elif o in ("-d", "--dsp"):
      OPTIONS.dsp = a
    elif o in ("-f", "--special_factory_reset"):
      OPTIONS.special_factory_reset = True
    elif o in ("-g", "--ubifs"):
      OPTIONS.ubifs = True  
    elif o in ("-t", "--tee"):
      OPTIONS.tee = a 
    elif o in ("-z", "--trustonic"):
      OPTIONS.trustonic = a
    else:
      return False
    return True

二,解析引數
  #解析引數
  args = common.ParseOptions(argv, __doc__,
                             extra_opts="b:k:i:d:wfgne:r:l:u:t:z:d:a:s:2",
                             extra_long_opts=["board_config=",
                                              "package_key=",
                                              "incremental_from=",
                                              "wipe_user_data",
                                              "special_factory_reset",
                                              "ubifs",
                                              "no_prereq",
                                              "extra_script=",
                                              "preloader=",
                                              "logo=",
                                              "uboot=",
                                              "tee=",
                                              "trustonic=",
                                              "dsp=",
                                              "worker_threads=",
                                              "aslr_mode=",
                                              "two_step",
                                              ],
                             extra_option_handler=option_handler)
    上面解析引數的操作實際上是封裝在了common.py模組來進行,這裡我們對extra_option_handler=option_handler簡單的解釋一下,在Python中函式是可以直接複製給一個變數的,option_handler是上面定義的一個方法,而這裡並沒有執行,而是直接賦值給了extra_option_handler,而且我們需要注意的是賦值並不會執行option_handler中內容,而是在實際呼叫的時候才會執行。那麼既然變數可以指向函式,所以一個函式可以接收另外一個函式作為引數。

    那麼我們接著來了解函式解析的流程,首先看程式碼:

#在python中,getopt模組是專門用來處理命令列引數的,這裡args是個列表,包含那些沒有"-"或"--"的引數,格式如下['target.zip','update.zip'],而opts是個包含元祖的列表,每個元祖是分析出來的格式資訊,如[('-u', 'lk.bin'),('-n', ''),('-i', 'base.zip')] ;如果想了解getopt具體的邏輯請參考這篇博文python中getopt的使用

#解析引數
def ParseOptions(argv,
                 docstring,
                 extra_opts="", extra_long_opts=(),
                 extra_option_handler=None):
  """Parse the options in argv and return any arguments that aren't
  flags.  docstring is the calling module's docstring, to be displayed
  for errors and -h.  extra_opts and extra_long_opts are for flags
  defined by the caller, which are processed by passing them to
  extra_option_handler."""

  try:
    opts, args = getopt.getopt(
        argv, "hvp:s:x:" + extra_opts,
        ["help", "verbose", "path=", "signapk_path=", "extra_signapk_args=",
         "java_path=", "public_key_suffix=", "private_key_suffix=",
         "device_specific=", "extra="] +
        list(extra_long_opts))
		
		
	#那麼我們可以在這裡新增log來檢視opts、args中的值
	print("begin")
	for i in range(len(opts)):
	    print(opts[i])
	print("************")
	for i in range(len(args)):
	    print(args[i])
	print("end")
	
	
  except getopt.GetoptError, err:
    Usage(docstring)
    print "**", str(err), "**"
    sys.exit(2)

  path_specified = False

  for o, a in opts:
    if o in ("-h", "--help"):
      Usage(docstring)
      sys.exit()
    elif o in ("-v", "--verbose"):
      OPTIONS.verbose = True
    elif o in ("-p", "--path"):
      OPTIONS.search_path = a
    elif o in ("--signapk_path",):
      OPTIONS.signapk_path = a
    elif o in ("--extra_signapk_args",):
      OPTIONS.extra_signapk_args = shlex.split(a)
    elif o in ("--java_path",):
      OPTIONS.java_path = a
    elif o in ("--public_key_suffix",):
      OPTIONS.public_key_suffix = a
    elif o in ("--private_key_suffix",):
      OPTIONS.private_key_suffix = a
    elif o in ("-s", "--device_specific"):
      OPTIONS.device_specific = a
    elif o in ("-x", "--extra"):
      key, value = a.split("=", 1)
      OPTIONS.extras[key] = value
    else:
      if extra_option_handler is None or not extra_option_handler(o, a):
        assert False, "unknown option \"%s\"" % (o,)
  #環境變數
  os.environ["PATH"] = (os.path.join(OPTIONS.search_path, "bin") +
                        os.pathsep + os.environ["PATH"])

  return args

下面是具體log列印的資訊:

begin
('-u', 'lk.bin')
('-n', '')
('-i', 'base.zip')
******
target.zip
update.zip
end
那麼引數解析完畢之後,執行流程繼續回到ota_from_target_files中的main函式。我們順著流程接著看。

下面是對完成引數解析的返回值的長度進行一個過濾,這裡固定是2,無論是整包或者差分包的製作。

  if len(args) != 2:
    common.Usage(__doc__)
    sys.exit(1)
#這裡沒有額外的指令碼,因此OPTIONS.extra_script的值為None。
  if OPTIONS.extra_script is not None:
    OPTIONS.extra_script = open(OPTIONS.extra_script).read()
下面我們來看解壓的流程。