Android OTA update package creation script (3. Packaging), androidota
This is the main flow statement packaged in the mian function in ota_from_target_files:
# Abstract A new temporary file temp_zip_file = tempfile. namedTemporaryFile () # create a zip package for packaging output_zip = zipfile. zipFile (temp_zip_file, "w", compression = zipfile. ZIP_DEFLATED) # determine whether it is a difference or if OPTIONS in the entire package. incremental_source is None: WriteFullOTAPackage (input_zip, output_zip) if OPTIONS. package_key is None: # determine whether a signature key is set. If not, use the default key to sign the key. Related blog posts on the platform signature have been designed. Please refer to OPTIONS on your own. package_key = OPTIONS.info _ dict. get ("default_syst Em_dev_certificate "," build/target/product/security/testkey ") else: # This is a series of operations involved in the creation of the difference package. Print "unzipping source target-files... "OPTIONS. source_tmp, source_zip = common. unzipTemp (OPTIONS. incremental_source) OPTIONS.tar get_info_dict = OPTIONS.info _ dict OPTIONS. source_info_dict = common. loadInfoDict (source_zip) if OPTIONS. package_key is None: OPTIONS. package_key = OPTIONS. source_info_dict.get ("default_system_dev_certificate", "build/target/product/security/testkey") if OPTIONS. verbose: print "--- source info ---" common. dumpInfoDict (OPTIONS. source_info_dict) WriteIncrementalOTAPackage (input_zip, source_zip, output_zip) output_zip.close ()
Step 2: make the entire package
The following is part of the code in the main function WriteFullOTAPackage of the entire package.
# The function processing process is to first obtain the script generator. The default format is edify. Then obtain the metadata, which is used for environment variables of Android. Then obtain the version of the device configuration parameters, such as the api function. Then, determine whether to ignore the timestamp. Def WriteFullOTAPackage (input_zip, output_zip): # TODO: how to determine this? We don't know what version it will # be installed on top. for now, we recommend CT the API just won't # change very often. # Here we introduce a new module edify_generator and abstract a script generator to generate the edify script. The script here refers to updater-script, which is a text file. # Edify has two main files. This file can be found in the META-INF/com/google/android folder in the. ZIP file. ① Update-binary -- the binary interpreter executed when the user chooses to refresh to. Zip (usually in recovery mode. ② Updater-script -- installation script, which is a text file. # What is edify? Edifyis a simple scripting language used to install CyanogenMod and other software from the. ZIP file. The edify script is not necessarily used to update firmware. It can be used to replace/Add/delete specific files, or even format partitions. Generally, the edify script runs when the user selects "Refresh zip" in recovery mode. Script = edify_generator.EdifyGenerator (3, OPTIONS.info _ dict) # create a metadata dictionary to encapsulate system attributes related to a more-row package, such as ro. build. fingerprint (system fingerprint. Metadata = {"post-build": GetBuildProp ("ro. build. fingerprint ", OPTIONS.info _ dict)," pre-device ": GetBuildProp (" ro. product. device ", # (device used) OPTIONS.info _ dict)," post-timestamp ": GetBuildProp (" ro. build. date. utc ", # (system Compilation Time (digital version), no need to modify) OPTIONS.info _ dict),} # obtain some environment variables and encapsulate them in the DEviceSpecificParams class, this is a class that encapsulates the specific properties of a device. Each of the following device parameters has been mentioned before and will not be described here. Device_specific = common. deviceSpecificParams (input_zip = input_zip, input_version = OPTIONS.info _ dict ["recovery_api_version"], output_zip = output_zip, script = script, input_tmp = OPTIONS. input_tmp, metadata = metadata, info_dict = OPTIONS.info _ dict) # In the following code, we can understand that downgrading is not allowed, that is, the Assert statement in the script, so that the update zip package can only be used to upgrade the old version. If not OPTIONS. omit_prereq: ts = GetBuildProp ("ro. build. date. utc ", OPTIONS.info _ dict) # obtain the world time ts_text = GetBuildProp (" ro. build. date ", OPTIONS.info _ dict) # obtain the compile date script. assertOlderBuild (ts, ts_text) # The following Assert statement indicates that the update zip package can only be used on the same device, that is, ro of the target device. product. the device token is the same as that in update.zip. AppendAssertions (script, OPTIONS.info _ dict) # callback function, used to call device-related code. Call device_specific.FullOTA_Assertions () When upgrading ()
def FullOTA_Assertions(self): """Called after emitting the block of assertions at the top of a full OTA package. Implementations can add whatever additional assertions they like.""" return self._DoCall("FullOTA_Assertions")
def _DoCall(self, function_name, *args, **kwargs): """Call the named function in the device-specific module, passing the given args and kwargs. The first argument to the call will be the DeviceSpecific object itself. If there is no module, or the module does not define the function, return the value of the 'default' kwarg (which itself defaults to None).""" if self.module is None or not hasattr(self.module, function_name): return kwargs.get("default", None) return getattr(self.module, function_name)(*((self,) + args), **kwargs)
The above is just a simple call, but simply put, here the value of self. module is None. Before that, we did not load the "device_specific" module. No related module files are found in the build/tools/release tools/directory (note! You can also refer to the log printed when you make the entire package, such as "unable to load device-specific module; assuming none ")
Next, we get a file object of recovery_image. The specific implementation code is as follows:
Recovery_img = common. GetBootableImage ("recovery. img", "recovery. img ",
OPTIONS. input_tmp, "RECOVERY ")
Def GetBootableImage (name, prebuilt_name, unpack_dir, tree_subdir, info_dict = None): "" Return a File object (with name 'name') with the desired bootable image. look for it in 'unpack _ dir'/BOOTABLE_IMAGES under the name 'prebuilt _ name', otherwise construct it from the source files in 'unpack _ dir'/'tree _ subdir '. "" # connect two file name addresses, such as OS. path. join ("D: \", "join.txt") returns D: \ join.txt. If we have a write action, the corresponding file in the corresponding directory is generated. Otherwise, no This file exists. Prebuilt_path = OS. path. join (unpack_dir, "BOOTABLE_IMAGES", prebuilt_name) if OS. path. exists (prebuilt_path): print "using prebuilt % s... "% (prebuilt_name,) return File. fromLocalFile (name, prebuilt_path) else: print "building image from target_files % s... "% (tree_subdir,) fs_config =" META/"+ tree_subdir.lower () +" _filesystem_config.txt "# encapsulate the File object to be returned in the File class. For details, see File source code, it can be found that the File class is just an abstraction of the File, but it also encapsulates the write and read operations return File (name, BuildBootableImage (OS. path. join (unpack_dir, tree_subdir), OS. path. join (unpack_dir, fs_config), info_dict ))
The following is a specific function in File. First, we can see that the entry function of the class has a parameter data, so the second parameter in the File object returned above is data, which can be understood as the input stream:
class File(object): def __init__(self, name, data): self.name = name self.data = data self.size = len(data) self.sha1 = sha1(data).hexdigest() @classmethod def FromLocalFile(cls, name, diskname): f = open(diskname, "rb") data = f.read() f.close() return File(name, data) def WriteToTemp(self): t = tempfile.NamedTemporaryFile() t.write(self.data) t.flush() return t def AddToZip(self, z): ZipWriteStr(z, self.name, self.data)DIFF_PROGRAM_BY_EXT = { ".gz" : "imgdiff", ".zip" : ["imgdiff", "-z"], ".jar" : ["imgdiff", "-z"], ".apk" : ["imgdiff", "-z"], ".img" : "imgdiff", }
Def BuildBootableImage (sourcedir, fs_config_file, info_dict = None): "" Take a kernel, using line, and ramdisk directory from the input (in 'sourcedir '), and turn them into a boot image. return the image data, or None if sourcedir does not appear to contains files for building the requested image. "# As the mode parameter of access (), test whether the path exists. if (not OS. access (OS. path. join (sourcedir, "RAMDISK"), OS. f_ OK) or not OS. Access (OS. path. join (sourcedir, "kernel"), OS. f_ OK): return None if info_dict is None: info_dict = OPTIONS.info _ dict # create a temporary file object ramdisk_img = tempfile. namedTemporaryFile () img = tempfile. namedTemporaryFile () if OS. access (fs_config_file, OS. f_ OK): # Use mkbootfs tool (mkbootfs tool is compiled after the Android source code, in the source directory/out/host/linux-x86/bin automatically generated) create ramdisk cmd = ["mkbootfs", "-f", fs_config_file, OS. path. join (sourcedir, "R AMDISK ")] else: cmd = [" mkbootfs ", OS. path. join (sourcedir, "RAMDISK")] p1 = Run (cmd, stdout = subprocess. PIPE) # fileno () is used to obtain the file description word used by the file stream specified by the parameter stream, mkbootfs and minigzip use the MKBOOTFS and MINIGZIP variables described in mkbootfs and minigzip to generate a ramdisk in cpio format. img. The source code of the mkbootfs and minigzip tools is located in the system/core/cpio and external/zlib directories respectively. P2 = Run (["minigzip"], stdin = p1.stdout, stdout = ramdisk_img.file.fileno () p2.wait () p1.wait () assert p1.returncode = 0, "mkbootfs of % s ramdisk failed" % (targetname,) assert p2.returncode = 0, "minigzip of % s ramdisk failed" % (targetname ,) # Call the command line file mkbootimg Android (out/host/linux-x86/bin/) to package cmd = ["mkbootimg", "-- kernel", OS. path. join (sourcedir, "kernel")] fn = OS. path. join (sourcedir, "cmdline") if OS. access (fn, OS. f_ OK): cmd. append ("-- cmdline") cmd. append (open (fn ). read (). rstrip ("\ n") fn = OS. path. join (sourcedir, "base") if OS. access (fn, OS. f_ OK): cmd. append ("-- base") cmd. append (open (fn ). read (). rstrip ("\ n") fn = OS. path. join (sourcedir, "pagesize") if OS. access (fn, OS. f_ OK): cmd. append ("-- pagesize") cmd. append (open (fn ). read (). rstrip ("\ n") args = info_dict.get ("mkbootimg_args", None) if args and args. strip (): cmd. extend (args. split () # wschen 2013-06-06 for firmware version in bootimage header and limit max length to 15 bytes fn = OS. path. join (sourcedir, "board") if OS. access (fn, OS. f_ OK): cmd. append ("-- board") cmd. append (open (fn ). read (). rstrip ("\ n") [: 15]) # cmd. extend (["-- ramdisk", ramdisk_img.name, # "-- output", img. name]) cmd. extend (["-- ramdisk", OS. path. join (sourcedir, "ramdisk"), "-- output", img. name]) p = Run (cmd, stdout = subprocess. PIPE) p. communicate () assert p. returncode = 0, "mkbootimg of % s image failed" % (OS. path. basename (sourcedir),) img. seek (OS. SEEK_SET, 0) data = img. read () ramdisk_img.close () img. close () return data
The above is the basic process of packaging files using the Android command line. In fact, I have not fully understood it. I have also referred to some online materials. If you have any questions, please correct me and discuss it.