diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..68a649c
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+.gitignore
+*.pyc
+.project
+.pydevproject
+.idea
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..5205d83
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,191 @@
+Apache License
+Version 2.0, January 2004
+http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+"License" shall mean the terms and conditions for use, reproduction, and
+distribution as defined by Sections 1 through 9 of this document.
+
+"Licensor" shall mean the copyright owner or entity authorized by the copyright
+owner that is granting the License.
+
+"Legal Entity" shall mean the union of the acting entity and all other entities
+that control, are controlled by, or are under common control with that entity.
+For the purposes of this definition, "control" means (i) the power, direct or
+indirect, to cause the direction or management of such entity, whether by
+contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
+outstanding shares, or (iii) beneficial ownership of such entity.
+
+"You" (or "Your") shall mean an individual or Legal Entity exercising
+permissions granted by this License.
+
+"Source" form shall mean the preferred form for making modifications, including
+but not limited to software source code, documentation source, and configuration
+files.
+
+"Object" form shall mean any form resulting from mechanical transformation or
+translation of a Source form, including but not limited to compiled object code,
+generated documentation, and conversions to other media types.
+
+"Work" shall mean the work of authorship, whether in Source or Object form, made
+available under the License, as indicated by a copyright notice that is included
+in or attached to the work (an example is provided in the Appendix below).
+
+"Derivative Works" shall mean any work, whether in Source or Object form, that
+is based on (or derived from) the Work and for which the editorial revisions,
+annotations, elaborations, or other modifications represent, as a whole, an
+original work of authorship. For the purposes of this License, Derivative Works
+shall not include works that remain separable from, or merely link (or bind by
+name) to the interfaces of, the Work and Derivative Works thereof.
+
+"Contribution" shall mean any work of authorship, including the original version
+of the Work and any modifications or additions to that Work or Derivative Works
+thereof, that is intentionally submitted to Licensor for inclusion in the Work
+by the copyright owner or by an individual or Legal Entity authorized to submit
+on behalf of the copyright owner. For the purposes of this definition,
+"submitted" means any form of electronic, verbal, or written communication sent
+to the Licensor or its representatives, including but not limited to
+communication on electronic mailing lists, source code control systems, and
+issue tracking systems that are managed by, or on behalf of, the Licensor for
+the purpose of discussing and improving the Work, but excluding communication
+that is conspicuously marked or otherwise designated in writing by the copyright
+owner as "Not a Contribution."
+
+"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
+of whom a Contribution has been received by Licensor and subsequently
+incorporated within the Work.
+
+2. Grant of Copyright License.
+
+Subject to the terms and conditions of this License, each Contributor hereby
+grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
+irrevocable copyright license to reproduce, prepare Derivative Works of,
+publicly display, publicly perform, sublicense, and distribute the Work and such
+Derivative Works in Source or Object form.
+
+3. Grant of Patent License.
+
+Subject to the terms and conditions of this License, each Contributor hereby
+grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
+irrevocable (except as stated in this section) patent license to make, have
+made, use, offer to sell, sell, import, and otherwise transfer the Work, where
+such license applies only to those patent claims licensable by such Contributor
+that are necessarily infringed by their Contribution(s) alone or by combination
+of their Contribution(s) with the Work to which such Contribution(s) was
+submitted. If You institute patent litigation against any entity (including a
+cross-claim or counterclaim in a lawsuit) alleging that the Work or a
+Contribution incorporated within the Work constitutes direct or contributory
+patent infringement, then any patent licenses granted to You under this License
+for that Work shall terminate as of the date such litigation is filed.
+
+4. Redistribution.
+
+You may reproduce and distribute copies of the Work or Derivative Works thereof
+in any medium, with or without modifications, and in Source or Object form,
+provided that You meet the following conditions:
+
+You must give any other recipients of the Work or Derivative Works a copy of
+this License; and
+You must cause any modified files to carry prominent notices stating that You
+changed the files; and
+You must retain, in the Source form of any Derivative Works that You distribute,
+all copyright, patent, trademark, and attribution notices from the Source form
+of the Work, excluding those notices that do not pertain to any part of the
+Derivative Works; and
+If the Work includes a "NOTICE" text file as part of its distribution, then any
+Derivative Works that You distribute must include a readable copy of the
+attribution notices contained within such NOTICE file, excluding those notices
+that do not pertain to any part of the Derivative Works, in at least one of the
+following places: within a NOTICE text file distributed as part of the
+Derivative Works; within the Source form or documentation, if provided along
+with the Derivative Works; or, within a display generated by the Derivative
+Works, if and wherever such third-party notices normally appear. The contents of
+the NOTICE file are for informational purposes only and do not modify the
+License. You may add Your own attribution notices within Derivative Works that
+You distribute, alongside or as an addendum to the NOTICE text from the Work,
+provided that such additional attribution notices cannot be construed as
+modifying the License.
+You may add Your own copyright statement to Your modifications and may provide
+additional or different license terms and conditions for use, reproduction, or
+distribution of Your modifications, or for any such Derivative Works as a whole,
+provided Your use, reproduction, and distribution of the Work otherwise complies
+with the conditions stated in this License.
+
+5. Submission of Contributions.
+
+Unless You explicitly state otherwise, any Contribution intentionally submitted
+for inclusion in the Work by You to the Licensor shall be under the terms and
+conditions of this License, without any additional terms or conditions.
+Notwithstanding the above, nothing herein shall supersede or modify the terms of
+any separate license agreement you may have executed with Licensor regarding
+such Contributions.
+
+6. Trademarks.
+
+This License does not grant permission to use the trade names, trademarks,
+service marks, or product names of the Licensor, except as required for
+reasonable and customary use in describing the origin of the Work and
+reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty.
+
+Unless required by applicable law or agreed to in writing, Licensor provides the
+Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
+including, without limitation, any warranties or conditions of TITLE,
+NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
+solely responsible for determining the appropriateness of using or
+redistributing the Work and assume any risks associated with Your exercise of
+permissions under this License.
+
+8. Limitation of Liability.
+
+In no event and under no legal theory, whether in tort (including negligence),
+contract, or otherwise, unless required by applicable law (such as deliberate
+and grossly negligent acts) or agreed to in writing, shall any Contributor be
+liable to You for damages, including any direct, indirect, special, incidental,
+or consequential damages of any character arising as a result of this License or
+out of the use or inability to use the Work (including but not limited to
+damages for loss of goodwill, work stoppage, computer failure or malfunction, or
+any and all other commercial damages or losses), even if such Contributor has
+been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability.
+
+While redistributing the Work or Derivative Works thereof, You may choose to
+offer, and charge a fee for, acceptance of support, warranty, indemnity, or
+other liability obligations and/or rights consistent with this License. However,
+in accepting such obligations, You may act only on Your own behalf and on Your
+sole responsibility, not on behalf of any other Contributor, and only if You
+agree to indemnify, defend, and hold each Contributor harmless for any liability
+incurred by, or claims asserted against, such Contributor by reason of your
+accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work
+
+To apply the Apache License to your work, attach the following boilerplate
+notice, with the fields enclosed by brackets "[]" replaced with your own
+identifying information. (Don't include the brackets!) The text should be
+enclosed in the appropriate comment syntax for the file format. We also
+recommend that a file or class name and description of purpose be included on
+the same "printed page" as the copyright notice for easier identification within
+third-party archives.
+
+ Copyright [2015] [Coron]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..be33df4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,78 @@
+### tools Introduction
+
+ * autopatch
+ Flyme changes will be merged into the vendor model tool for the first time to
+ perform `patchall` or late upgrade `upgrade`.
+
+ * bootimgpack
+ Pack or unpack boot.img/recovery.img tool.
+
+ * common
+ Some common tools.
+
+ * config
+ `makeconfig` or `flyme config` to execute the call of the tools used to create the configuration.
+
+ * formatters
+ Formatted text, such as deleting `smali` in `.line`, or `id` and `name` conversion.
+
+ * helpdoc
+ Help document, and return the cause of the error when the program executes an error.
+
+ * reverses
+ Pack or unpack apk/jar and unpack the block package and odex2dex tool.
+
+ * smaliparser
+ smali file analyzer, used to deal with the smali file to better patch.
+
+ * su-tools
+ Root permissions with `adb pull` or `adb push` can facilitate the push file to the phone system.
+
+ * workflow
+ Workflow configuration tool.
+
+ * aapt adb fastboot zipalign
+ From the corresponding Android version of the AOSP tools.
+
+ * otadiff
+ Scripts used to generate differential packages.
+
+-------------------------------------------------------------------------------
+
+### tools 介绍
+
+ * autopatch
+ 将Flyme的改动合并到厂商机型的工具,用于首次执行`patchall`或后期升级`upgrade`.
+
+ * bootimgpack
+ boot.img/recovery.img解包打包工具.
+
+ * common
+ 一些共用工具.
+
+ * config
+ 执行`makeconfig`或`flyme config`时调用的工具,用于创建配置.
+
+ * formatters
+ 格式化文本,例如删`smali`中的`.line`,或`id`与`name`的转换.
+
+ * helpdoc
+ 帮助文档,在程序执行错误时返回可能导致错误的原因.
+
+ * reverses
+ 对apk/jar解包打包,对block包进行解包,odex合并工具.
+
+ * smaliparser
+ smali文件分析器,用于处理smali文件以更好的插桩.
+
+ * su-tools
+ 拥有root权限的`adb pull/push 可以方便的push文件到手机系统.
+
+ * workflow
+ 工作流配置工具.
+
+ * aapt adb fastboot zipalign
+ 来自于对应安卓版本的aosp工具.
+
+ * otadiff
+ 用于生成差分包的脚本.
diff --git a/__init__.py b/__init__.py
new file mode 100755
index 0000000..e69de29
diff --git a/aapt b/aapt
new file mode 100755
index 0000000..149d149
Binary files /dev/null and b/aapt differ
diff --git a/adb b/adb
new file mode 100755
index 0000000..8251ad0
Binary files /dev/null and b/adb differ
diff --git a/apktool b/apktool
new file mode 120000
index 0000000..d67901f
--- /dev/null
+++ b/apktool
@@ -0,0 +1 @@
+reverses/apktool.sh
\ No newline at end of file
diff --git a/autopatch/__init__.py b/autopatch/__init__.py
new file mode 100755
index 0000000..e69de29
diff --git a/autopatch/autopatch.py b/autopatch/autopatch.py
new file mode 100755
index 0000000..7e0f9f5
--- /dev/null
+++ b/autopatch/autopatch.py
@@ -0,0 +1,323 @@
+#!/usr/bin/python
+# Filename: autopatch.py
+
+"""
+Patch the files automatically based on the autopatch.xsd.
+
+Usage: $autopatch.py [OPTIONS]
+ OPTIONS:
+ --patchall, -p : Patch all the changes
+ --upgrade, -u : Patch the upgrade changes
+ --porting, -t : Porting changes from the other device
+
+ Loosely, make sure you have prepared the autopatch directory by your self
+ --loosely, -l : Not prepare autopatch/
+"""
+
+__author__ = 'duanqz@gmail.com'
+
+
+
+import shutil
+import os, sys
+import fnmatch
+import traceback
+import precondition
+import rejector
+
+from diff_patch import DiffPatch
+from target_finder import TargetFinder
+from config import Config
+from error import Error
+
+from formatters.format import Format
+from formatters.log import Paint
+
+
+try:
+ import xml.etree.cElementTree as ET
+except ImportError:
+ import xml.etree.ElementTree as ET
+
+
+TAG="autopatch"
+
+
+class AutoPatch:
+
+ def __init__(self, targetRoot, olderRoot, newerRoot, patchXML):
+ AutoPatch.TARGET_ROOT = targetRoot
+ AutoPatch.OLDER_ROOT = olderRoot
+ AutoPatch.NEWER_ROOT = newerRoot
+ AutoPatch.PATCH_XML = patchXML
+
+
+ def run(self):
+ # Parse out the PATCH_XML
+ AutoPatchXML().parse()
+
+ Error.report()
+
+# End of class AutoPatch
+
+
+class AutoPatchXML:
+ """ Represent the tree model of the patch XML.
+ """
+
+ def parse(self):
+ """ Parse the XML with the schema defined in autopatch.xsd
+ """
+
+ XMLDom = ET.parse(AutoPatch.PATCH_XML)
+
+ for feature in XMLDom.findall('feature'):
+ self.handleRevise(feature)
+
+
+ def handleRevise(self, feature):
+ """ Parse the revise node to handle the revise action.
+ """
+
+ description = feature.attrib['description']
+
+ if len(feature.getchildren()) == 0:
+ print "\n No changes between %s and %s, nothing to patch." % (AutoPatch.OLDER_ROOT, AutoPatch.NEWER_ROOT)
+ else:
+ print "\n [%s]" % description
+ for revise in feature:
+ ReviseExecutor(revise).run()
+
+# End of class AutoPatchXML
+
+
+class ReviseExecutor:
+ """ Execute revise action to a unique file.
+ Actions including ADD, MERGE, REPLACE.
+ """
+
+ ADD = "ADD"
+ MERGE = "MERGE"
+ DELETE = "DELETE"
+ REPLACE = "REPLACE"
+
+ TARGET_FINDER = TargetFinder()
+
+ def __init__(self, revise):
+ """ @args revise: the revise XML node.
+ """
+
+ self.action = revise.attrib['action']
+
+ # Compose the source and target file path
+ target = revise.attrib['target']
+ self.mTarget = target
+ self.mOlder = os.path.join(AutoPatch.OLDER_ROOT, target)
+ self.mNewer = os.path.join(AutoPatch.NEWER_ROOT, target)
+
+
+ def run(self):
+ if os.path.isfile(self.mNewer) or os.path.isfile(self.mOlder):
+ self.singleAction(self.mTarget, self.mOlder, self.mNewer)
+
+ elif os.path.isdir(self.mNewer): self.handleDirectory(self.mNewer)
+ elif os.path.isdir(self.mOlder): self.handleDirectory(self.mOlder)
+
+ elif self.mNewer.endswith("*"): self.handleRegex(self.mNewer)
+ elif self.mOlder.endswith("*"): self.handleRegex(self.mOlder)
+
+ else:
+ print Paint.red(" Can not handle: %s" % self.mTarget)
+
+
+ def handleDirectory(self, directory):
+ """ Handle target is a directory
+ """
+
+ if directory.startswith(AutoPatch.OLDER_ROOT):
+ relpathStart = AutoPatch.OLDER_ROOT
+ elif directory.startswith(AutoPatch.NEWER_ROOT):
+ relpathStart = AutoPatch.NEWER_ROOT
+
+ for (dirpath, dirnames, filenames) in os.walk(directory):
+
+ dirnames = dirnames # No use, just avoid of warning
+
+ for filename in filenames:
+ path = os.path.join(dirpath, filename)
+
+ target = os.path.relpath(path, relpathStart)
+ older = os.path.join(AutoPatch.OLDER_ROOT, target)
+ newer = os.path.join(AutoPatch.NEWER_ROOT, target)
+
+ self.singleAction(target, older, newer)
+
+
+ def handleRegex(self, regex):
+ """ Handle target ends with *
+ """
+
+ targetdir = os.path.dirname(self.mTarget)
+ olderdir = os.path.dirname(self.mOlder)
+ newerdir = os.path.dirname(self.mNewer)
+
+ regexdir = os.path.dirname(regex)
+ regexbase = os.path.basename(regex)
+
+ # Match the filename in the directory
+ for filename in os.listdir(regexdir):
+ if fnmatch.fnmatch(filename, regexbase):
+ target = os.path.join(targetdir, filename)
+ older = os.path.join(olderdir, filename)
+ newer = os.path.join(newerdir, filename)
+
+ self.singleAction(target, older, newer)
+
+
+ @staticmethod
+ def bakupTarget(target, dst):
+ if not os.path.exists(target):
+ return
+
+ dstTarget = os.path.join(dst, target)
+ if os.path.exists(dstTarget):
+ os.remove(dstTarget)
+
+ dirname = os.path.dirname(dstTarget)
+ if not os.path.exists(dirname):
+ os.makedirs(dirname)
+
+ shutil.copy(target, dstTarget)
+
+
+ def singleAction(self, target, older, newer):
+ """ action for a single file
+ """
+
+ ReviseExecutor.bakupTarget(target, Config.VENDOR_ORIGINAL_ROOT)
+
+ try:
+ if self.action == ReviseExecutor.ADD: result = ReviseExecutor.singleReplaceOrAdd(target, newer)
+ elif self.action == ReviseExecutor.MERGE: result = ReviseExecutor.singleMerge(target, older, newer)
+ elif self.action == ReviseExecutor.DELETE: result = ReviseExecutor.singleDelete(target)
+ elif self.action == ReviseExecutor.REPLACE: result = ReviseExecutor.singleReplaceOrAdd(target, newer)
+
+ print result
+ except:
+ Error.fail(" * Failed to %s %s" % (self.action, target))
+ traceback.print_exc()
+
+ ReviseExecutor.bakupTarget(target, Config.VENDOR_PATCHED_ROOT)
+
+
+ @staticmethod
+ def singleReplaceOrAdd(target, source):
+ """ Add a file from source to target.
+ Replace the target if exist.
+ """
+
+ # Find out the actual target
+ target = ReviseExecutor.TARGET_FINDER.find(target)
+
+ if os.path.exists(target):
+ execute = "REPLACE " + target
+ else:
+ execute = " ADD " + target
+ ReviseExecutor.createIfNotExist(os.path.dirname(target))
+
+ if not os.path.exists(source):
+ Error.fileNotFound(source)
+
+ return "%s %s" % (Paint.red(" [FAIL]"), execute)
+
+ # Only format access method and res id
+ action = Format.RESID_TO_NAME
+ formatSource = Format(AutoPatch.NEWER_ROOT, source).do(action)
+ formatTarget = Format(AutoPatch.TARGET_ROOT, target).do(action)
+
+ shutil.copy(source, target)
+
+ # Would not change res name back
+ formatSource.undo(action)
+ formatTarget.undo(action)
+
+ return "%s %s" % (Paint.green(" [PASS]"), execute)
+
+
+ @staticmethod
+ def singleMerge(target, older, newer):
+ """ Incorporate changes from older to newer into target
+ """
+
+ # Find out the actual target loosely
+ target = ReviseExecutor.TARGET_FINDER.find(target, loosely=True)
+
+ execute = " MERGE " + target
+
+ if not os.path.exists(target):
+ Error.fileNotFound(target)
+ return "%s %s" % (Paint.red(" [FAIL]"), execute)
+
+ action = Format.REMOVE_LINE | Format.RESID_TO_NAME
+ formatTarget = Format(AutoPatch.TARGET_ROOT, target).do(action)
+ formatOlder = Format(AutoPatch.OLDER_ROOT, older).do(action)
+ formatNewer = Format(AutoPatch.NEWER_ROOT, newer).do(action)
+
+ DiffPatch(target, older, newer).run()
+
+ # Would not change res name back
+ action = Format.REMOVE_LINE
+ formatTarget.undo(action)
+ formatOlder.undo(action)
+ formatNewer.undo(action)
+
+ conflictNum = rejector.process_conflicts(target)
+
+ if conflictNum > 0 :
+ Error.conflict(conflictNum, target)
+ return "%s %s" % (Paint.yellow(" [CFLT]"), execute)
+ else:
+ return "%s %s" % (Paint.green(" [PASS]"), execute)
+
+
+ @staticmethod
+ def singleDelete(target):
+ """ delete the target
+ """
+
+ # Find out the actual target
+ target = ReviseExecutor.TARGET_FINDER.find(target)
+
+ execute = " DELETE " + target
+
+ if os.path.exists(target):
+ os.remove(target)
+ return "%s %s" % (Paint.green(" [PASS]"), execute)
+
+ return "%s %s" % (Paint.red(" [FAIL]"), execute)
+
+
+ @staticmethod
+ def createIfNotExist(dirname):
+ if not os.path.exists(dirname):
+ os.makedirs(dirname)
+
+
+# End of class ReviseExecutor
+
+
+def main(argv):
+ argc = len(argv)
+ if argc <= 1:
+ print __doc__
+ sys.exit(1)
+
+ options = precondition.OPTIONS.handle(argv)
+
+ if options.prepare: precondition.Prepare()
+
+ AutoPatch(Config.PRJ_ROOT, options.olderRoot, options.newerRoot, options.patchXml).run()
+
+if __name__ == "__main__":
+ main(sys.argv)
+
diff --git a/autopatch/autopatch.xsd b/autopatch/autopatch.xsd
new file mode 100644
index 0000000..933c4ab
--- /dev/null
+++ b/autopatch/autopatch.xsd
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/autopatch/changelist.py b/autopatch/changelist.py
new file mode 100755
index 0000000..59092e6
--- /dev/null
+++ b/autopatch/changelist.py
@@ -0,0 +1,185 @@
+#!/usr/bin/python
+# Filename: changelist.py
+
+
+"""
+Usage: $shell changelist.py [OPTIONS]
+
+ OPTIONS:
+ --make : Make the change list out
+ --show : Show the change list out
+"""
+
+__author__ = 'duanqz@gmail.com'
+
+
+import shutil
+import os, sys
+import subprocess
+import tempfile
+
+import xml.dom.minidom
+
+from config import Config
+from formatters.log import Log
+
+
+try:
+ import xml.etree.cElementTree as ET
+except ImportError:
+ import xml.etree.ElementTree as ET
+
+#from lxml import etree as ET
+
+TAG="changelist"
+
+class ChangeList:
+
+
+ def __init__(self, olderRoot, newerRoot, patchXML):
+ ChangeList.OLDER_ROOT = olderRoot
+ ChangeList.NEWER_ROOT = newerRoot
+ ChangeList.PATCH_XML = patchXML
+
+ def make(self, force=True):
+ """ Generate the change list into XML.
+ Set force as False not to generate again if exists.
+ """
+
+ if not force and os.path.exists(ChangeList.PATCH_XML):
+ Log.d(TAG, "Using the existing %s" % ChangeList.PATCH_XML)
+ return True
+
+ Log.i(TAG, "Generating %s" % ChangeList.PATCH_XML)
+ hasChange = ChangeList.XMLFromDiff()
+
+ return hasChange
+
+
+ @staticmethod
+ def XMLFromDiff():
+ (dom, feature) = ChangeList.createXML()
+
+ tmp = ChangeList.fuse()
+
+ hasChange = False
+
+ for (dirpath, dirnames, filenames) in os.walk(tmp):
+
+ dirnames = dirnames # No use, just avoid of warning
+
+ for filename in filenames:
+ path = os.path.join(dirpath, filename)
+
+ target = os.path.relpath(path, tmp)
+ older = os.path.join(ChangeList.OLDER_ROOT, target)
+ newer = os.path.join(ChangeList.NEWER_ROOT, target)
+
+ olderExists = os.path.exists(older)
+ newerExists = os.path.exists(newer)
+ if olderExists and newerExists:
+ subp = subprocess.Popen(["diff", older, newer], stdout=subprocess.PIPE)
+ subp.communicate()
+ # 0 if inputs are the same
+ # 1 if different
+ # 2 if trouble, we do not handle this case
+ if subp.returncode == 1:
+ ChangeList.appendReivse(dom, feature, "MERGE", target)
+ hasChange = True
+ elif olderExists:
+ ChangeList.appendReivse(dom, feature, "DELETE", target)
+ hasChange = True
+ elif newerExists:
+ ChangeList.appendReivse(dom, feature, "ADD", target)
+ hasChange = True
+
+ shutil.rmtree(tmp)
+
+ ChangeList.writeXML(dom)
+
+ return hasChange
+
+
+ @staticmethod
+ def fuse():
+ tmp = tempfile.mktemp()
+ os.makedirs(tmp)
+ for subdir in os.listdir(ChangeList.OLDER_ROOT):
+ src = os.path.join(ChangeList.OLDER_ROOT, subdir)
+ subprocess.Popen(["cp", "-frp", src, tmp], stdout=subprocess.PIPE).communicate()
+
+ for subdir in os.listdir(ChangeList.NEWER_ROOT):
+ src = os.path.join(ChangeList.NEWER_ROOT, subdir)
+ subprocess.Popen(["cp", "-frp", src, tmp], stdout=subprocess.PIPE).communicate()
+
+ return tmp
+
+
+ @staticmethod
+ def createXML():
+# root = ET.Element('features')
+# feature = ET.Element('feature',
+# {'description' : 'These files are diff from %s and %s' %(ChangeList.OLDER_ROOT, ChangeList.NEWER_ROOT)})
+# root.append(feature)
+
+ impl = xml.dom.minidom.getDOMImplementation()
+ dom = impl.createDocument(None, "features", None)
+ root = dom.documentElement
+ feature = dom.createElement("feature")
+ feature.setAttribute("description", "These files are diff from %s and %s" % (ChangeList.OLDER_ROOT, ChangeList.NEWER_ROOT))
+ root.appendChild(feature)
+
+ return (dom, feature)
+
+
+ @staticmethod
+ def writeXML(dom):
+# tree = ET.ElementTree(root)
+# tree.write(ChangeList.PATCH_XML, #pretty_print=True,
+# xml_declaration=True, encoding='utf-8')
+
+ f = open(ChangeList.PATCH_XML, 'w')
+ dom.writexml(f, addindent=' ', newl='\n', encoding='utf-8')
+ f.close()
+
+ Log.i(TAG, "%s is generated" % ChangeList.PATCH_XML)
+
+
+ @staticmethod
+ def appendReivse(dom, feature, action, target):
+# revise = ET.Element('revise',
+# {'action' : action,
+# 'target' : target})
+#
+ revise = dom.createElement("revise")
+ revise.setAttribute("action", action)
+ revise.setAttribute("target", target)
+
+ feature.appendChild(revise)
+
+
+ def show(self):
+ if not os.path.exists(ChangeList.PATCH_XML):
+ print ChangeList.PATCH_XML + " not exists. Use `make` to generate."
+ return
+
+ changelist = []
+
+ revises = ET.parse(ChangeList.PATCH_XML).findall("feature/revise")
+ for revise in revises:
+ target = revise.attrib["target"]
+ changelist.append(target)
+
+ print "\n".join(changelist)
+
+
+if __name__ == "__main__":
+ for arg in sys.argv:
+ if arg in ("--make", "-m"):
+ ChangeList(Config.AOSP_ROOT, Config.BOSP_ROOT, Config.PATCHALL_XML).make()
+ sys.exit(0)
+ elif arg in ("--show", "-s"):
+ ChangeList(Config.AOSP_ROOT, Config.BOSP_ROOT, Config.PATCHALL_XML).show()
+ sys.exit(0)
+
+ print __doc__
diff --git a/autopatch/config.py b/autopatch/config.py
new file mode 100755
index 0000000..1f6199e
--- /dev/null
+++ b/autopatch/config.py
@@ -0,0 +1,67 @@
+#!/usr/bin/python
+
+"""
+Configuration
+"""
+
+__author__ = 'duanqz@gmail.com'
+
+
+import os
+from os import sys, path
+
+sys.path.append(path.dirname(path.dirname(path.abspath(__file__))))
+
+class Config:
+ """ Configuration.
+ """
+
+### Root directory
+ # Root directory of current project
+ PRJ_ROOT = os.curdir
+
+### AUTOPATCH Directory
+ # We need to hold three directory because diff3
+ # incorporate changes from newer to older into target.
+
+ AUTOPATCH = os.path.join(PRJ_ROOT, "autopatch/")
+
+ # AOSP root directory
+ AOSP_ROOT = os.path.join(AUTOPATCH, "aosp/")
+
+ # BOSP root directory
+ BOSP_ROOT = os.path.join(AUTOPATCH, "bosp/")
+
+ # Last BOSP root directory
+ LAST_BOSP_ROOT = os.path.join(AUTOPATCH, "last_bosp/")
+
+ # Vendor original root directory
+ VENDOR_ORIGINAL_ROOT = os.path.join(AUTOPATCH, "vendor_original")
+
+ # Vendor patched root directory
+ VENDOR_PATCHED_ROOT = os.path.join(AUTOPATCH, "vendor_patched")
+
+ # Root directory of reject files
+ REJ_ROOT = os.path.join(AUTOPATCH, "reject/")
+
+### Patch XML
+ PATCHALL_XML = os.path.join(AUTOPATCH, "patchall.xml")
+
+ UPGRADE_XML = os.path.join(AUTOPATCH, "upgrade.xml")
+
+ PORTING_XML = os.path.join(AUTOPATCH, "porting.xml")
+
+ @staticmethod
+ def toString():
+ print "-----------------------------------------------------------"
+ print "PRJ_ROOT:\t" + Config.PRJ_ROOT
+ print "AOSP_DIR:\t" + Config.AOSP_ROOT
+ print "BOSP_DIR:\t" + Config.BOSP_ROOT
+ print "---"
+ print "PATCHALL_XML:\t" + Config.PATCHALL_XML
+ print "-----------------------------------------------------------"
+
+# End of class Config
+
+if __name__ == "__main__":
+ Config.toString()
diff --git a/autopatch/decode_ota.py b/autopatch/decode_ota.py
new file mode 100755
index 0000000..5dfb7e8
--- /dev/null
+++ b/autopatch/decode_ota.py
@@ -0,0 +1,21 @@
+#!/usr/bin/python
+
+"""
+Usage: $decode_ota.py ota.zip output
+ decode ota.zip to output
+"""
+
+__author__ = 'duanqz@gmail.com'
+
+
+import sys
+from precondition import Utils
+
+if __name__ == "__main__":
+
+ argc = len(sys.argv)
+
+ if argc < 3:
+ print __doc__
+
+ Utils.decode(sys.argv[1], sys.argv[2])
\ No newline at end of file
diff --git a/autopatch/diff_patch.py b/autopatch/diff_patch.py
new file mode 100755
index 0000000..f85cadb
--- /dev/null
+++ b/autopatch/diff_patch.py
@@ -0,0 +1,329 @@
+#!/usr/bin/python
+
+### File Information ###
+"""
+Incorporate changes from older to newer into target.
+"""
+
+__author__ = 'duanqz@gmail.com'
+
+
+TAG="diff_patch"
+
+import os
+import commands
+import shutil
+import fnmatch
+import tempfile
+
+from config import Config
+from smaliparser.Smali import Smali
+from formatters.log import Log
+
+
+class DiffPatch():
+
+ def __init__(self, target, older, newer):
+ self.mTarget = target
+ self.mOlder = older
+ self.mNewer = newer
+
+ def run(self):
+ if fnmatch.fnmatch(self.mTarget, "*.smali"):
+ self.diff3_smali()
+ else:
+ self.diff3_non_smali()
+
+ def diff3_non_smali(self):
+ """ diff3 for non SMALI file
+ """
+
+ DiffPatch.__diff3(self.mTarget, self.mOlder, self.mNewer)
+
+ def diff3_smali(self):
+ """ diff3 for SMALI file
+ Each file will be split to lots of parts, then using diff3 by each part,
+ at last, all the parts will be joined again.
+ """
+
+ # Acquire splitters
+ splitters = Splitters()
+ (target, older, newer) = splitters.aquire(self.mTarget, self.mOlder, self.mNewer)
+
+ # Delete the unnecessary part
+ # Here should use deep copy of the list, otherwise will cause leak deleting
+ for targetPart in target.getAllParts()[:]:
+ olderPart = older.match(targetPart)
+ newerPart = newer.match(targetPart)
+
+ # Case
+ # target older newer action
+ # o o x delete
+ # x o x ignore
+ # o x x ignore
+ # x x x ignore
+
+ olderExists = os.path.exists(olderPart)
+ newerExists = os.path.exists(newerPart)
+ if olderExists and (not newerExists):
+ target.deletePart(targetPart)
+
+
+ # Patch partitions one by one
+ for newerPart in newer.getAllParts():
+ targetPart = target.match(newerPart)
+ olderPart = older.match(newerPart)
+
+ # Case
+ # target older newer action
+ # o o o merge
+ # o x o replace
+ # x o o conflict
+ # x x o append
+
+ targetExists = os.path.exists(targetPart)
+ olderExists = os.path.exists(olderPart)
+ if targetExists and olderExists:
+ # all of items exist. (merge)
+ DiffPatch.__diff3(targetPart, olderPart, newerPart)
+ elif targetExists:
+ # newer already exist in target. (replace)
+ target.replacePart(targetPart, newerPart)
+ pass
+ elif olderExists:
+ # target might be removed by vendor. (conflict)
+ target.conflictPart(olderPart, newerPart)
+ pass
+ else:
+ # newer not exist in older and target. (append)
+ target.appendPart(newerPart)
+
+ # Release splitters
+ splitters.release()
+
+
+ @staticmethod
+ def __diff3(target, older, newer):
+ """ Incorporate changes from older to newer into target.
+ Return True if no conflicts, otherwise return False.
+ """
+
+ # Exit status is 0 if successful, 1 if conflicts, 2 if trouble
+ cmd = "diff3 -E -m -L VENDOR %s -L AOSP %s -L BOSP %s" % \
+ (commands.mkarg(target), commands.mkarg(older), commands.mkarg(newer))
+ (status, output) = commands.getstatusoutput(cmd)
+
+ # Append "\n" to output for shell invoking result will miss it
+ output += "\n"
+
+ if status != 2:
+ # Write back the patched file
+ targetFile = open(target, "wb")
+ if status != 0:
+ output = DiffPatch.__markConflictMethod(output)
+ targetFile.write(output)
+ targetFile.close()
+
+ # status is 0 if successful
+ return status == 0
+
+
+ @staticmethod
+ def __markConflictMethod(output):
+ """ Mark out the method name in conflict part.
+ """
+
+ subStart = output.find(".method")
+ subEnd = output.find("(")
+ if subStart >= 0:
+ methodName = output[subStart:subEnd]
+ output = output.replace("=======", "======= #@%s@" %methodName)
+
+ return output
+
+
+###
+### SMALI Splitter
+###
+
+class Splitters:
+ """ Holder of all the SMALI splitters.
+ """
+
+ TMP = tempfile.mktemp()
+ TARGET_OUT = os.path.join(TMP, "target")
+ OLDER_OUT = os.path.join(TMP, "older")
+ NEWER_OUT = os.path.join(TMP, "newer")
+
+ def aquire(self, target, older, newer):
+
+ # Prepare temporary directory
+ for tmpDir in (Splitters.TARGET_OUT, Splitters.OLDER_OUT, Splitters.NEWER_OUT):
+ if not os.path.exists(tmpDir): os.makedirs(tmpDir)
+
+ # Split the target, older and newer
+ self.targetSplitter = SmaliSplitter().split(target, Splitters.TARGET_OUT)
+ self.olderSplitter = SmaliSplitter().split(older, Splitters.OLDER_OUT)
+ self.newerSplitter = SmaliSplitter().split(newer, Splitters.NEWER_OUT)
+
+ return (self.targetSplitter, self.olderSplitter, self.newerSplitter)
+
+ def release(self):
+ # Join the partitions
+ self.targetSplitter.join()
+
+ # Remove temporary directory
+ shutil.rmtree(Splitters.TMP)
+
+
+class SmaliSplitter:
+ """ Independent splitter of SMALI file
+ """
+
+ def split(self, origSmali, output=None):
+ """ Split the original SMALI file into partitions
+ """
+
+ if output == None: output = os.path.dirname(origSmali)
+
+ self.mOrigSmali = origSmali
+ self.mOutput = output
+ self.mPartList = Smali(origSmali).split(self.mOutput)
+
+ return self
+
+ def match(self, part):
+ basename = os.path.basename(part)
+ return os.path.join(self.mOutput, basename)
+
+ def getAllParts(self):
+ return self.mPartList
+
+ def appendPart(self, part):
+ """ Append a part to list if not exist
+ """
+
+ try:
+ self.mPartList.index(part)
+ except:
+ Log.d(TAG, " [Add new part %s ] " % part)
+ self.mPartList.append(part)
+
+ def deletePart(self, part):
+ """ Delete a part
+ """
+
+ try:
+ self.mPartList.remove(part)
+ Log.d(TAG, " [Delete part %s ] " % part)
+ except:
+ Log.e(TAG, "SmaliSpliiter.deltePart(): can not find part %s" % part)
+
+ def replacePart(self, targetPart, newerPart):
+ """ Replace the target with the newer.
+ """
+
+ try:
+ index = self.mPartList.index(targetPart)
+ self.mPartList[index] = newerPart
+ Log.d(TAG, " [Replace %s by %s] " % (targetPart, newerPart))
+ except:
+ Log.e(TAG, "SmaliSplitter.replacePart() can not find part %s" % targetPart)
+
+ def conflictPart(self, olderPart, newerPart):
+ """ If older and newer are the same content, no conflict happen. Otherwise, mark out conflict.
+ """
+
+ # Get older part content
+ olderHandle = open(olderPart, "rb")
+ olderContent = olderHandle.read()
+ olderHandle.close()
+
+ # Get newer part content
+ newerHandle = open(newerPart, "r+")
+ newerContent = newerHandle.read()
+
+ # Compare older and newer content
+ if olderContent == newerContent:
+ # No need to handle access any more
+ # # BOSP has no change on AOSP.
+ # # Still handle this case: "access$" method
+ # if newerPart.find("access$") >= 0:
+ # Log.d(TAG, " [Might useful access part %s ] " % newerPart)
+ #
+ # lines = []
+ # lines.append("\n# Remove the first '#' if you want to enable this method. It might be invoked from codes of BOSP.\n")
+ # for line in newerContent.splitlines():
+ # if len(line) > 0: line = "#%s\n" % line
+ # lines.append(line)
+ #
+ # newerHandle.seek(0)
+ # newerHandle.truncate()
+ # newerHandle.writelines(lines)
+ # newerHandle.close()
+ # self.mPartList.append(newerPart)
+ # else:
+ # newerHandle.close()
+ newerHandle.close()
+ else:
+ # BOSP has changes on AOSP.
+
+ # Conflict happened
+ Log.d(TAG, " [Conflict part %s ] " % newerPart)
+
+ # Mark out the conflict
+ newerContent = "\n<<<<<<< VENDOR\n=======%s\n>>>>>>> BOSP\n" % newerContent
+ newerHandle.seek(0)
+ newerHandle.truncate()
+ newerHandle.write(newerContent)
+ newerHandle.close()
+ self.mPartList.append(newerPart)
+
+
+ def join(self):
+ """ Join all the partitions.
+ """
+
+ newSmali = open(self.mOrigSmali, "wb")
+
+ # Write back the part by sequence
+ for part in self.mPartList:
+ if not os.path.exists(part):
+ continue
+
+ partHandle = open(part ,"rb")
+ newSmali.write(partHandle.read())
+ partHandle.close()
+
+ newSmali.close()
+
+ return self
+
+
+
+def test():
+ print "Result\t%s" % Config.PRJ_ROOT
+ print "------ ---------------------------------------------------"
+
+ # Test for XML
+ f = "framework-res/AndroidManifest.xml"
+ target = os.path.join(Config.PRJ_ROOT, f)
+ older = os.path.join(Config.AOSP_ROOT, f)
+ newer = os.path.join(Config.BOSP_ROOT, f)
+
+ print DiffPatch(target, older, newer).run(),
+ print "\t" + f
+
+ # Test for SMALI
+ f = "framework.jar.out/smali/android/content/res/AssetManager.smali"
+ target = os.path.join(Config.PRJ_ROOT, f)
+ older = os.path.join(Config.AOSP_ROOT, f)
+ newer = os.path.join(Config.BOSP_ROOT, f)
+
+ print DiffPatch(target, older, newer).run(),
+ print "\t" + f
+
+
+
+if __name__ == "__main__":
+ test()
diff --git a/autopatch/error.py b/autopatch/error.py
new file mode 100755
index 0000000..bbe115c
--- /dev/null
+++ b/autopatch/error.py
@@ -0,0 +1,114 @@
+#!/usr/bin/python
+
+
+__author__ = 'duanqz@gmail.com'
+
+
+import os
+from config import Config
+from formatters.log import Paint
+
+class Error:
+
+ FAILED_LIST = []
+
+ TOTAL_FILENOTFOUND = 0
+ FILENOTFOUND_STATISTIC = {}
+
+ TOTAL_CONFLICTS = 0
+ CONFLICT_FILE_NUM = 0
+ CONFLICT_STATISTIC = {}
+
+
+ @staticmethod
+ def fileNotFound(target):
+ # Add to FAILED_LIST
+ Error.fail(" * File not found: %s" % target)
+
+ # Increment total file not found
+ Error.TOTAL_FILENOTFOUND += 1
+
+ # Increment file not found of each part
+ key = Error.getStatisticKey(target)
+ if not Error.FILENOTFOUND_STATISTIC.has_key(key):
+ Error.FILENOTFOUND_STATISTIC[key] = 0
+
+ Error.FILENOTFOUND_STATISTIC[key] += 1
+
+
+ @staticmethod
+ def conflict(conflictNum, target):
+ # Add to FAILED_LIST
+ Error.fail(" %3d conflicts in: %s" %(conflictNum, target))
+
+ # Increment total conflicts
+ Error.TOTAL_CONFLICTS += conflictNum;
+ Error.CONFLICT_FILE_NUM += 1
+
+ # Increment conflicts of each part
+ key = Error.getStatisticKey(target)
+ if not Error.CONFLICT_STATISTIC.has_key(key):
+ Error.CONFLICT_STATISTIC[key] = 0
+
+ Error.CONFLICT_STATISTIC[key] += conflictNum
+
+ @staticmethod
+ def getStatisticKey(target):
+ relpath = os.path.relpath(target, Config.PRJ_ROOT)
+ key = relpath[0:relpath.find("/")]
+
+ return key
+
+ @staticmethod
+ def fail(message):
+ Error.FAILED_LIST.append(message)
+
+
+ @staticmethod
+ def showStatistic():
+ for key in Error.FILENOTFOUND_STATISTIC.keys():
+ print "%3d files not found in %s" % (Error.FILENOTFOUND_STATISTIC[key], key)
+
+ if Error.TOTAL_FILENOTFOUND > 0:
+ print Paint.bold("%3d files not found totally, they might be removed or moved to other place by vendor?" % Error.TOTAL_FILENOTFOUND)
+ print " "
+
+ for key in Error.CONFLICT_STATISTIC.keys():
+ print "%3d conflicts in %s " % (Error.CONFLICT_STATISTIC[key], key)
+
+ if Error.TOTAL_CONFLICTS > 0:
+ print Paint.bold("%3d conflicts in %d files, go through the reject files in 'autopatch/reject' to find them out" % (Error.TOTAL_CONFLICTS, Error.CONFLICT_FILE_NUM))
+ print Paint.red("\n Ask for advice? Please type 'flyme help CONFLICTS_HAPPENED' \n")
+
+
+ @staticmethod
+ def report():
+
+ if len(Error.FAILED_LIST) == 0:
+ print Paint.green("\n Ask for advice? Please type 'flyme help NO_CONFLICT' \n")
+ else:
+ Error.createAllRejects()
+
+ Error.printDivide()
+ for failed in Error.FAILED_LIST: print failed
+
+ Error.printDivide()
+ Error.showStatistic()
+
+
+ @staticmethod
+ def printDivide():
+ print " ____________________________________________________________________________________"
+ print " "
+
+
+ @staticmethod
+ def createAllRejects():
+ if not os.path.exists(Config.REJ_ROOT):
+ os.makedirs(Config.REJ_ROOT)
+ allrejects = os.path.join(Config.REJ_ROOT, "allrejects.txt")
+ handle = open(allrejects, "w")
+ handle.write("\n".join(Error.FAILED_LIST))
+ handle.close()
+
+
diff --git a/autopatch/precondition.py b/autopatch/precondition.py
new file mode 100755
index 0000000..7404fd5
--- /dev/null
+++ b/autopatch/precondition.py
@@ -0,0 +1,713 @@
+#!/usr/bin/python
+
+"""
+Usage: $shell precondition.py [OPTIONS]
+ OPTIONS:
+ --patchall, -p : preparation for patchall
+ --upgrade, -u : preparation for upgrade
+ --porting, -t : preparation for porting
+"""
+
+__author__ = 'duanqz@gmail.com'
+
+
+
+import shutil
+import subprocess
+import os, sys
+import commands
+import getopt
+from config import Config
+from changelist import ChangeList
+from formatters.log import Log, Paint
+from reverses.zipformatter import ZipFormatter
+
+
+
+TAG="precondition"
+
+FRAMEWORK_JARS = ("framework.jar", "services.jar", "telephony-common.jar", "wifi-service.jar", "android.policy.jar",
+ "secondary-framework.jar", "secondary_framework.jar",
+ "framework-ext.jar", "framework_ext.jar",
+ "framework2.jar",
+ "mediatek-framework.jar")
+
+PARTITIONS = ("secondary_framework.jar.out", "secondary-framework.jar.out",
+ "framework-ext.jar.out", "framework_ext.jar.out",
+ "framework2.jar.out")
+
+
+USELESS_DIRS = ("framework.jar.out/smali/com/flyme", "framework.jar.out/smali/com/meizu",
+ "framework.jar.out/smali/flyme", "framework.jar.out/smali/meizu",
+ "framework.jar.out/original",
+ "services.jar.out/original",
+ "telephony-common.jar.out/original",
+ "wifi-service.jar.out/original",
+ "android.policy.jar.out/original",
+ "framework-res/original")
+
+
+class Options(object):
+
+ def __init__(self):
+ self.prepare = True # Whether to trigger prepare action
+ self.patchXml = Config.PATCHALL_XML # The patch XML, default to be PATCHALL_XML
+ self.baseName = "base" # Short base device name, default to be 'base'
+ self.commit1 = None # The 7 bits lower commit ID
+ self.commit2 = None # The 7 bits upper commit ID
+ self.olderRoot = None # The older and newer is a pair of directory for comparing
+ self.newerRoot = None # Default to be AOSP and BOSP
+
+ @staticmethod
+ def usage():
+ print __doc__
+ sys.exit()
+
+ def handle(self, argv):
+ if len(argv) == 1: Options.usage()
+
+ try:
+ (opts, args) = getopt.getopt(argv[1:], "hlputb:c1:c2:", \
+ [ "help", "loosely", "patchall", "upgrade", "porting", "base=", "commit1=", "commit2=" ])
+ Log.d(TAG, "Program args = %s" %args)
+ except getopt.GetoptError:
+ Options.usage()
+
+ for name, value in opts:
+ if name in ("--help", "-h"):
+ Options.usage()
+
+ elif name in ("--loosely", "-l"):
+ self.prepare = False
+
+ elif name in ("--patchall", "-p"):
+ self.patchXml = Config.PATCHALL_XML
+ self.olderRoot = Config.AOSP_ROOT
+ self.newerRoot = Config.BOSP_ROOT
+
+ elif name in ("--upgrade", "-u"):
+ self.patchXml = Config.UPGRADE_XML
+ self.olderRoot = Config.LAST_BOSP_ROOT
+ self.newerRoot = Config.BOSP_ROOT
+
+ elif name in ("--porting", "-t"):
+ # The older and newer root are generated by the commit1 and commit2
+ self.patchXml = Config.PORTING_XML
+
+ elif name in ("--base", "-b"):
+ if len(value) > 0: self.baseName = value
+
+ elif name in ("--commit1", "-1"):
+ if len(value) > 0: self.commit1 = value
+
+ elif name in ("--commit2", "-2"):
+ if len(value) > 0: self.commit2 = value
+
+ self.dump()
+
+ return self
+
+
+ def dump(self):
+ Log.d(TAG, "prepare = %s" %str(self.prepare))
+ Log.d(TAG, "baseName = %s, patchXml = %s, olderRoot = %s, newerRoot = %s, commit1 = %s, commit2 = %s"
+ %(self.baseName, self.patchXml, self.olderRoot, self.newerRoot, self.commit1, self.commit2))
+
+
+# Global OPTIONS
+OPTIONS = Options()
+
+class Prepare:
+
+ def __init__(self):
+ """ baseName is the short device name
+ """
+
+ Log.i(TAG, "Start preparing essential files in %s" %Config.AUTOPATCH)
+
+ self.baseDevice = BaseDevice(OPTIONS.baseName)
+ if OPTIONS.patchXml == Config.PATCHALL_XML: self.patchall()
+ elif OPTIONS.patchXml == Config.UPGRADE_XML: self.upgrade()
+ elif OPTIONS.patchXml == Config.PORTING_XML: self.porting()
+
+
+ def patchall(self):
+ """ Prepare precondition of patchall
+ """
+
+ # Phase 1: prepare AOSP
+ self.baseDevice.aosp(OPTIONS.olderRoot)
+
+ # Phase 2: prepare BOSP
+ self.baseDevice.bosp(OPTIONS.newerRoot)
+
+ # Phase 3: record last head
+ self.baseDevice.setLastHead()
+
+ # Phase 4: prepare patch XML, not force
+ ChangeList(OPTIONS.olderRoot, OPTIONS.newerRoot, OPTIONS.patchXml).make(force=False)
+
+
+ def upgrade(self):
+ """ Prepare precondition of upgrade
+ """
+
+ # Remove last_bosp and bosp
+ Utils.run(["rm", "-rf", OPTIONS.olderRoot], stdout=subprocess.PIPE).communicate()
+ Utils.run(["rm", "-rf", OPTIONS.newerRoot], stdout=subprocess.PIPE).communicate()
+
+ lastBoardZip = os.path.join(Config.PRJ_ROOT, "board/last_board.zip")
+ boardZip = os.path.join(Config.PRJ_ROOT, "board/board.zip")
+
+ if os.path.exists(lastBoardZip) and os.path.exists(boardZip):
+ # Phase 1: prepare LAST_BOSP from last_board.zip
+ Utils.decode(lastBoardZip, OPTIONS.olderRoot)
+
+ # Phase 2: prepare BOSP from board.zip
+ Utils.decode(boardZip, OPTIONS.newerRoot)
+
+ # Phase 3: prepare patch XML
+ ChangeList(OPTIONS.olderRoot, OPTIONS.newerRoot, OPTIONS.patchXml).make(force=True)
+
+ else:
+ if OPTIONS.commit1 is not None:
+ self.baseDevice.setLastHead(OPTIONS.commit1)
+
+ # Phase 1: get last and origin head from base device
+ (lastHead, origHead) = self.baseDevice.getLastAndOrigHead()
+ if lastHead == origHead:
+ Log.w(TAG, Paint.red("Nothing to upgrade. Did you forget to sync the %s ?" % OPTIONS.baseName))
+ Log.w(TAG, Paint.red("In the root directory, try the following command to sync:"))
+ Log.w(TAG, Paint.red(" $ repo sync devices/%s" % OPTIONS.baseName))
+
+ # Phase 2: porting from last to origin head
+ OPTIONS.commit1 = lastHead[0:7]
+ OPTIONS.commit2 = origHead[0:7]
+ self.porting()
+
+
+ def porting(self):
+ """ Porting changes from commit1 to commit2
+ commit is 7 bits
+ """
+
+ # Phase 1: get the lower and upper commit
+ commitModel = CommitModel(self.baseDevice)
+ (lowerCommit, upperCommit) = commitModel.getCommitRange(OPTIONS.commit1, OPTIONS.commit2)
+ Log.d(TAG, "Porting.prepare(). lowerCommit = %s, upperCommit = %s" %(lowerCommit, upperCommit))
+
+ # Phase 2: Prepare the older and newer root
+ if OPTIONS.patchXml == Config.PORTING_XML:
+ OPTIONS.newerRoot = os.path.join(Config.AUTOPATCH, "%s_newer_%s" % (self.baseDevice.name(), upperCommit))
+ OPTIONS.olderRoot = os.path.join(Config.AUTOPATCH, "%s_older_%s" % (self.baseDevice.name(), lowerCommit))
+ filesChanged = self.baseDevice.getFilesChanged(lowerCommit, upperCommit)
+ self.baseDevice.portingFiles(upperCommit, filesChanged, OPTIONS.newerRoot)
+ self.baseDevice.portingFiles(lowerCommit, filesChanged, OPTIONS.olderRoot)
+
+ # Phase 3: Restore the commit model
+ commitModel.restore()
+
+ # Phase 4: prepare patch XML
+ ChangeList(OPTIONS.olderRoot, OPTIONS.newerRoot, OPTIONS.patchXml).make(force=True)
+
+
+
+class CommitModel:
+ """ The Commits Model of a device
+ """
+
+ def __init__(self, baseDevice):
+ """ Initialize the commits model from a device path.
+ """
+
+ self.baseDevice = baseDevice
+ self.commitIDs = []
+ self.comments = []
+
+ baseDevice.fillCommitsInfo(self.commitIDs, self.comments)
+
+
+ def showUserInputHint(self):
+ """ Show user input hint
+ """
+
+ for i in range(0, len(self.commitIDs)):
+ commitID = self.commitIDs[i]
+ comment = self.comments[i]
+ print " %s %s" % (Paint.bold(commitID), comment)
+
+ deviceName = self.baseDevice.name()
+ oneCommit = Paint.bold(self.commitIDs[0])
+ twoCommits = "%s %s" % (Paint.bold(self.commitIDs[-1]), Paint.bold(self.commitIDs[0]))
+ print " ________________________________________________________________________________________"
+ print " "
+ print " Each 7 bits SHA1 code identify a commit on %s," % Paint.blue(deviceName),
+ print " You could input: "
+ print " - Only one single commit, like: %s" % oneCommit
+ print " will porting changes between the selected and the latest from %s to your device" % Paint.blue(deviceName)
+ print " - Two commits as a range, like: %s" % twoCommits
+ print " will porting changes between the two selected from %s to your device" % Paint.blue(deviceName)
+ print " ________________________________________________________________________________________"
+ print " "
+
+
+ def readUserInput(self):
+ """ Read user input
+ """
+
+ self.showUserInputHint()
+
+ userInput = raw_input(Paint.bold(">>> Input the 7 bits SHA1 commit ID (q to exit): "))
+ if userInput in ("q", "Q"): sys.exit()
+
+ commits = userInput.split()
+ size = len(commits)
+ commit1 = commit2 = None
+ if size > 0 : commit1 = commits[0]
+ if size > 1 : commit2 = commits[1]
+
+ return (commit1, commit2)
+
+
+ def computeLowerAndUpper(self, commit1, commit2=None):
+ """ Retrieve the lower and upper commit ID
+ """
+
+ if commit2 == None: commit2 = commit1
+
+ try: index1 = self.commitIDs.index(commit1)
+ except: index1 = 0
+
+ try: index2 = self.commitIDs.index(commit2)
+ except: index2 = 0
+
+ if index1 == index2:
+ upper = 0
+ lower = index1
+ elif index1 < index2:
+ upper = index1
+ lower = index2
+ else:
+ upper = index2
+ lower = index1
+
+ lowerCommit = self.commitIDs[lower]
+ upperCommit = self.commitIDs[upper]
+
+ return (lowerCommit, upperCommit)
+
+
+ def getCommitRange(self, commit1, commit2):
+ """ Get the range of commit1 and commit2
+ """
+
+ # If no commit present, ask for user input
+ if commit1 == None and commit2 == None:
+ (commit1, commit2) = self.readUserInput()
+
+ return self.computeLowerAndUpper(commit1, commit2)
+
+
+ def restore(self):
+ """ Restore the base device
+ """
+
+ self.baseDevice.reset(self.commitIDs[0])
+
+# End of class PortingDevice
+
+class BaseDevice:
+
+ def __init__(self, baseName):
+ self.baseName = baseName
+ self.basePath = BaseDevice.getBasePath(baseName)
+ self.targetPath = os.path.abspath(Config.PRJ_ROOT)
+
+ targetName = os.path.basename(self.targetPath)
+ self.lastHeadPath = os.path.join(self.basePath, ".git/%s:LAST_HEAD" %targetName)
+ self.origHeadPath = os.path.join(self.basePath, ".git/%s:ORIG_HEAD" %targetName)
+
+ def name(self):
+ return self.baseName
+
+ @staticmethod
+ def getBasePath(base):
+ try:
+ devices = os.path.join(os.environ["PORT_ROOT"], "devices")
+ return os.path.join(devices, base)
+ except KeyError:
+ Log.e(TAG, "device %s not found" % base)
+ sys.exit(155)
+
+
+ def setLastHead(self, commit=None):
+ """ Set last head of input device
+ """
+
+ if commit == None: commit = self.parseHeadCommit()
+ BaseDevice.writeCommit(self.lastHeadPath, commit)
+
+
+ def getLastAndOrigHead(self):
+ """ Get the last head and the origin head of input device.
+ """
+
+ # If last or orig head not exists, write the current head into them.
+ head = self.parseHeadCommit()
+ if not os.path.exists(self.lastHeadPath): BaseDevice.writeCommit(self.lastHeadPath, head)
+ if not os.path.exists(self.origHeadPath): BaseDevice.writeCommit(self.origHeadPath, head)
+
+ # Check whether need to update the lastHead and origHead
+ oldOrigHead = BaseDevice.readCommit(self.origHeadPath)
+ newOrigHead = head
+
+ if oldOrigHead == newOrigHead:
+ Log.d(TAG, "BaseDevice.getLastAndOrigHead(). oldOrig == newOrig")
+ pass
+ else:
+ Log.d(TAG, "BaseDevice.getLastAndOrigHead(). oldOrig -> LAST_HEAD, newOrig -> ORIG_HEAD")
+ BaseDevice.writeCommit(self.lastHeadPath, oldOrigHead)
+ BaseDevice.writeCommit(self.origHeadPath, newOrigHead)
+
+ lastHead = BaseDevice.readCommit(self.lastHeadPath)
+ origHead = BaseDevice.readCommit(self.origHeadPath)
+ Log.d(TAG, "BaseDevice.getLastAndOrigHead(). lastHead = %s, origHead = %s" %(lastHead, origHead))
+
+ return (lastHead, origHead)
+
+
+ def parseHeadCommit(self):
+ """ Parse out current head commit
+ """
+
+ os.chdir(self.basePath)
+
+ subp = Utils.run(["git", "rev-parse", "HEAD"], stdout=subprocess.PIPE)
+
+ os.chdir(self.targetPath)
+
+ return subp.stdout.read().strip("\n")
+
+
+ def fillCommitsInfo(self, commitIDs, comments):
+ """ Fill the commitIDs and comments
+ commitIDs is an array of 7 bits SHA1 code.
+ comment is an array of string.
+ """
+
+ os.chdir(self.basePath)
+
+ subp = Utils.run(["git", "log", "--oneline"], stdout=subprocess.PIPE)
+ while True:
+ buff = subp.stdout.readline().strip('\n')
+ if buff == '' and subp.poll() != None:
+ break
+
+ buff = buff.strip()
+ # The first 7 bits is commit ID
+ commitIDs.append(buff[0:7])
+ comments.append(buff[7:])
+
+ os.chdir(self.targetPath)
+
+
+ def reset(self, commit):
+ """ Reset to commit
+ """
+
+ os.chdir(self.basePath)
+
+ subp = Utils.run(["git", "reset", "--hard", commit], stdout=subprocess.PIPE)
+ subp.communicate()
+
+ os.chdir(self.targetPath)
+
+
+ def aosp(self, aospDst):
+ """ Prepare AOSP to asopDst
+ """
+
+ aospSrc = os.path.join(self.basePath, "vendor/aosp")
+ # If no AOSP under vendor/ , decode them out
+ if not os.path.exists(aospSrc):
+ os.makedirs(aospSrc)
+ vendorRoot = os.path.join(self.basePath, "vendor")
+ Utils.decodeAPKandJAR(vendorRoot, aospSrc)
+
+ if not os.path.exists(aospDst):
+ Log.i(TAG, "Generating %s from %s" % (aospDst, aospSrc))
+ Utils.copyAPKandJAR(aospSrc, aospDst)
+
+
+ def bosp(self, bospDst, force=True):
+ """ Prepare BOSP, set force to be False to not generate again if exists.
+ """
+
+ if force:
+ subp = Utils.run(["rm", "-rf", bospDst], stdout=subprocess.PIPE)
+ subp.communicate()
+
+ Log.i(TAG, "Generating %s from %s" %(bospDst, self.basePath))
+ Utils.copyAPKandJAR(self.basePath, bospDst)
+
+
+ def getFilesChanged(self, lowerCommit, upperCommit):
+ """ Get files changed from lower to upper commit
+ """
+
+ os.chdir(self.basePath)
+
+ changes = []
+ subp = Utils.run(["git", "diff", "--name-only", lowerCommit, upperCommit], stdout=subprocess.PIPE)
+ while True:
+ buff = subp.stdout.readline().strip('\n')
+ if buff == '' and subp.poll() != None:
+ break
+
+ changes.append(buff.strip())
+
+ os.chdir(self.targetPath)
+
+ return changes
+
+
+ def portingFiles(self, commit, filesChanged, dstDir):
+ """ Generate patch files for porting
+ """
+
+ # Reset to the commit
+ self.reset(commit)
+
+ Log.i(TAG, "Generating %s from %s at commit %s" % (dstDir, self.basePath, commit))
+ if not os.path.exists(dstDir): os.makedirs(dstDir)
+
+ # Copy changed items from source
+ for item in filesChanged:
+ src = os.path.join(self.basePath, item)
+ dst = os.path.join(dstDir, item)
+ if os.path.exists(src):
+
+ # Only copy files in FRAMEWORK_JARS
+ if not Utils.isInFramework(item): continue
+
+ # We need to format the SMALI file.
+ # Copy all the sub classes even if no change.
+ Utils.copyWholly(src, os.path.dirname(dst))
+
+ Utils.combineFrameworkPartitions(dstDir)
+
+
+ @staticmethod
+ def readCommit(filepath):
+ handle = open(filepath, "r")
+ content = handle.read().strip("\n")
+ handle.close()
+
+ return content
+
+ @staticmethod
+ def writeCommit(filepath, commit):
+ handle = open(filepath, "w")
+ handle.write(commit)
+ handle.close()
+
+
+class Utils:
+ """ Utilities
+ """
+
+ @staticmethod
+ def decode(boardZip, out):
+ """ Decode FRAMEWORK_JARS in board.zip into out directory.
+ """
+
+ Log.i(TAG, "Generating %s from %s" %(out, boardZip))
+
+ # Phase 1: normalize
+ temp = Utils.otaNormalize(boardZip)
+ if temp == None:
+ Log.e(TAG, "decode(): ota normalized failed")
+ return
+
+ if not os.path.exists(out): os.makedirs(out)
+
+ Utils.decodeAPKandJAR(temp, out)
+
+ shutil.rmtree(temp)
+
+ # Phase 3: combine framework partitions
+ Utils.combineFrameworkPartitions(out)
+
+ # Phase 4: remove useless files
+ Utils.removeUseless(out)
+
+
+ @staticmethod
+ def decodeAPKandJAR(root, out):
+ # Format path
+ if os.path.exists(os.path.join(root, "SYSTEM")):
+ shutil.move(os.path.join(root, "SYSTEM"), os.path.join(root, "system"))
+
+ dirname = os.path.join(root, "system/framework")
+
+ Log.i(TAG, "decoding framework-res.apk")
+ jarpath = os.path.join(dirname, "framework-res.apk")
+ jarout = os.path.join(out, "framework-res")
+ subp = Utils.run(["apktool", "d", "-f", jarpath, "-o", jarout], stdout=subprocess.PIPE)
+ Utils.printSubprocessOut(subp)
+
+ for jarname in FRAMEWORK_JARS:
+ jarpath = os.path.join(dirname, jarname)
+ if os.path.exists(jarpath):
+ Log.i(TAG, "decoding %s" % jarname)
+ jarout = os.path.join(out, jarname + ".out")
+ subp = Utils.run(["apktool", "d", "-f", jarpath, "-o", jarout], stdout=subprocess.PIPE)
+ Utils.printSubprocessOut(subp)
+
+
+ @staticmethod
+ def otaNormalize(boardZip):
+ """ Normalize the OTA package zip, return the root directory of unziped files
+ """
+
+ if not os.path.exists(boardZip):
+ Log.e(TAG, "oatNormalize() % not exists" % boardZip)
+ return None
+
+ zipFormatter = ZipFormatter.create(ZipFormatter.genOptions(boardZip))
+ # Do not need to zip back
+ zipFormatter.format(zipBack=False)
+
+ root = zipFormatter.getFilesRoot()
+ if not os.path.exists(root):
+ Log.e(TAG, "otaNormalize() normalize %s failed!" % boardZip)
+ return None
+
+ return root
+
+
+ @staticmethod
+ def copyAPKandJAR(src, dst):
+ if not os.path.exists(dst):
+ os.makedirs(dst)
+
+ bootImage = os.path.join(src, "boot.img.out")
+ subp = Utils.run(["cp", "-r", bootImage, dst], stdout=subprocess.PIPE)
+ subp.communicate()
+
+ frwRes = os.path.join(src, "framework-res")
+ subp = Utils.run(["cp", "-r", frwRes, dst], stdout=subprocess.PIPE)
+ subp.communicate()
+
+ for jarname in FRAMEWORK_JARS:
+ jarname += ".out"
+ srcJar = os.path.join(src, jarname)
+ if os.path.exists(srcJar):
+ Log.d(TAG, "Utils.copyAPKandJAR(). copying %s to %s" %(srcJar, dst))
+ subp = Utils.run(["cp", "-r", srcJar, dst], stdout=subprocess.PIPE)
+ subp.communicate()
+
+ Utils.combineFrameworkPartitions(dst)
+
+
+ @staticmethod
+ def combineFrameworkPartitions(frameworkDir):
+ """ Combine framework partitions into framework.jar.out.
+ """
+
+ # For Android 5.0, handle dex partitions.
+ dst = os.path.join(frameworkDir, "framework.jar.out", "smali")
+ partitionPath = os.path.join(frameworkDir, "framework.jar.out", "smali_classes2")
+ if os.path.exists(partitionPath):
+ Log.i(TAG, "Combine %s into framework.jar.out/smali" % partitionPath)
+ for subDir in os.listdir(partitionPath):
+ src = os.path.join(partitionPath, subDir)
+ Utils.run(["cp", "-r", src, dst], stdout=subprocess.PIPE).communicate()
+
+ shutil.rmtree(partitionPath)
+
+
+ # For Android Handle framework partitions
+ for partition in PARTITIONS:
+ if partition == "framework.jar.out": continue
+
+ partitionPath = os.path.join(frameworkDir, partition)
+ if os.path.exists(partitionPath):
+ Log.i(TAG, "Combine %s into framework.jar.out" % partition)
+ src = os.path.join(partitionPath, "smali")
+ dst = os.path.join(frameworkDir, "framework.jar.out")
+ Utils.run(["cp", "-r", src, dst], stdout=subprocess.PIPE).communicate()
+ shutil.rmtree(partitionPath)
+
+
+ @staticmethod
+ def removeUseless(frameworkDir):
+ """ Remove useless directory
+ """
+
+ for uselessDir in USELESS_DIRS:
+ dst = os.path.join(frameworkDir, uselessDir)
+ if os.path.exists(dst):
+ shutil.rmtree(dst)
+
+
+ @staticmethod
+ def isInFramework(filepath):
+ """ Is the file path in jars defined in FRAMEWORK_JARS
+ """
+
+ relRoot = filepath.split("/")[0]
+ result = relRoot[0:-4] in FRAMEWORK_JARS
+ result |= (relRoot == "framework-res")
+ Log.d(TAG, "Utils.isInFramework(): %s, %s -> %s" %(result, filepath, relRoot))
+ return result
+
+
+ @staticmethod
+ def copyWholly(srcFilePath, dstDirname):
+ """ Copy whole SMALI files which are in the same JAVA file
+ Especially for the case of inner class, it has '$' in file path
+ """
+
+ if not os.path.exists(dstDirname): os.makedirs(dstDirname)
+
+ # Copy all the sub classes even if no change.
+ # We need to format the SMALI file
+ pos = srcFilePath.find("$")
+ if pos > 0:
+ srcFilePath = srcFilePath[0:pos] + "*"
+ elif srcFilePath.endswith(".smali"):
+ srcFilePath = srcFilePath.rstrip(".smali") + "*"
+
+ # Note: Do not use commands.mkarg here
+ cmd = "cp %s %s" %(srcFilePath, dstDirname)
+ Log.d(TAG, "Utils.copyWholly(): %s" %cmd)
+ commands.getstatusoutput(cmd)
+
+
+
+ @staticmethod
+ def run(args, **kwargs):
+ """Create and return a subprocess.Popen object, printing the command
+ line on the terminal
+ """
+
+ return subprocess.Popen(args, **kwargs)
+
+
+ @staticmethod
+ def printSubprocessOut(subp):
+ while True:
+ buff = subp.stdout.readline().strip('\n')
+ if buff == '' and subp.poll() != None:
+ break
+
+ Log.i(TAG, buff)
+
+# End of class Utils
+
+
+if __name__ == "__main__":
+ OPTIONS.handle(sys.argv)
+ if OPTIONS.prepare: Prepare()
+
diff --git a/autopatch/rejector.py b/autopatch/rejector.py
new file mode 100755
index 0000000..d27f066
--- /dev/null
+++ b/autopatch/rejector.py
@@ -0,0 +1,179 @@
+#!/usr/bin/python
+
+# Copyright 2015 Coron
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+"""
+rejector.
+
+"""
+
+__author__ = 'duanqz@gmail.com'
+
+
+import os
+import fnmatch
+from config import Config
+
+
+class Collector:
+ """ Collect the conflicts
+ """
+
+ CONFILCT_START = "<<<<<<<"
+ CONFLICT_MID = "======="
+ CONFILCT_END = ">>>>>>>"
+
+ def __init__(self, target):
+ self.mTarget = target
+ self.mConflictNum = 0
+
+ def get_conflict_num(self):
+ return self.mConflictNum
+
+ def collect_conflict(self):
+ """ Check whether conflict happen or not in the target
+ """
+
+ self.mConflictNum = 0
+
+ top = 0
+
+ # delLinesNumbers record the lines of conflicts
+ del_line_numbers = []
+ need_to_del = False
+
+ target_file = open(self.mTarget, "r+")
+
+ line_num = 0
+ lines = target_file.readlines()
+
+ for line in lines:
+ size = self.mConflictNum
+ if line.startswith(Collector.CONFILCT_START):
+
+ top += 1
+
+ # Modify the conflict in the original
+ lines[line_num] = "%s #Conflict %d\n" % (line.rstrip(), size)
+ self.mConflictNum += 1
+
+ del_line_numbers.append(line_num)
+
+ elif line.startswith(Collector.CONFILCT_END):
+
+ # Modify the conflict in the original
+ lines[line_num] = "%s #Conflict %d\n" % (line.rstrip(), size-top)
+
+ del_line_numbers.append(line_num)
+ need_to_del = False
+
+ if top == 0:
+ break
+
+ top -= 1
+
+ else:
+ if top > 0:
+
+ if line.startswith(Collector.CONFLICT_MID):
+ need_to_del = True
+
+ if need_to_del:
+ del_line_numbers.append(line_num)
+
+ line_num += 1
+
+ # Create a reject file if conflict happen
+ if self.mConflictNum > 0:
+ rej_file_path = create_reject_file(self.mTarget)
+ f = open(rej_file_path, "wb")
+ f.writelines(lines)
+ f.close()
+
+ # Remove conflict blocks, and write back target.
+ for line_num in del_line_numbers[::-1]:
+ del lines[line_num]
+
+ target_file.seek(0)
+ target_file.truncate()
+ target_file.writelines(lines)
+ target_file.close()
+
+ return self
+
+ # Deprecated
+ def resolve_conflict(self):
+
+ f = open(self.mTarget, "r+")
+
+ top = 0
+ line_num = 0
+
+ del_line_numbers = []
+ need_to_del = True
+
+ lines = f.readlines()
+ for line in lines:
+ if line.startswith(Collector.CONFILCT_START):
+ top += 1
+ del_line_numbers.append(line_num)
+
+ elif line.startswith(Collector.CONFILCT_END):
+ top -= 1
+ del_line_numbers.append(line_num)
+ need_to_del = True
+
+ if top < 0: break;
+ else:
+ if top > 0:
+ if need_to_del:
+ del_line_numbers.append(line_num)
+
+ if line.startswith(Collector.CONFLICT_MID):
+ need_to_del = False
+
+ line_num += 1
+
+ for line_num in del_line_numbers[::-1]:
+ del lines[line_num]
+
+ f.seek(0)
+ f.truncate()
+ f.writelines(lines)
+ f.close()
+
+
+def create_reject_file(target):
+ """ create a reject file
+ :param target: the target might have conflicts
+ :return: reject file path
+ """
+ rel_target = os.path.relpath(target, Config.PRJ_ROOT)
+ rej_file_path = os.path.join(Config.REJ_ROOT, rel_target)
+ d = os.path.dirname(rej_file_path)
+ if not os.path.exists(d):
+ os.makedirs(d)
+
+ return rej_file_path
+
+
+def process_conflicts(target):
+ """ process conflicts of the target
+ :param target: the target might have conflicts
+ :return: number of conflicts
+ """
+
+ return Collector(target).collect_conflict().get_conflict_num()
\ No newline at end of file
diff --git a/autopatch/target_finder.py b/autopatch/target_finder.py
new file mode 100755
index 0000000..f4c60be
--- /dev/null
+++ b/autopatch/target_finder.py
@@ -0,0 +1,196 @@
+#!/usr/bin/python
+# Filename: target_finder.py
+
+"""
+Fast search the target out.
+
+Usage: target_finder.py TARGET
+ - TARGET target path relative to current directory.
+"""
+
+__author__ = 'duanqz@gmail.com'
+
+
+import sys
+import re
+import os
+import fnmatch
+import commands
+
+class TargetFinder:
+
+ # The framework partitions
+ PARTITIONS = []
+
+ def __init__(self):
+ self.__initPartitions()
+
+ # Path Regex to match out useful parts
+ # Using named group match with "(?P)", using minimum match end with "?"
+ self.pathRegex = re.compile("(?P.*?)/(?Psmali/.*?)(?P.*)")
+
+ def __initPartitions(self):
+ """ Parse out the framework partitions.
+ """
+
+ makefile = None
+ for filename in os.listdir(os.curdir):
+ if fnmatch.fnmatch(filename.lower(), "makefile"):
+ makefile = filename
+
+ if makefile == None:
+ return
+
+ fileHandle = open(makefile, "r")
+ content = fileHandle.read()
+ modifyJars = re.compile("\n\s*vendor_modify_jars\s*:=\s*(?P.*)\n")
+ match = modifyJars.search(content)
+ if match != None:
+ TargetFinder.PARTITIONS = match.group("jars").split(" ")
+
+ fileHandle.close()
+
+
+ def __findInDexPartitions(self, target):
+ """ Find the target in dex partition.
+ On Android 5.0, Files might be split to different dex-partitions
+ """
+
+ if os.path.exists(target):
+ return target
+
+
+ (outClass, innerClass) = TargetFinder.__extractInnerClass(target)
+ match = self.pathRegex.search(outClass)
+ if match != None:
+ # Part 1: top directory of framework
+ # Part 2: smali or smali_classes2 ...
+ # Part 3: the remains of the path
+ part1 = match.group("part1")
+ part2 = match.group("part2")
+ part3 = match.group("part3")
+
+ if not os.path.exists(part1):
+ return target
+
+ for subDir in os.listdir(part1):
+ if subDir.startswith("smali") and subDir != part2:
+ newTarget = os.path.join(part1, subDir, part3)
+ if os.path.exists(newTarget):
+ return TargetFinder.__concatInnerClass(newTarget, innerClass)
+
+ # Not found
+ return target
+
+
+
+ def __findInFrwPartitions(self, target):
+ """ Find the target in the partitions.
+ Files might be split to different framework-partition
+ """
+
+ (outClass, innerClass) = TargetFinder.__extractInnerClass(target)
+
+ match = self.pathRegex.search(outClass)
+ if match != None:
+ # Part 1: top directory of framework
+ # Part 2: smali or smali_classes2 ...
+ # Part 3: the remains of the path
+ part1 = match.group("part1")
+ part2 = match.group("part2")
+ part3 = match.group("part3")
+
+ for partition in TargetFinder.PARTITIONS:
+ if not partition.endswith(".jar.out"):
+ partition += ".jar.out"
+
+ newTarget = os.path.join(partition, part2, part3)
+ if os.path.exists(newTarget):
+ return TargetFinder.__concatInnerClass(outClass, innerClass)
+
+
+ # Not found
+ return target
+
+
+ @staticmethod
+ def __extractInnerClass(target):
+ """ Extract the inner class file from target
+ """
+
+ pos = target.find("$")
+ if pos >= 0:
+ # Inner class, set outer class as new target to find
+ outClass = target[:pos] + ".smali"
+ innerClass = target[pos:]
+ return (outClass, innerClass)
+ else:
+ return (target, None)
+
+
+ @staticmethod
+ def __concatInnerClass(outClass, innerClass):
+ if innerClass != None:
+ return outClass.replace(".smali", innerClass)
+ else:
+ return outClass
+
+
+
+ def __findInAll(self, target):
+ """ Find the target in all project root
+ """
+
+ basename = os.path.basename(target)
+ searchPath = []
+ for partition in TargetFinder.PARTITIONS:
+ if not partition.endswith(".jar.out"):
+ partition += ".jar.out"
+ searchPath.append(partition)
+
+ cmd = "find %s -name %s" % (" ".join(searchPath), commands.mkarg(basename))
+ (sts, text) = commands.getstatusoutput(cmd)
+ try:
+ if sts == 0:
+ text = text.split("\n")[0]
+ if len(text) > 0:
+ return text
+ except:
+ pass
+
+ return target
+
+
+ def find(self, target, loosely=False):
+ """ Find the target out in the current directory.
+ Set loosely to be True to find file base name in all directory
+ """
+
+ # Firstly, check whether target exists in dex partitions
+ target = self.__findInDexPartitions(target)
+ if os.path.exists(target):
+ return target
+
+ # Secondly, check whether target exists in framework partitions
+ # It is more efficiently than find in all files
+ target = self.__findInFrwPartitions(target)
+ if os.path.exists(target):
+ return target
+
+ # Thirdly, still not find the target, search in all sub directories
+ if loosely:
+ return self.__findInAll(target)
+ else:
+ return target
+
+
+# End of class TargetFinder
+
+if __name__ == "__main__":
+ argc = len(sys.argv)
+ if argc != 2 :
+ print __doc__
+ sys.exit()
+
+ target = sys.argv[1]
+ print TargetFinder().find(target)
diff --git a/baksmali b/baksmali
new file mode 120000
index 0000000..8acd0b7
--- /dev/null
+++ b/baksmali
@@ -0,0 +1 @@
+reverses/de-odex/baksmali.sh
\ No newline at end of file
diff --git a/bootimgpack/.gitignore b/bootimgpack/.gitignore
new file mode 100644
index 0000000..49542df
--- /dev/null
+++ b/bootimgpack/.gitignore
@@ -0,0 +1,25 @@
+# Gradle
+.gradle/
+bin/
+
+# Build
+**/build/
+*.pyc
+
+# Eclipse
+**/nbproject/private/
+*.project
+*.classpath
+*.settings
+*.setting
+
+# Tmp Files
+*.kate-swp
+*~
+
+# IntelliJ
+*.iml
+.idea/*
+/out
+
+
diff --git a/bootimgpack/CONTRIBUTORS.md b/bootimgpack/CONTRIBUTORS.md
new file mode 100644
index 0000000..1367837
--- /dev/null
+++ b/bootimgpack/CONTRIBUTORS.md
@@ -0,0 +1,7 @@
+# bootimgpack Contributors
+Copyright 2015 duanqz
+
+This product includes software developed by:
+
+ * duanqz (duanqz@gmail.com)
+ * The Apache Software Foundation (http://www.apache.org/)
diff --git a/bootimgpack/LICENSE b/bootimgpack/LICENSE
new file mode 100644
index 0000000..c910663
--- /dev/null
+++ b/bootimgpack/LICENSE
@@ -0,0 +1,210 @@
+Sub projects brut.apktool, brut.j.common, brut.dir and brut.j.util are
+released under the following license:
+
+*******************************************************************************
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*******************************************************************************
+
+The sub project (brut.apktool.smali) is a clone of the smali project. Its
+license can be found at brut.apktool.smali/NOTICE-SMALI
\ No newline at end of file
diff --git a/bootimgpack/README.md b/bootimgpack/README.md
new file mode 100644
index 0000000..5e119f5
--- /dev/null
+++ b/bootimgpack/README.md
@@ -0,0 +1,71 @@
+### 介绍
+
+***bootimgpack*** 是一个智能解包和打包 Android boot.img 的工具,
+采用 Python 和 Shell 语言实现, 目前仅支持类 Unix 系统.
+
+### 特点
+
+ * 自适应不同手机厂商的 boot.img 格式,智能完成解包
+ * 提供命令和图形界面两种操作方式
+
+### 使用
+
+ * 命令方式
+
+ ***解包:*** unpack_bootimg.py boot.img output/
+
+ ***打包:*** pack_bootimg.py BOOT/ boot.img
+
+ * 图形界面
+
+ 运行 ui/main.py 将会启动操作界面
+
+### 类型
+
+目前,bootimgpack支持大部分厂商的boot.img类型:
+
+ * SONY (Sony的boot.img类型)
+ * MTK (MTK的boot.img类型)
+ * QCOM (高通的boot.img类型,包括dt.img)
+ * COMMON-V1 (Android 4.3+的boot.img类型)
+ * COMMON (Android 2.3~4.3的boot.img类型)
+
+### 版本
+
+v1.0
+
+-------------------------------------------------------------------------------
+
+### Introduction
+
+***bootimgpack*** is a smart tool for unpack or pack boot.img of Android,
+implemented in Python and Shell, now only for Unix-like OS.
+
+### Features
+ * Hide format differences of boot.img of different manufactors
+ * Both command and graphic mode are provided
+
+### Usages
+ * Command Mode
+
+ ***Unpack :*** unpack_bootimg.py boot.img output/
+
+ ***Pack :*** pack_bootimg.py BOOT/ boot.img
+
+ * Graphic Mode
+
+ Run ui/main.py to launch the UI
+
+### Types
+
+Bootimgpack support for a broad range types of boot.img including:
+
+ * SONY (Support boot.img of Sony)
+ * MTK (Support boot.img of MTK 2.3~4.2)
+ * QCOM (Support boot.img of QCOM 4.3+, especially for dt.img of QCOM)
+ * COMMON-V1 (Support boot.img of Android 4.3+)
+ * COMMON (Support boot.img of Android 2.3 ~ Android 4.2)
+
+### Version
+
+V1.0
diff --git a/bootimgpack/__init__.py b/bootimgpack/__init__.py
new file mode 100755
index 0000000..e69de29
diff --git a/bootimgpack/flash.py b/bootimgpack/flash.py
new file mode 100755
index 0000000..f673b8e
--- /dev/null
+++ b/bootimgpack/flash.py
@@ -0,0 +1,31 @@
+#!/usr/bin/python
+'''
+USAGE: flash device recovery.fstab device.img [fstab_version]
+ device: [boot|recovery]
+ device.img: [boot.img|recovery.img]
+ fstab_version: [1|2]
+ 1 for android 4.0 - 4.2 (DEFAULT)
+ 2 for android 4.3 - *
+
+Created on Jul 31, 2014
+
+@author: tangliuxiang
+'''
+import sys
+import os
+import traceback
+from pull import utils
+
+if __name__ == '__main__':
+
+ try:
+ if len(sys.argv) == 4:
+ utils.PushUtils.push(sys.argv[1], sys.argv[2], sys.argv[3])
+ elif len(sys.argv) >= 5:
+ utils.PushUtils.push(sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4])
+ else:
+ print __doc__
+ except Exception as e:
+ traceback.print_exc()
+ # See help.xml ERR_PULL_BOOT_RECOVERY_FAILED
+ sys.exit(156)
\ No newline at end of file
diff --git a/bootimgpack/internal/__init__.py b/bootimgpack/internal/__init__.py
new file mode 100755
index 0000000..e69de29
diff --git a/bootimgpack/internal/bootimg.py b/bootimgpack/internal/bootimg.py
new file mode 100755
index 0000000..16e9d6a
--- /dev/null
+++ b/bootimgpack/internal/bootimg.py
@@ -0,0 +1,257 @@
+#!/usr/bin/env python
+
+# Copyright 2015 duanqz(duanqz@gmail.com)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+"""
+Unpack and pack boot.img Intelligently.
+"""
+
+__author__ = 'duanqz@gmail.com'
+
+
+import os
+import subprocess
+import commands
+import tempfile
+import time
+import signal
+import shutil
+
+try:
+ import xml.etree.cElementTree as ET
+except ImportError:
+ import xml.etree.ElementTree as ET
+
+DEBUG = False
+
+
+class Toolkit:
+ """ Toolkit including all tools
+ """
+
+ TOOLS_ROOT = os.path.dirname(os.path.abspath(__file__))
+ TOOLKIT_XML = os.path.join(TOOLS_ROOT, "toolkit.xml")
+ TYPE_CONFIG = "type.config"
+
+ def __init__(self):
+ """ Initialize tools factory from config.xml
+ """
+
+ self.allTools = {}
+ self.sequence = {}
+
+ tree = ET.parse(Toolkit.TOOLKIT_XML)
+ for tool in tree.findall("tool"):
+ seq = tool.attrib["seq"]
+ boot_type = tool.attrib["type"]
+ description = tool.attrib["description"]
+
+ unpack_tool = tool.find("unpack").text
+ pack_tool = tool.find("pack").text
+ self.allTools[boot_type] = { "UNPACK" : os.path.join(Toolkit.TOOLS_ROOT, unpack_tool),
+ "PACK" : os.path.join(Toolkit.TOOLS_ROOT, pack_tool) }
+ self.sequence[seq] = (boot_type, description)
+
+ def parse_boot_img_type(self, boot_img):
+ """ Match appropriate tools for the boot image file.
+ """
+
+ remain_seqs = sorted(self.sequence.keys())
+ # Try to unpack boot image for each type,
+ # choose the appropriate one.
+ print "Trying to unpack:"
+ for seq in sorted(self.sequence.keys()):
+
+ # Delete from remain_seqs
+ remain_seqs.remove(seq)
+
+ # Try to unpack the boot image by unpack tool
+ (boot_type, description) = self.sequence.get(seq)
+ unpack_tool = self.get_tools(boot_type, "UNPACK")
+ if Utils.try_unpack(unpack_tool, boot_img):
+ print " Succeed trying with %s(%s) " % (boot_type, description)
+ break
+ else:
+ print " Failed trying with %s(%s)" % (boot_type, description)
+
+ if len(remain_seqs) > 0:
+ print " "
+ print "You might manually try unpacking with the remaining if %s does not actually work:" % boot_type
+ for seq in remain_seqs:
+ (remain_boot_type, description) = self.sequence.get(seq)
+ print " %s(%s)" % (remain_boot_type, description)
+
+ return boot_type
+
+ def get_tools(self, boot_type, attrib=None):
+ """ Get tools by type of boot.img
+ """
+
+ tools = self.allTools.get(boot_type)
+ if attrib is None:
+ return tools
+ else:
+ return tools[attrib]
+
+
+class Utils:
+
+ def __init__(self):
+ pass
+
+ @staticmethod
+ def try_unpack(unpack_tool, boot_img):
+ """ Try to unpack the boot image into TEMP_DIR.
+ Return True: unpack successfully. False: otherwise.
+ """
+
+ tmp_dir = tempfile.mkdtemp()
+
+ cmd = "%s %s %s" % (unpack_tool, boot_img, tmp_dir)
+ p = subprocess.Popen(cmd, shell=True, preexec_fn=os.setsid, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+
+ timeout = 10
+ while True:
+ if p.poll() is not None:
+ status = p.poll()
+ break
+ timeout -= 1
+ time.sleep(0.5)
+ if timeout <= 0:
+ status = -1
+ os.killpg(p.pid, signal.SIGTERM)
+ break
+
+ (output, err) = p.communicate()
+
+ # Debug code. Useless for release version
+ Utils.__debug("\nTry: %s" % cmd)
+ Utils.__debug(output)
+
+ shutil.rmtree(tmp_dir)
+
+ return status == 0
+
+
+ @staticmethod
+ def format(boot_img):
+ """ Format boot_img
+ """
+
+ f = open(boot_img, "r+")
+ hstr = f.read(len("ANDROID"))
+
+ if hstr == "ANDROID":
+ return
+
+ f.seek(0)
+ content = f.read()
+
+ idx = content.find("ANDROID")
+ if idx >= 0:
+ f.seek(0)
+ f.truncate()
+ f.write(content[idx:])
+
+ f.close()
+
+ @staticmethod
+ def write_boot_img_type(boot_type, boot_dir):
+ """ Write boot_type into a type.config in boot_dir
+ """
+
+ f = open(os.path.join(boot_dir, Toolkit.TYPE_CONFIG), "w")
+ f.write(boot_type)
+ f.close()
+
+
+ @staticmethod
+ def read_boot_img_type(boot_dir):
+ """ Read boot_type from type.config in boot_dir
+ """
+
+ try:
+ f = open(os.path.join(boot_dir, Toolkit.TYPE_CONFIG), "r")
+ boot_type = f.read().rstrip()
+ f.close()
+ except IOError:
+ print "Can not find type.config, use COMMON as image type by default"
+ boot_type = "COMMON-V1"
+
+ return boot_type
+
+
+ @staticmethod
+ def __debug(msg):
+ if DEBUG:
+ print msg
+
+
+def unpack(boot_img, output):
+ """ Unpack the boot image into the output directory.
+ """
+
+ Utils.format(boot_img)
+
+ tool_kit = Toolkit()
+
+ # Try unpack tool set to find the suitable one
+ boot_type = tool_kit.parse_boot_img_type(boot_img)
+
+ # Check whether the tools exists
+ if boot_type is None:
+ raise ValueError("Unknown boot image type: " + boot_img)
+
+ # Execute the unpack command
+ unpack_tool = tool_kit.get_tools(boot_type, "UNPACK")
+ cmd = "%s %s %s" % (commands.mkarg(unpack_tool), commands.mkarg(boot_img), commands.mkarg(output))
+ (status, result) = commands.getstatusoutput(cmd)
+
+ if status != 0:
+ print "\n* Unpack failed"
+ print result
+ else:
+ print "\n* Unpack %s %s --> %s" % (boot_type, boot_img, output)
+
+ # Store the used tools to output
+ Utils.write_boot_img_type(boot_type, output)
+
+
+def pack(boot_dir, output):
+ """ Pack the BOOT directory into the output image
+ """
+
+ # Retrieve the last used tools from boot directory
+ boot_type = Utils.read_boot_img_type(boot_dir)
+
+ # Check whether the tools exists
+ if boot_type is None:
+ raise ValueError("Unknown boot image type.")
+
+ # Execute the pack command
+ pack_tool = Toolkit().get_tools(boot_type, "PACK")
+ cmd = "%s %s %s" % (commands.mkarg(pack_tool), commands.mkarg(boot_dir), commands.mkarg(output))
+ (status, result) = commands.getstatusoutput(cmd)
+
+ if status != 0:
+ print "Pack failed"
+ print result
+ else:
+ print "Pack %s %s --> %s" % (boot_type, boot_dir, output)
+
+
+if __name__ == '__main__':
+ pass
\ No newline at end of file
diff --git a/bootimgpack/internal/common-v1/README.md b/bootimgpack/internal/common-v1/README.md
new file mode 100644
index 0000000..00f9f6a
--- /dev/null
+++ b/bootimgpack/internal/common-v1/README.md
@@ -0,0 +1,8 @@
+abootimg is an open source project on https://github.com/coruus/abootimg
+
+Extension of abootimg, LZ4 - Extremely fast compression: https://github.com/Cyan4973/lz4
+From Android 5.0, lz4 compression instead of gzip for many devices.
+
+Current version of LZ4 used here is [r128].
+
+Support Android 4.3+
diff --git a/bootimgpack/internal/common-v1/abootimg b/bootimgpack/internal/common-v1/abootimg
new file mode 100755
index 0000000..f0716f6
Binary files /dev/null and b/bootimgpack/internal/common-v1/abootimg differ
diff --git a/bootimgpack/internal/common-v1/abootimg-pack-initrd b/bootimgpack/internal/common-v1/abootimg-pack-initrd
new file mode 100755
index 0000000..c0c921b
--- /dev/null
+++ b/bootimgpack/internal/common-v1/abootimg-pack-initrd
@@ -0,0 +1,23 @@
+#!/bin/sh
+#
+
+if [ "$1" = "-f" ]; then
+ forcewrite=yes
+ shift
+fi
+
+initrd=${1:-initrd.img}
+ramdisk=${2:-RAMDISK}
+
+if [ ! -d $ramdisk ]; then
+ echo "$ramdisk does not exist."
+ exit 1
+fi
+
+if [ -f $initrd -a -z "$forcewrite" ]; then
+ echo "$initrd already exist."
+ exit 1
+fi
+
+( cd $ramdisk; find | sort | cpio --quiet -o -H newc ) | gzip > $initrd
+
diff --git a/bootimgpack/internal/common-v1/abootimg-unpack-initrd b/bootimgpack/internal/common-v1/abootimg-unpack-initrd
new file mode 100755
index 0000000..f5171f2
--- /dev/null
+++ b/bootimgpack/internal/common-v1/abootimg-unpack-initrd
@@ -0,0 +1,19 @@
+#!/bin/sh
+#
+
+initrd=${1:-initrd.img}
+ramdisk=${2:-RAMDISK}
+
+if [ ! -f $initrd ]; then
+ echo "$initrd does not exist."
+ exit 1
+fi
+
+if [ -d $ramdisk ]; then
+ rm $ramdisk -rf
+fi
+
+mkdir -p $ramdisk
+
+zcat $initrd | ( cd $ramdisk; cpio -i )
+
diff --git a/bootimgpack/internal/common-v1/lz4 b/bootimgpack/internal/common-v1/lz4
new file mode 100755
index 0000000..560f8da
Binary files /dev/null and b/bootimgpack/internal/common-v1/lz4 differ
diff --git a/bootimgpack/internal/common-v1/lz4-pack-initrd b/bootimgpack/internal/common-v1/lz4-pack-initrd
new file mode 100755
index 0000000..f928807
--- /dev/null
+++ b/bootimgpack/internal/common-v1/lz4-pack-initrd
@@ -0,0 +1,29 @@
+#!/bin/sh
+#
+
+old_pwd=`pwd`
+basepath=`cd $(dirname $0); pwd`
+cd $old_pwd
+LZ4=$basepath/lz4
+
+if [ "$1" = "-f" ]; then
+ forcewrite=yes
+ shift
+fi
+
+initrd=${1:-initrd.img}
+ramdisk=${2:-RAMDISK}
+
+if [ ! -d $ramdisk ]; then
+ echo "$ramdisk does not exist."
+ exit 1
+fi
+
+if [ -f $initrd -a -z "$forcewrite" ]; then
+ echo "$initrd already exist."
+ exit 1
+fi
+
+( cd $ramdisk; find | sort | cpio --quiet -H newc -o ) | $LZ4
+mv stdin.lz4 $initrd
+
diff --git a/bootimgpack/internal/common-v1/lz4-unpack-initrd b/bootimgpack/internal/common-v1/lz4-unpack-initrd
new file mode 100755
index 0000000..42506c7
--- /dev/null
+++ b/bootimgpack/internal/common-v1/lz4-unpack-initrd
@@ -0,0 +1,27 @@
+#!/bin/sh
+#
+
+old_pwd=`pwd`
+basepath=`cd $(dirname $0); pwd`
+cd $old_pwd
+
+LZ4=$basepath/lz4
+initrd=${1:-initrd.img}
+ramdisk=${2:-RAMDISK}
+
+if [ ! -f $initrd ]; then
+ echo "$initrd does not exist."
+ exit 1
+fi
+
+if [ -d $ramdisk ]; then
+ rm $ramdisk -rf
+fi
+
+echo $initrd
+
+mkdir -p $ramdisk
+cd $ramdisk
+$LZ4 -d ../$initrd | cpio -i
+cd - > /dev/null
+
diff --git a/bootimgpack/internal/common-v1/pack.sh b/bootimgpack/internal/common-v1/pack.sh
new file mode 100755
index 0000000..221c8ba
--- /dev/null
+++ b/bootimgpack/internal/common-v1/pack.sh
@@ -0,0 +1,53 @@
+#!/bin/bash
+
+# pack_boot.sh
+# Pack the directory to boot.img
+
+BOOTDIR=$1
+OUTPUT=$2
+
+function usage()
+{
+ echo "Usage pack_boot.sh BOOTDIR [OUTPUT]"
+ echo " BOOTDIR: the directory containing boot files to be pack"
+ echo " OUTPUT: the output directory. if not present, the out.img will be used"
+}
+
+function init_tools()
+{
+ local old_pwd=`pwd`
+ TOOL_DIR=`cd $(dirname $0); pwd`
+ ABOOTIMG=$TOOL_DIR/abootimg
+ ABOOTIMG_PACK_INITRD=$TOOL_DIR/abootimg-pack-initrd
+ LZ4_PACK_INITRD=$TOOL_DIR/lz4-pack-initrd
+ cd $old_pwd
+}
+
+function pack_bootimg()
+{
+ local old_pwd=`pwd`
+
+ cd $BOOTDIR
+
+ $ABOOTIMG_PACK_INITRD -f newinitrd.img
+ [ $? != 0 ] && exit 1
+
+ if [ -e secondstage ]; then
+ $ABOOTIMG --create pack.img -f bootimg.cfg -k zImage -r newinitrd.img -s secondstage
+ [ $? != 0 ] && exit 1
+ else
+ $ABOOTIMG --create pack.img -f bootimg.cfg -k zImage -r newinitrd.img
+ [ $? != 0 ] && exit 1
+ fi
+ rm -f newinitrd.img
+
+ cd $old_pwd
+ mv $BOOTDIR/pack.img $OUTPUT
+}
+
+# Check parameters
+[ $# -eq 0 ] && usage && exit 1;
+[ -z $2 ] && OUTPUT=out.img;
+
+init_tools;
+pack_bootimg;
diff --git a/bootimgpack/internal/common-v1/unpack.sh b/bootimgpack/internal/common-v1/unpack.sh
new file mode 100755
index 0000000..ab5f504
--- /dev/null
+++ b/bootimgpack/internal/common-v1/unpack.sh
@@ -0,0 +1,76 @@
+#!/bin/bash
+
+# unpack_boot.sh
+# Unpack the standard boot.img or recovery.img of Android
+#
+# @author: duanqz@gmail.com
+#
+
+BOOTIMG=$1
+OUTPUT=$2
+
+function usage()
+{
+ echo "Usage unpack_boot.sh BOOTIMG [OUTPUT]"
+ echo " BOOTIMG: the file path of the boot.img to be unpack"
+ echo " OUTPUT: the output directory. if not present, the OUT/ directory will be used"
+}
+
+function init_tools()
+{
+ local old_pwd=`pwd`
+ TOOL_DIR=`cd $(dirname $0); pwd`
+ ABOOTIMG=$TOOL_DIR/abootimg
+ ABOOTIMG_UNPACK_INITRD=$TOOL_DIR/abootimg-unpack-initrd
+ LZ4_UNPACK_INITRD=$TOOL_DIR/lz4-unpack-initrd
+ cd $old_pwd
+}
+
+function unpack_bootimg()
+{
+ local old_pwd=`pwd`
+ mkdir -p $OUTPUT
+ cp $BOOTIMG $OUTPUT/boot.img
+ cd $OUTPUT
+
+ # Open the macro variable to filter " *** glibc detected *** " error
+ # local tmp_stderr=$LIBC_FATAL_STDERR_
+ # export LIBC_FATAL_STDERR_=1
+
+ # Unpack boot image
+ $ABOOTIMG -x boot.img
+ [ $? != 0 ] && exit 1
+
+ $ABOOTIMG_UNPACK_INITRD
+ if [ $? != 0 ]; then
+ # Failed, try lz4 to unpack ramdisk
+ $LZ4_UNPACK_INITRD
+ [ $? != 0 ] && exit 1
+ fi
+
+ rm -f initrd.img boot.img
+
+ # Remove the bootsize
+ sed -i {1d} bootimg.cfg
+
+ # export LIBC_FATAL_STDERR_=$tmp_stderr
+ cd $old_pwd
+}
+
+function check_result()
+{
+ [ ! -e $OUTPUT/zImage ] && exit 1
+ [ ! -e $OUTPUT/RAMDISK/init.rc ] && exit 1
+}
+
+
+### Start Script ###
+
+# Check parameters
+[ $# -eq 0 ] && usage && exit 1;
+[ -z $2 ] && OUTPUT="OUT/";
+
+init_tools;
+unpack_bootimg;
+check_result;
+exit 0
diff --git a/bootimgpack/internal/common/README.md b/bootimgpack/internal/common/README.md
new file mode 100644
index 0000000..f9e38ef
--- /dev/null
+++ b/bootimgpack/internal/common/README.md
@@ -0,0 +1,2 @@
+
+Support Android 2.3 ~ Android 4.2
diff --git a/bootimgpack/internal/common/minigzip b/bootimgpack/internal/common/minigzip
new file mode 100755
index 0000000..0b0e7e9
Binary files /dev/null and b/bootimgpack/internal/common/minigzip differ
diff --git a/bootimgpack/internal/common/mkbootfs b/bootimgpack/internal/common/mkbootfs
new file mode 100755
index 0000000..31775fb
Binary files /dev/null and b/bootimgpack/internal/common/mkbootfs differ
diff --git a/bootimgpack/internal/common/mkbootimg b/bootimgpack/internal/common/mkbootimg
new file mode 100755
index 0000000..dc6722e
Binary files /dev/null and b/bootimgpack/internal/common/mkbootimg differ
diff --git a/bootimgpack/internal/common/pack.sh b/bootimgpack/internal/common/pack.sh
new file mode 100755
index 0000000..3d0cbc0
--- /dev/null
+++ b/bootimgpack/internal/common/pack.sh
@@ -0,0 +1,50 @@
+#!/bin/bash
+
+# pack_boot.sh
+# Pack the directory to boot.img
+
+BOOTDIR=$1
+OUTPUT=$2
+
+function usage()
+{
+ echo "Usage pack_boot.sh BOOTDIR [OUTPUT]"
+ echo " BOOTDIR: the directory containing boot files to be pack"
+ echo " OUTPUT: the output directory. if not present, the out.img will be used"
+}
+
+function init_tools()
+{
+ local old_pwd=`pwd`
+ TOOL_DIR=`cd $(dirname $0); pwd`
+ MKBOOTFS=$TOOL_DIR/mkbootfs
+ MINIGZIP=$TOOL_DIR/minigzip
+ MKBOOTIMG=$TOOL_DIR/mkbootimg
+ cd $old_pwd
+}
+
+function pack_bootimg()
+{
+ local old_pwd=`pwd`
+
+ cd $BOOTDIR
+ $MKBOOTFS ./RAMDISK | $MINIGZIP > ramdisk.img
+ [ $? != 0 ] && exit 1
+
+ BOOTBASE=$(cat ./base)
+ BOOTCMDLINE=$(cat ./cmdline)
+ BOOTPAGESIZE=$(cat ./pagesize)
+ $MKBOOTIMG --kernel ./kernel --cmdline "$BOOTCMDLINE" --pagesize "$BOOTPAGESIZE" --base "$BOOTBASE" --ramdisk ./ramdisk.img --output pack.img
+ [ $? != 0 ] && exit 1
+
+ rm ramdisk.img
+ cd $old_pwd
+ mv $BOOTDIR/pack.img $OUTPUT
+}
+
+# Check parameters
+[ $# -eq 0 ] && usage && exit 1;
+[ -z $2 ] && OUTPUT=out.img;
+
+init_tools;
+pack_bootimg;
diff --git a/bootimgpack/internal/common/unpack-bootimg.pl b/bootimgpack/internal/common/unpack-bootimg.pl
new file mode 100755
index 0000000..f5402ce
--- /dev/null
+++ b/bootimgpack/internal/common/unpack-bootimg.pl
@@ -0,0 +1,59 @@
+#!/usr/bin/perl -W
+
+use strict;
+use bytes;
+use File::Path;
+
+die "did not specify boot img file\n" unless $ARGV[0];
+
+my $bootimgfile = $ARGV[0];
+
+my $slurpvar = $/;
+undef $/;
+open (BOOTIMGFILE, "$bootimgfile") or die "could not open boot img file: $bootimgfile\n";
+my $bootimg = ;
+close BOOTIMGFILE;
+$/ = $slurpvar;
+
+# chop off the header
+$bootimg = substr($bootimg,2048);
+
+# we'll check how many ramdisks are embedded in this image
+my $numfiles = 0;
+
+# we look for the hex 00 00 00 00 1F 8B because we expect some trailing padding zeroes from the kernel or previous ramdisk, followed by 1F 8B (the gzip magic number)
+while ($bootimg =~ m/\x00\x00\x00\x00\x1F\x8B/g) {
+ $numfiles++;
+}
+
+if ($numfiles == 0) {
+ die "Could not find any embedded ramdisk images. Are you sure this is a full boot image?\n";
+} elsif ($numfiles > 1) {
+ die "Found a secondary file after the ramdisk image. According to the spec (mkbootimg.h) this file can exist, but this script is not designed to deal with this scenario.\n";
+}
+
+$bootimg =~ /(.*\x00\x00\x00\x00)(\x1F\x8B.*)/s;
+
+my $kernel = $1;
+my $ramdisk = $2;
+
+
+open (KERNELFILE, ">$ARGV[0]-kernel");
+print KERNELFILE $kernel or die;
+close KERNELFILE;
+
+open (RAMDISKFILE, ">$ARGV[0]-ramdisk.cpio.gz");
+print RAMDISKFILE $ramdisk or die;
+close RAMDISKFILE;
+
+print "\nkernel written to $ARGV[0]-kernel\nramdisk written to $ARGV[0]-ramdisk.cpio.gz\n";
+if (-e "$ARGV[0]-ramdisk") {
+ rmtree "$ARGV[0]-ramdisk";
+ print "\nremoved old directory $ARGV[0]-ramdisk\n";
+}
+
+mkdir "$ARGV[0]-ramdisk" or die;
+chdir "$ARGV[0]-ramdisk" or die;
+system ("gunzip -c ../$ARGV[0]-ramdisk.cpio.gz | cpio -i");
+
+print "\nextracted ramdisk contents to directory $ARGV[0]-ramdisk/\n";
diff --git a/bootimgpack/internal/common/unpack.sh b/bootimgpack/internal/common/unpack.sh
new file mode 100755
index 0000000..cf81cbb
--- /dev/null
+++ b/bootimgpack/internal/common/unpack.sh
@@ -0,0 +1,74 @@
+#!/bin/bash
+
+# unpack_boot.sh
+# Unpack the standard boot.img or recovery.img of Android
+#
+# @author: duanqz@gmail.com
+#
+
+BOOTIMG=$1
+OUTPUT=$2
+
+function usage()
+{
+ echo "Usage unpack_boot.sh BOOTIMG [OUTPUT]"
+ echo " BOOTIMG: the file path of the boot.img to be unpack"
+ echo " OUTPUT: the output directory. if not present, the OUT/ directory will be used"
+}
+
+function init_tools()
+{
+ local old_pwd=`pwd`
+ TOOL_DIR=`cd $(dirname $0); pwd`
+ UNPACKBOOTIMG=$TOOL_DIR/unpackbootimg
+ UNPACKBOOTIMGPL=$TOOL_DIR/unpack-bootimg.pl
+ cd $old_pwd
+}
+
+function unpack_bootimg()
+{
+ local old_pwd=`pwd`
+ mkdir -p $OUTPUT
+ cp $BOOTIMG $OUTPUT/boot.img
+ cd $OUTPUT
+
+ # Open the macro variable to filter " *** glibc detected *** " error
+ local tmp_stderr=$LIBC_FATAL_STDERR_
+ export LIBC_FATAL_STDERR_=1
+
+ # Unpack boot image
+ $UNPACKBOOTIMG -i boot.img -o ./
+ [ $? != 0 ] && exit 1
+
+ $UNPACKBOOTIMGPL boot.img
+ [ $? != 0 ] && exit 1
+
+ mv boot.img-ramdisk RAMDISK
+ mv boot.img-zImage kernel
+ mv boot.img-cmdline cmdline
+ mv boot.img-base base
+ mv boot.img-pagesize pagesize
+ rm -rf boot.img*
+
+ export LIBC_FATAL_STDERR_=$tmp_stderr
+
+ cd $old_pwd
+}
+
+function check_result()
+{
+ [ ! -e $OUTPUT/kernel ] && exit 1
+ [ ! -e $OUTPUT/RAMDISK/init.rc ] && exit 1
+}
+
+
+### Start Script ###
+
+# Check parameters
+[ $# -eq 0 ] && usage && exit 1;
+[ -z $2 ] && OUTPUT="OUT/";
+
+init_tools;
+unpack_bootimg;
+check_result;
+exit 0
diff --git a/bootimgpack/internal/common/unpackbootimg b/bootimgpack/internal/common/unpackbootimg
new file mode 100755
index 0000000..067cd63
Binary files /dev/null and b/bootimgpack/internal/common/unpackbootimg differ
diff --git a/bootimgpack/internal/imgformat.py b/bootimgpack/internal/imgformat.py
new file mode 100644
index 0000000..6cb157f
--- /dev/null
+++ b/bootimgpack/internal/imgformat.py
@@ -0,0 +1,46 @@
+'''
+Created on Jul 25, 2014
+
+@author: tangliuxiang
+'''
+
+import sys
+
+class ImgFormat(object):
+ '''
+ classdocs
+ '''
+
+ IMAGE_HEAD_STR = "ANDROID"
+
+ def __init__(self, bootfile):
+ '''
+ Constructor
+ '''
+ self.bootfile = bootfile
+
+ def format(self):
+ f = open(self.bootfile, "r+")
+ hstr = f.read(len(ImgFormat.IMAGE_HEAD_STR))
+
+ if hstr == ImgFormat.IMAGE_HEAD_STR:
+ return
+
+ f.seek(0)
+ fstr = f.read()
+
+ try:
+ idx = fstr.index(ImgFormat.IMAGE_HEAD_STR)
+ if idx < 0:
+ return
+ except:
+ #print "Unknown boot image type: " + self.bootfile
+ return
+
+ f.seek(0)
+ f.truncate()
+ f.write(fstr[idx:])
+ f.close()
+
+if __name__ == '__main__':
+ ImgFormat("/home/tangliuxiang/tmp/boot-sign-test/boot.img").format()
\ No newline at end of file
diff --git a/bootimgpack/internal/mtk-v1/mkbootfs b/bootimgpack/internal/mtk-v1/mkbootfs
new file mode 100755
index 0000000..1880d7d
Binary files /dev/null and b/bootimgpack/internal/mtk-v1/mkbootfs differ
diff --git a/bootimgpack/internal/mtk-v1/mkbootimg b/bootimgpack/internal/mtk-v1/mkbootimg
new file mode 100755
index 0000000..f0f0145
Binary files /dev/null and b/bootimgpack/internal/mtk-v1/mkbootimg differ
diff --git a/bootimgpack/internal/mtk-v1/pack.sh b/bootimgpack/internal/mtk-v1/pack.sh
new file mode 100755
index 0000000..92e3ebe
--- /dev/null
+++ b/bootimgpack/internal/mtk-v1/pack.sh
@@ -0,0 +1,39 @@
+#!/bin/bash -x
+
+# unpack_boot.sh
+# Unpack the standard boot.img or recovery.img of Android
+#
+# @author: duanqizhi(duanqz@gmail.com)
+#
+
+BOOTDIR=$1
+OUTPUT=$2
+
+function usage()
+{
+ echo "Usage pack_boot.sh BOOTDIR [OUTPUT]"
+ echo " BOOTDIR: the directory containing boot files to be pack"
+ echo " OUTPUT: the output directory. if not present, the out.img will be used"
+}
+
+function init_tools()
+{
+ local old_pwd=`pwd`
+ TOOL_DIR=`cd $(dirname $0); pwd`
+ REPACK_PL=$TOOL_DIR/repack.pl
+ cd $old_pwd
+}
+
+function pack_bootimg()
+{
+ $REPACK_PL -boot $BOOTDIR/kernel $BOOTDIR/RAMDISK $OUTPUT
+}
+
+### Start Script ###
+
+# Check parameters
+[ $# -eq 0 ] && usage && exit 1;
+[ -z $2 ] && OUTPUT=out.img;
+
+init_tools;
+pack_bootimg;
diff --git a/bootimgpack/internal/mtk-v1/readme b/bootimgpack/internal/mtk-v1/readme
new file mode 100644
index 0000000..f97c36f
--- /dev/null
+++ b/bootimgpack/internal/mtk-v1/readme
@@ -0,0 +1,29 @@
+1、unpack command:
+boot.img:
+./unpack.pl boot.img
+
+recovery.img:
+./unpack.pl recovery.img
+
+2、repack command:
+repack boot.img:
+./repack.pl -boot kernel ramdisk/ boot-new.img
+
+repack recovery.img:
+./repack.pl -recovery kernel ramdisk/ recovery-new.img
+
+
+
+### bootimgpack
+
+mtk-v1, support mt6752. https://github.com/cofface/android-mtk-tools.git
+
+We made changes:
+
+1. Capitalize ramdisk after unpack.pl
+
+2. Do NOT clean_files after repack
+
+
+
+
diff --git a/bootimgpack/internal/mtk-v1/repack.pl b/bootimgpack/internal/mtk-v1/repack.pl
new file mode 100755
index 0000000..16eb850
--- /dev/null
+++ b/bootimgpack/internal/mtk-v1/repack.pl
@@ -0,0 +1,198 @@
+#!/usr/bin/perl
+
+#
+#
+# Change history:
+# - add support mt6752/mt6732/mt6595 by cofface 20150127
+#
+
+use v5.14;
+use warnings;
+use Cwd;
+use Compress::Zlib;
+use Term::ANSIColor;
+use FindBin qw($Bin);
+use File::Basename;
+use Text::Wrap;
+
+my $dir = getcwd;
+
+my $version = "mtk-unpack script by cofface 20150813\n";
+my $usageMain = "repack-MTK.pl \n Repacks MTK boot, recovery\n\n";
+my $usage = $usageMain;
+
+print colored ("$version", 'bold blue') . "\n";
+die "Usage: $usage" unless $ARGV[0] && $ARGV[1] && $ARGV[2];
+
+if ($ARGV[0] eq "-boot" || $ARGV[0] eq "-recovery") {
+ if ($ARGV[1] eq "--debug") {
+ die "Usage: $usage" unless $ARGV[3] && $ARGV[4] && !$ARGV[5];
+ } else {
+ die "Usage: $usage" unless $ARGV[3] && !$ARGV[4];
+ splice (@ARGV, 1, 0, "--normal");
+ }
+ repack_boot();
+} else {
+ die "Usage: $usage";
+}
+
+sub clean_files {
+#clean kernel args.txt ramdisk
+if( -e "kernel")
+{
+unlink("kernel");
+}
+if( -e "args.txt")
+{
+unlink("args.txt");
+}
+if( -d "ramdisk")
+{
+system ("rm -rf ramdisk");
+}
+}
+
+sub repack_boot {
+ my ($type, $mode, $kernel, $ramdiskdir, $outfile) = @ARGV;
+ $type =~ s/^-//;
+ my $debug_mode = ($mode =~ /debug/ ? 1 : 0);
+ $ramdiskdir =~ s/\/$//;
+ my $ramdiskfile = "ramdisk-new.cpio.gz";
+ my $signature = ($type eq "boot" ? "ROOTFS" : "RECOVERY");
+ my %args = (base => "0x10000000", kernel_offset => "0x00008000", ramdisk_offset => "0x01000000", second_offset => "0x00f00000", tags_offset => "0x00000100", pagesize => 2048, board => "", cmdline => "");
+
+ die_msg("kernel file '$kernel' not found!") unless (-e $kernel);
+ chdir $ramdiskdir or die_msg("directory '$ramdiskdir' not found!");
+
+ foreach my $tool ("find", "cpio", "gzip") {
+ die_msg("'$tool' binary not found! Double check your environment setup.")
+ if system ("command -v $tool >/dev/null 2>&1");
+ }
+ print "Repacking $type image...\n";
+ if ($debug_mode) {
+ print colored ("\nRamdisk repack command:", 'yellow') . "\n";
+ print "'find . | cpio -o -H newc | gzip > $dir/$ramdiskfile'\n\n";
+ }
+ print "Ramdisk size: ";
+ system ("find . | cpio -o -H newc | gzip > $dir/$ramdiskfile");
+
+ chdir $dir or die "\n$ramdiskdir $!";;
+
+ open (RAMDISKFILE, $ramdiskfile)
+ or die_msg("couldn't open ramdisk file '$ramdiskfile'!");
+ my $ramdisk;
+ while () {
+ $ramdisk .= $_;
+ }
+ close (RAMDISKFILE);
+
+ # generate the header according to the ramdisk size
+ my $sizeramdisk = length($ramdisk);
+ my $header = gen_header($signature, $sizeramdisk);
+
+ # attach the header to ramdisk
+ my $newramdisk = $header . $ramdisk;
+
+ if ($debug_mode) {
+ open (HEADERFILE, ">ramdisk-new.header")
+ or die_msg("couldn't create MTK header file 'ramdisk-new.header'!");
+ binmode (HEADERFILE);
+ print HEADERFILE $header or die;
+ close (HEADERFILE);
+ } elsif (-e "ramdisk-new.header") {
+ system ("rm ramdisk-new.header");
+ }
+ open (RAMDISKFILE, ">temp-$ramdiskfile")
+ or die_msg("couldn't create repacked ramdisk file 'temp-$ramdiskfile'!");
+ binmode (RAMDISKFILE);
+ print RAMDISKFILE $newramdisk or die;
+ close (RAMDISKFILE);
+
+ # load extra args needed for creating the output file
+ my $argsfile = $kernel;
+ $argsfile =~ s/kernel/args.txt/;
+ my @extrargs;
+ if (-e $argsfile) {
+ open(ARGSFILE, $argsfile)
+ or die_msg("couldn't open extra args file '$argsfile'!");
+ while () {
+ if ($_ =~ /^\--(\w+) (.+)$/) {
+ if (exists $args{$1}) {
+ push (@extrargs, $_);
+ $args{$1} = $2;
+ }
+ }
+ }
+ close (ARGSFILE);
+ chomp (@extrargs);
+ } else {
+ print colored ("\nWarning: file containing extra arguments was not found! The $type image will be repacked using default base address, kernel and ramdisk offsets (as shown bellow).", 'yellow') . "\n";
+ }
+
+ # print build information (only in normal mode)
+ if (!$debug_mode) {
+ print colored ("\nBuild information:\n", 'cyan') . "\n";
+ print colored (" Base address and offsets:\n", 'cyan') . "\n";
+ printf (" Base address:\t\t\t%s\n", $args{"base"});
+ printf (" Kernel offset:\t\t%s\n", $args{"kernel_offset"});
+ printf (" Ramdisk offset:\t\t%s\n", $args{"ramdisk_offset"});
+ printf (" Second stage offset:\t\t%s\n", $args{"second_offset"});
+ printf (" Tags offset:\t\t\t%s\n\n", $args{"tags_offset"});
+ print colored (" Other:\n", 'cyan') . "\n";
+ printf (" Page size (bytes):\t\t%s\n", $args{"pagesize"});
+ printf (" ASCIIZ product name:\t\t'%s'\n", $args{"board"});
+ printf (" Command line:\t\t\t'%s'\n", $args{"cmdline"});
+ }
+
+ # create the output file
+ my $tool = "mkbootimg" . (($^O eq "cygwin") ? ".exe" : (($^O eq "darwin") ? ".osx" : ""));
+ die_msg("couldn't execute '$tool' binary!\nCheck if file exists or its permissions.")
+ unless (-x "$Bin/$tool");
+ if ($debug_mode) {
+ print colored ("\nBuild $type image command:", 'yellow') . "\n";
+ print "'$tool --kernel $kernel --ramdisk temp-$ramdiskfile @extrargs -o $outfile'\n";
+ }
+ system ("$Bin/$tool --kernel $kernel --ramdisk temp-$ramdiskfile @extrargs -o $outfile");
+
+ # cleanup
+ unlink ($ramdiskfile) or die $! unless ($debug_mode);
+ system ("rm temp-$ramdiskfile");
+
+ if (-e $outfile) {
+ print colored ("\nSuccessfully repacked $type image into '$outfile'.", 'green') . "\n";
+ #clean_files();
+ }
+}
+
+sub gen_header {
+ my ($header_type, $length) = @_;
+
+ return pack('a4 L a32 a472', "\x88\x16\x88\x58", $length, $header_type, "\xFF"x472);
+}
+
+sub png_to_rgb565 {
+ my $filename = $_[0] =~ s/.png$//r;
+ my ($rgb565_data, $data, @encoded);
+
+ # convert png into raw rgb (rgb888)
+ system ("convert -depth 8 $filename.png rgb:$filename.raw");
+
+ # convert raw rgb (rgb888) into rgb565
+ open (RAWFILE, "$filename.raw")
+ or die_msg("couldn't open temporary image file '$filename.raw'!");
+ binmode (RAWFILE);
+ while (read (RAWFILE, $data, 3) != 0) {
+ @encoded = unpack('C3', $data);
+ $rgb565_data .= pack('S', (($encoded[0] >> 3) << 11) | (($encoded[1] >> 2) << 5) | ($encoded[2] >> 3));
+ }
+ close (RAWFILE);
+
+ # cleanup
+ system ("rm $filename.raw");
+
+ return $rgb565_data;
+}
+
+sub die_msg {
+ die colored ("\n" . wrap(""," ","Error: $_[0]"), 'red') . "\n";
+}
diff --git a/bootimgpack/internal/mtk-v1/unpack.pl b/bootimgpack/internal/mtk-v1/unpack.pl
new file mode 100755
index 0000000..a020c7f
--- /dev/null
+++ b/bootimgpack/internal/mtk-v1/unpack.pl
@@ -0,0 +1,210 @@
+#!/usr/bin/perl
+
+#
+#
+# Change history:
+# - add support mt6752/mt6732/mt6595 by cofface 20150127
+#
+
+use v5.14;
+use warnings;
+use bytes;
+use File::Path;
+use File::Basename;
+use Compress::Zlib;
+use Term::ANSIColor;
+use Scalar::Util qw(looks_like_number);
+use FindBin qw($Bin);
+use Text::Wrap;
+
+my $version = "mtk-unpack script by cofface 20150812\n";
+my $usageMain = "unpack-MTK.pl [COMMAND ...]\n Unpacks MTK boot, recovery\n\n";
+my $usage = $usageMain;
+
+print colored ("$version", 'bold blue') . "\n";
+die "Usage: $usage" unless $ARGV[0];
+
+if ($ARGV[1]) {
+ if ($ARGV[1] eq "-info_only" || $ARGV[1] eq "--debug") {
+ die "Usage: $usage" unless !$ARGV[2];
+ } elsif ($ARGV[1] eq "-kernel_only" || $ARGV[1] eq "-ramdisk_only") {
+ die "Usage: $usage" unless (!$ARGV[2] || $ARGV[2] eq "--debug" && !$ARGV[3]);
+ } else {
+ die "Usage: $usage";
+ }
+}
+
+my $inputfile = $ARGV[0];
+my $inputFilename = fileparse($inputfile);
+
+open (INPUTFILE, "$inputfile")
+ or die_msg("couldn't open the specified file '$inputfile'!");
+my $input;
+while () {
+ $input .= $_;
+}
+close (INPUTFILE);
+
+sub clean_files {
+#clean boot-new.img kernel args.txt ramdisk
+if( -e "boot-new.img")
+{
+unlink("boot-new.img");
+}
+if( -e "kernel")
+{
+unlink("kernel");
+}
+if( -e "args.txt")
+{
+unlink("args.txt");
+}
+if( -d "ramdisk")
+{
+system ("rm -rf ramdisk");
+}
+}
+
+clean_files();
+
+if (substr($input, 0, 8) eq "ANDROID!") {
+ # else, if a valid Android signature is found, try to unpack boot or recovery image
+ print "Valid Android signature found...\n";
+ if ($ARGV[1]) {
+ die_msg("argument '$ARGV[1]' can't be used with boot or recovery images!")
+ unless ($ARGV[1] eq "-info_only" || $ARGV[1] eq "-kernel_only" || $ARGV[1] eq "-ramdisk_only" ||
+ $ARGV[1] eq "--debug");
+ if ($ARGV[1] eq "--debug") {
+ unpack_boot($input, "kernel and ramdisk", $ARGV[1]);
+ } else {
+ $ARGV[1] =~ s/-//;
+ $ARGV[1] =~ s/_only//;
+ unpack_boot($input, $ARGV[1], $ARGV[2] ? $ARGV[2] : "--normal");
+ }
+ } else {
+ unpack_boot($input, "kernel and ramdisk", "--normal");
+ }
+} else {
+ die_msg("the input file does not appear to be supported or valid!");
+}
+
+
+sub unpack_boot {
+ my ($bootimg, $extract, $mode) = @_;
+ my ($bootMagic, $kernelSize, $kernelLoadAddr, $ram1Size, $ram1LoadAddr, $ram2Size, $ram2LoadAddr, $tagsAddr, $pageSize, $unused1, $unused2, $bootName, $cmdLine, $id) = unpack('a8 L L L L L L L L L L a16 a512 a20', $bootimg);
+ my $magicAddr = 0x00000000;
+ my $baseAddr = $kernelLoadAddr - 0x00008000;
+ my $kernelOffset = $kernelLoadAddr - $baseAddr;
+ my $ram1Offset = $ram1LoadAddr - $baseAddr;
+ my $ram2Offset = $ram2LoadAddr - $baseAddr;
+ my $tagsOffset = $tagsAddr - $baseAddr;
+ my $debug_mode = ($mode =~ /debug/ ? 1 : 0);
+ my $unpack_sucess = 0;
+
+ # remove trailing zeros from board and cmdline
+ $bootName =~ s/\x00+$//;
+ $cmdLine =~ s/\x00+$//;
+
+
+
+ # print input file information (only in normal mode)
+ if (!$debug_mode) {
+ print colored ("\nInput file information:\n", 'cyan') . "\n";
+ print colored (" Header:\n", 'cyan') . "\n";
+ printf (" Boot magic:\t\t\t%s\n", $bootMagic);
+ printf (" Kernel size (bytes):\t\t%d\t\t(0x%.8x)\n", $kernelSize, $kernelSize);
+ printf (" Kernel load address:\t\t0x%.8x\n\n", $kernelLoadAddr);
+ printf (" Ramdisk size (bytes):\t\t%d\t\t(0x%.8x)\n", $ram1Size, $ram1Size);
+ printf (" Ramdisk load address:\t\t0x%.8x\n", $ram1LoadAddr);
+ printf (" Second stage size (bytes):\t%d\t\t(0x%.8x)\n", $ram2Size, $ram2Size);
+ printf (" Second stage load address:\t0x%.8x\n\n", $ram2LoadAddr);
+ printf (" Tags address:\t\t\t0x%.8x\n", $tagsAddr);
+ printf (" Page size (bytes):\t\t%d\t\t(0x%.8x)\n", $pageSize, $pageSize);
+ printf (" ASCIIZ product name:\t\t'%s'\n", $bootName);
+ printf (" Command line:\t\t\t'%s'\n", $cmdLine);
+ printf (" ID:\t\t\t\t%s\n\n", unpack('H*', $id));
+ print colored (" Other:\n", 'cyan') . "\n";
+ printf (" Boot magic offset:\t\t0x%.8x\n", $magicAddr);
+ printf (" Base address:\t\t\t0x%.8x\n\n", $baseAddr);
+ printf (" Kernel offset:\t\t0x%.8x\n", $kernelOffset);
+ printf (" Ramdisk offset:\t\t0x%.8x\n", $ram1Offset);
+ printf (" Second stage offset:\t\t0x%.8x\n", $ram2Offset);
+ printf (" Tags offset:\t\t\t0x%.8x\n", $tagsOffset);
+ }
+
+ if ($extract eq "info") {
+ die colored ("Successfully displayed input file information.", 'green') . "\n";
+ }
+
+ # create file containing extra arguments for further repacking
+ open (ARGSFILE, ">args.txt")
+ or die_msg("couldn't create file 'args.txt'!");
+ printf ARGSFILE ("--base %#.8x\n--pagesize %d\n--kernel_offset %#.8x\n--ramdisk_offset %#.8x\n--second_offset %#.8x\n--tags_offset %#.8x%s%s", $baseAddr, $pageSize, $kernelOffset, $ram1Offset, $ram2Offset, $tagsOffset, $bootName eq "" ? "" : "\n--board $bootName", $cmdLine eq "" ? "" : "\n--cmdline $cmdLine") or die;
+ close (ARGSFILE);
+ print "\nExtra arguments written to 'args.txt'\n";
+
+ if ($extract =~ /kernel/) {
+ my $kernel = substr($bootimg, $pageSize, $kernelSize);
+
+ open (KERNELFILE, ">kernel")
+ or die_msg("couldn't create file 'kernel'!");
+ binmode (KERNELFILE);
+ print KERNELFILE $kernel or die;
+ close (KERNELFILE);
+
+ print "Kernel written to 'kernel'\n";
+ $unpack_sucess = 1;
+ }
+
+ if ($extract =~ /ramdisk/) {
+ my $kernelAddr = $pageSize;
+ my $kernelSizeInPages = int(($kernelSize + $pageSize - 1) / $pageSize);
+
+ my $ram1Addr = (1 + $kernelSizeInPages) * $pageSize;
+ my $ram1 = substr($bootimg, $ram1Addr, $ram1Size);
+
+ # chop ramdisk header
+ $ram1 = substr($ram1, 512);
+
+ if (substr($ram1, 0, 2) ne "\x1F\x8B") {
+ die_msg("the specified boot image does not appear to contain a valid gzip file!");
+ }
+
+ open (RAMDISKFILE, ">ramdisk.cpio.gz")
+ or die_msg("couldn't create file 'ramdisk.cpio.gz'!");
+ binmode (RAMDISKFILE);
+ print RAMDISKFILE $ram1 or die;
+ close (RAMDISKFILE);
+
+ if (-e "ramdisk") {
+ rmtree "ramdisk";
+ print "Removed old ramdisk directory 'ramdisk'\n";
+ }
+
+ mkdir "ramdisk" or die;
+ chdir "ramdisk" or die;
+ foreach my $tool ("gzip", "cpio") {
+ die_msg("'$tool' binary not found! Double check your environment setup.")
+ if system ("command -v $tool >/dev/null 2>&1");
+ }
+ if ($debug_mode) {
+ print colored ("\nRamdisk unpack command:", 'yellow') . "\n";
+ print "'gzip -d -c ../ramdisk.cpio.gz | cpio -i'\n\n";
+ }
+ print "Ramdisk size: ";
+ system ("gzip -d -c ../ramdisk.cpio.gz | cpio -i");
+ system ("rm ../ramdisk.cpio.gz") unless ($debug_mode);
+
+ print "Extracted ramdisk contents to directory 'ramdisk'\n";
+ $unpack_sucess = 1;
+ }
+
+ if ($unpack_sucess == 1) {
+ print colored ("\nSuccessfully unpacked $extract.", 'green') . "\n";
+ }
+}
+
+sub die_msg {
+ die colored ("\n" . wrap("","","Error: $_[0]"), 'red') . "\n";
+}
+
diff --git a/bootimgpack/internal/mtk-v1/unpack.sh b/bootimgpack/internal/mtk-v1/unpack.sh
new file mode 100755
index 0000000..54a4b16
--- /dev/null
+++ b/bootimgpack/internal/mtk-v1/unpack.sh
@@ -0,0 +1,59 @@
+#!/bin/bash
+
+# unpack_boot.sh
+# Unpack the standard boot.img or recovery.img of Android
+#
+# @author: duanqizhi(duanqz@gmail.com)
+#
+
+BOOTIMG=$1
+OUTPUT=$2
+
+function usage()
+{
+ echo "Usage unpack_boot.sh BOOTIMG [OUTPUT]"
+ echo " BOOTIMG: the file path of the boot.img to be unpack"
+ echo " OUTPUT: the output directory. if not present, the OUT/ directory will be used"
+}
+
+function init_tools()
+{
+ local old_pwd=`pwd`
+ TOOL_DIR=`cd $(dirname $0); pwd`
+ UNPACK_PL=$TOOL_DIR/unpack.pl
+ cd $old_pwd
+}
+
+function unpack_bootimg()
+{
+ # Unpack boot image
+ local abs_bootimg=$(readlink -f $BOOTIMG)
+
+ rm -rf $OUTPUT
+ mkdir -p $OUTPUT
+ cd $OUTPUT
+ $UNPACK_PL $abs_bootimg
+ [ $? != 0 ] && exit 1
+ # Capitalize ramdisk
+ [ -e ramdisk ] && mv ramdisk RAMDISK
+ cd - > /dev/null
+}
+
+function check_result()
+{
+ #[ ! -e $OUTPUT/dt.img ] && exit 1
+ [ ! -e $OUTPUT/kernel ] && exit 1
+ [ ! -e $OUTPUT/RAMDISK/init.rc ] && exit 1
+}
+
+
+### Start Script ###
+
+# Check parameters
+[ $# -eq 0 ] && usage && exit 1;
+[ -z $2 ] && OUTPUT="OUT/";
+
+init_tools;
+unpack_bootimg;
+check_result;
+exit 0
\ No newline at end of file
diff --git a/bootimgpack/internal/mtk/README.md b/bootimgpack/internal/mtk/README.md
new file mode 100644
index 0000000..382e6f3
--- /dev/null
+++ b/bootimgpack/internal/mtk/README.md
@@ -0,0 +1,2 @@
+
+Support boot.img of MTK 2.3~4.2
diff --git a/bootimgpack/internal/mtk/mkimage b/bootimgpack/internal/mtk/mkimage
new file mode 100755
index 0000000..786bc15
Binary files /dev/null and b/bootimgpack/internal/mtk/mkimage differ
diff --git a/bootimgpack/internal/mtk/mkmtkbootimg b/bootimgpack/internal/mtk/mkmtkbootimg
new file mode 100755
index 0000000..a5bdc55
Binary files /dev/null and b/bootimgpack/internal/mtk/mkmtkbootimg differ
diff --git a/bootimgpack/internal/mtk/mtkbootroot.sh b/bootimgpack/internal/mtk/mtkbootroot.sh
new file mode 100755
index 0000000..427c6a2
--- /dev/null
+++ b/bootimgpack/internal/mtk/mtkbootroot.sh
@@ -0,0 +1,30 @@
+#/bin/sh
+ramdisk="$1-ramdisk"
+bootheader="$1-bootheader"
+bootkernel="$1-kernel"
+cpio="$1-ramdisk.cpio.gz"
+
+defaultprop="$ramdisk/default.prop"
+kernelnotroot="ro.secure=1"
+kernelroot="ro.secure=0"
+startline="5"
+rpath="`dirname $0`"
+
+if [ $# -lt "1" ];then
+ echo "usage:$0 bootimage"
+ exit 0
+fi
+$rpath/unpack-mtk-bootimg.pl $1
+if [ -f $defaultprop ];then
+ sed -i "/$kernelnotroot/d" $defaultprop
+ sed -i "/$kernelroot/d" $defaultprop
+ echo "$kernelroot" >> $defaultprop
+fi
+
+$rpath/mkbootfs $ramdisk | minigzip > .ramdisk.ori
+$rpath/mkimage .ramdisk.ori ROOTFS > .ramdisk.img
+rm .ramdisk.ori
+echo "generate the root bootimage to $1.boot"
+cat $bootheader $bootkernel .ramdisk.img > $1.boot
+rm $bootheader $bootkernel .ramdisk.img $cpio
+rm -rf $ramdisk
diff --git a/bootimgpack/internal/mtk/pack.sh b/bootimgpack/internal/mtk/pack.sh
new file mode 100755
index 0000000..a52bd42
--- /dev/null
+++ b/bootimgpack/internal/mtk/pack.sh
@@ -0,0 +1,60 @@
+#!/bin/bash
+
+# pack_boot_mtk.sh
+# Pack the directory to boot.img
+
+BOOTDIR=$1
+OUTPUT=$2
+
+function usage()
+{
+ echo "Usage pack_boot_mtk.sh BOOTDIR [OUTPUT]"
+ echo " BOOTDIR: the directory containing boot files to be pack"
+ echo " OUTPUT: the output directory. if not present, the out.img will be used"
+}
+
+function init_tools()
+{
+ local old_pwd=`pwd`
+ TOOL_DIR=`cd $(dirname $0); pwd`
+ MKMTKBOOTIMG=$TOOL_DIR/mkmtkbootimg
+ MKIMAGE=$TOOL_DIR/mkimage
+
+ local common_dir=`cd $TOOL_DIR; cd ../common; pwd`
+ MKBOOTFS=$common_dir/mkbootfs
+ MINIGZIP=$common_dir/minigzip
+ MKBOOTIMG=$common_dir/mkbootimg
+
+ cd $old_pwd
+}
+
+function pack_bootimg()
+{
+ local old_pwd=`pwd`
+
+ cd $BOOTDIR
+ $MKBOOTFS ./RAMDISK | $MINIGZIP > ramdisk.img
+ [ $? != 0 ] && exit 1
+
+ if [ -f ./RAMDISK/etc/recovery.fstab ];then
+ $MKIMAGE ./ramdisk.img RECOVERY > ramdisk_root.img
+ [ $? != 0 ] && exit 1
+ else
+ $MKIMAGE ./ramdisk.img ROOTFS > ramdisk_root.img
+ [ $? != 0 ] && exit 1
+ fi
+
+ $MKMTKBOOTIMG --kernel ./kernel --ramdisk ./ramdisk_root.img --output pack.img
+ [ $? != 0 ] && exit 1
+
+ rm ramdisk.img ramdisk_root.img
+ cd $old_pwd
+ mv $BOOTDIR/pack.img $OUTPUT
+}
+
+# Check parameters
+[ $# -eq 0 ] && usage && exit 1;
+[ -z $2 ] && OUTPUT=out.img;
+
+init_tools;
+pack_bootimg;
diff --git a/bootimgpack/internal/mtk/unpack-mtk-bootimg.pl b/bootimgpack/internal/mtk/unpack-mtk-bootimg.pl
new file mode 100755
index 0000000..41c5116
--- /dev/null
+++ b/bootimgpack/internal/mtk/unpack-mtk-bootimg.pl
@@ -0,0 +1,66 @@
+#!/usr/bin/perl -W
+
+use strict;
+use bytes;
+use File::Path;
+
+die "did not specify boot img file\n" unless $ARGV[0];
+
+my $bootimgfile = $ARGV[0];
+
+my $slurpvar = $/;
+undef $/;
+open (BOOTIMGFILE, "$bootimgfile") or die "could not open boot img file: $bootimgfile\n";
+my $bootimg = ;
+close BOOTIMGFILE;
+$/ = $slurpvar;
+
+my $bootheader = substr($bootimg, 0, 2048);
+
+# chop off the header
+$bootimg = substr($bootimg,2048);
+
+# we'll check how many ramdisks are embedded in this image
+my $numfiles = 0;
+
+# we look for the hex 00 00 00 00 1F 8B because we expect some trailing padding zeroes from the kernel or previous ramdisk, followed by 1F 8B (the gzip magic number)
+while ($bootimg =~ m/\xFF\xFF\xFF\xFF\x1F\x8B/g) {
+ $numfiles++;
+}
+
+if ($numfiles == 0) {
+ die "Could not find any embedded ramdisk images. Are you sure this is a full boot image?\n";
+} elsif ($numfiles > 1) {
+ die "Found a secondary file after the ramdisk image. According to the spec (mkbootimg.h) this file can exist, but this script is not designed to deal with this scenario.\n";
+}
+
+$bootimg =~ /(.*\xFF\xFF\xFF\xFF)(\x1F\x8B.*)/s;
+
+my $kernel = $1;
+my $ramdisk = $2;
+
+$kernel = substr($kernel, 0, length($kernel) - 512);
+
+open (BOOTHEADERLFILE, ">$ARGV[0]-bootheader");
+print BOOTHEADERLFILE $bootheader or die;
+close BOOTHEADERLFILE;
+
+open (KERNELFILE, ">$ARGV[0]-kernel");
+print KERNELFILE $kernel or die;
+close KERNELFILE;
+
+open (RAMDISKFILE, ">$ARGV[0]-ramdisk.cpio.gz");
+print RAMDISKFILE $ramdisk or die;
+close RAMDISKFILE;
+
+print "\nbootheader written to $ARGV[0]-bootheader\nkernel written to $ARGV[0]-kernel\nramdisk written to $ARGV[0]-ramdisk.cpio.gz\n";
+if (-e "$ARGV[0]-ramdisk") {
+ rmtree "$ARGV[0]-ramdisk";
+ print "\nremoved old directory $ARGV[0]-ramdisk\n";
+}
+
+mkdir "$ARGV[0]-ramdisk" or die;
+chdir "$ARGV[0]-ramdisk" or die;
+system ("gunzip -c ../$ARGV[0]-ramdisk.cpio.gz | cpio -i");
+
+print "\nextracted ramdisk contents to directory $ARGV[0]-ramdisk/\n";
diff --git a/bootimgpack/internal/mtk/unpack.sh b/bootimgpack/internal/mtk/unpack.sh
new file mode 100755
index 0000000..da6b86c
--- /dev/null
+++ b/bootimgpack/internal/mtk/unpack.sh
@@ -0,0 +1,61 @@
+#!/bin/bash
+
+# unpack_boot_mtk.sh
+# Unpack the boot.img or recovery.img of MTK format
+#
+# @author: duanqz@gmail.com
+#
+
+BOOTIMG=$1
+OUTPUT=$2
+
+function usage()
+{
+ echo "Usage unpack_boot_mtk.sh BOOTIMG [OUTPUT]"
+ echo " BOOTIMG: the file path of the boot.img to be unpack"
+ echo " OUTPUT: the output directory. if not present, the OUT/ directory will be used"
+}
+
+function init_tools()
+{
+ local old_pwd=`pwd`
+ TOOL_DIR=`cd $(dirname $0); pwd`
+ UNPACKBOOTIMG=$TOOL_DIR/unpack-mtk-bootimg.pl
+ cd $old_pwd
+}
+
+function unpack_bootimg()
+{
+ local old_pwd=`pwd`
+ mkdir -p $OUTPUT
+ cp $BOOTIMG $OUTPUT/boot.img
+ cd $OUTPUT
+
+ # Unpack boot image
+ $UNPACKBOOTIMG boot.img
+ [ $? != 0 ] && exit 1
+
+ mv boot.img-ramdisk RAMDISK
+ mv boot.img-kernel kernel
+ rm -rf boot.img*
+
+ cd $old_pwd
+}
+
+function check_result()
+{
+ [ ! -e $OUTPUT/kernel ] && exit 1
+ [ ! -e $OUTPUT/RAMDISK/init.rc ] && exit 1
+}
+
+
+### Start Script ###
+
+# Check parameters
+[ $# -eq 0 ] && usage && exit 1;
+[ -z $2 ] && OUTPUT="OUT/";
+
+init_tools;
+unpack_bootimg;
+check_result;
+exit 0
diff --git a/bootimgpack/internal/param.py b/bootimgpack/internal/param.py
new file mode 100644
index 0000000..ba6dddd
--- /dev/null
+++ b/bootimgpack/internal/param.py
@@ -0,0 +1,33 @@
+'''
+Created on Jul 26, 2014
+
+@author: tangliuxiang
+'''
+
+import getopt
+import sys
+
+class Options(object): pass
+OPTIONS = Options()
+
+OPTIONS.quiet = False
+
+def ParseOptions(argv,
+ extra_opts="", extra_long_opts=(),
+ extra_option_handler=None):
+ try:
+ opts, args = getopt.getopt(
+ argv, "q" + extra_opts,
+ ["quiet"] +
+ list(extra_long_opts))
+ except getopt.GetoptError, err:
+ print "**", str(err), "**"
+ sys.exit(2)
+
+ for o, a in opts:
+ if o in ("-q", "--quiet"):
+ OPTIONS.quiet = True
+ else:
+ if extra_option_handler is None or not extra_option_handler(o, a):
+ assert False, "unknown option \"%s\"" % (o,)
+ return args
\ No newline at end of file
diff --git a/bootimgpack/internal/qcom/ARM/README.md b/bootimgpack/internal/qcom/ARM/README.md
new file mode 100755
index 0000000..f3ed934
--- /dev/null
+++ b/bootimgpack/internal/qcom/ARM/README.md
@@ -0,0 +1,96 @@
+1. Built to be used on Arm devices.
+
+2. All binaries are statically compiled using the arm-linux-androideabi and arm-linux-gnueabi toolchains.
+
+3. Run the mkboot script and have fun (mkboot script is a shebang manipulator)
+
+NOTE: This project (ARM) is designed to work on Android (ARM) devices, however, it will also work on Linux as well. Tested and confirmed working on Ubuntu 15.04.
+
+### Unpack Boot.img or Recovery.img:
+ root@android:/data/local/tmp/mkbootimg_tool/ARM # ./mkboot boot.img bootfolder
+
+ Unpack & decompress boot.img to bootfolder
+
+ ****** WARNING ******* WARNING ******* WARNING ******
+
+ This image is built using NON-standard mkbootimg!
+
+ BASE is 0x80400000
+ RAMDISK_OFFSET is 0x01408000
+
+ You can modify mkbootimg.c with the above value(s)
+
+ ****** WARNING ******* WARNING ******* WARNING ******
+
+ kernel : zImage
+ ramdisk : ramdisk
+ page size : 2048
+ kernel size : 5690888
+ ramdisk size : 520206
+ base : 0x80400000 (Non Standard)
+ kernel offset : 0x00008000
+ ramdisk offset : 0x01408000 (Non Standard)
+ second offset : 0x00f00000
+ tags offset : 0x00000100
+ cmd line : console=ttyHSL0,115200,n8 user_debug=31
+
+ Ramdisk is gzip format.
+ 1851 blocks
+ Unpack completed.
+
+ root@android:/data/local/tmp/mkbootimg_tools-master/ARM #
+
+### Repack Boot.img or Recovery.img:
+ root@android:/data/local/tmp/mkbootimg_tools-master/ARM # ./mkboot bootfolder boot.img
+
+ mkbootimg from bootfolder/img_info.
+
+ kernel : zImage
+ ramdisk : new_ramdisk.gzip
+ page size : 2048
+ kernel size : 5690888
+ ramdisk size : 521739
+ base : 0x80400000
+ kernel offset : 0x00008000
+ ramdisk offset : 0x01408000
+ second offset : 0x00f00000
+ tags offset : 0x00000100
+ cmd line : console=ttyHSL0,115200,n8 user_debug=31
+
+ Kernel size: 5690888, new ramdisk size: 521739, boot.img: 6215680.
+
+ boot.img has been created.
+
+ root@android:/data/local/tmp/mkbootimg_tools-master/ARM #
+
+### Repack Boot.img or Recovery.img with larger build than original:
+ root@android:/data/local/tmp/mkbootimg_tools-master/ARM # ./mkboot bootfolder boot.img
+
+ mkbootimg from bootfolder/img_info.
+
+ kernel : zImage
+ ramdisk : new_ramdisk.gzip
+ page size : 2048
+ kernel size : 5690888
+ ramdisk size : 11233890
+ base : 0x80400000
+ kernel offset : 0x00008000
+ ramdisk offset : 0x01408000
+ second offset : 0x00f00000
+ tags offset : 0x00000100
+ cmd line : console=ttyHSL0,115200,n8 user_debug=31
+
+ Kernel size: 5690888, new ramdisk size: 11233890, boot.img: 16928768.
+
+ boot.img has been created.
+
+
+ ****** CAUTION ******* CAUTION ******* CAUTION ******
+
+ boot.img is 151552 bytes larger than
+ the original build! Make sure this new
+ size is not larger than the actual partition!
+
+ ****** CAUTION ******* CAUTION ******* CAUTION ******
+
+ root@android:/data/local/tmp/mkbootimg_tools-master/ARM #
diff --git a/bootimgpack/internal/qcom/ARM/bash b/bootimgpack/internal/qcom/ARM/bash
new file mode 100755
index 0000000..4dd852d
Binary files /dev/null and b/bootimgpack/internal/qcom/ARM/bash differ
diff --git a/bootimgpack/internal/qcom/ARM/cpio b/bootimgpack/internal/qcom/ARM/cpio
new file mode 100755
index 0000000..0a91dd8
Binary files /dev/null and b/bootimgpack/internal/qcom/ARM/cpio differ
diff --git a/bootimgpack/internal/qcom/ARM/file b/bootimgpack/internal/qcom/ARM/file
new file mode 100755
index 0000000..af0dff4
Binary files /dev/null and b/bootimgpack/internal/qcom/ARM/file differ
diff --git a/bootimgpack/internal/qcom/ARM/grep b/bootimgpack/internal/qcom/ARM/grep
new file mode 100755
index 0000000..2190149
Binary files /dev/null and b/bootimgpack/internal/qcom/ARM/grep differ
diff --git a/bootimgpack/internal/qcom/ARM/gzip b/bootimgpack/internal/qcom/ARM/gzip
new file mode 100755
index 0000000..eecf12e
Binary files /dev/null and b/bootimgpack/internal/qcom/ARM/gzip differ
diff --git a/bootimgpack/internal/qcom/ARM/lz4 b/bootimgpack/internal/qcom/ARM/lz4
new file mode 100755
index 0000000..76c9fd4
Binary files /dev/null and b/bootimgpack/internal/qcom/ARM/lz4 differ
diff --git a/bootimgpack/internal/qcom/ARM/lzma b/bootimgpack/internal/qcom/ARM/lzma
new file mode 100755
index 0000000..b6c90a9
Binary files /dev/null and b/bootimgpack/internal/qcom/ARM/lzma differ
diff --git a/bootimgpack/internal/qcom/ARM/lzop b/bootimgpack/internal/qcom/ARM/lzop
new file mode 100755
index 0000000..6b80ff5
Binary files /dev/null and b/bootimgpack/internal/qcom/ARM/lzop differ
diff --git a/bootimgpack/internal/qcom/ARM/magic.mgc b/bootimgpack/internal/qcom/ARM/magic.mgc
new file mode 100644
index 0000000..f2d6926
Binary files /dev/null and b/bootimgpack/internal/qcom/ARM/magic.mgc differ
diff --git a/bootimgpack/internal/qcom/ARM/mkboot b/bootimgpack/internal/qcom/ARM/mkboot
new file mode 100755
index 0000000..1e29c0c
--- /dev/null
+++ b/bootimgpack/internal/qcom/ARM/mkboot
@@ -0,0 +1,16 @@
+# This is a wrapper script which basically manipulates the boundaries of an already limited shebang.
+# This allows the execution of the real mkboot (aka wrapper) script using bash from the project directory whether it be on Linux or an ARM device.
+
+target=$(pwd)
+script="${target}/wrapper"
+shebang=$(head -1 "$script")
+buildit="$@"
+
+# Use an array in case a argument is there too
+interp=( ${shebang#\#!} )
+
+# Now run it, passing in the remaining command line arguments
+# EXAMPLE 1: Unpacking: ./mkboot recovery.img recoveryfolder
+# EXAMPLE 2: Packing: ./mkboot recoveryfolder recovery.img
+shift 1
+exec "${target}/${interp[@]}" "$script" ${buildit}
diff --git a/bootimgpack/internal/qcom/ARM/mkbootfs b/bootimgpack/internal/qcom/ARM/mkbootfs
new file mode 100755
index 0000000..c177ea6
Binary files /dev/null and b/bootimgpack/internal/qcom/ARM/mkbootfs differ
diff --git a/bootimgpack/internal/qcom/ARM/mkbootimg b/bootimgpack/internal/qcom/ARM/mkbootimg
new file mode 100755
index 0000000..9cfb1e4
Binary files /dev/null and b/bootimgpack/internal/qcom/ARM/mkbootimg differ
diff --git a/bootimgpack/internal/qcom/ARM/od b/bootimgpack/internal/qcom/ARM/od
new file mode 100755
index 0000000..b63cba4
Binary files /dev/null and b/bootimgpack/internal/qcom/ARM/od differ
diff --git a/bootimgpack/internal/qcom/ARM/wrapper b/bootimgpack/internal/qcom/ARM/wrapper
new file mode 100755
index 0000000..24184d0
--- /dev/null
+++ b/bootimgpack/internal/qcom/ARM/wrapper
@@ -0,0 +1,435 @@
+#!bash
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#boot.img tool
+#original author: xiaolu
+#heavily modified by: Modding.MyMind
+
+#set -x # for debugging
+
+trap "clean" 2 3 4
+workdir=$(pwd)
+toolpath=$(readlink -f $0)
+tooldir=$(dirname $toolpath)
+mkbootimg=$tooldir/mkbootimg
+mkbootfs=$tooldir/mkbootfs
+busybox=$(command -v busybox)
+od=$tooldir/od
+gzip=$tooldir/gzip
+lz4=$tooldir/lz4
+lzop=$tooldir/lzop
+lzma=$tooldir/lzma
+xz=$tooldir/xz # Also used for lzma compression
+grep=$tooldir/grep
+cpio=$tooldir/cpio
+magic=$tooldir/magic.mgc
+file=$tooldir/file
+old_bootimg=true
+C_OUT="\033[0;1m"
+C_ERR="\033[31;1m"
+C_CAUT="\033[33;1m"
+C_CLEAR="\033[0;0m"
+
+pout() {
+ $busybox printf "${C_OUT}${*}${C_CLEAR}\n"
+}
+perr() {
+ $busybox printf "${C_ERR}${*}${C_CLEAR}\n"
+}
+pcaut() {
+ $busybox printf "${C_CAUT}${*}${C_CLEAR}\n"
+}
+clean()
+{
+ $busybox rm -rf /tmp/mkboot.*
+ #pout "..."
+ exit
+}
+
+# Check for busybox
+if [ -z $busybox ]; then
+ clear; pcaut "Busybox is NOT installed!\nThis may cause issues with the script!\nUse at your own peril or install busybox!"; sleep 2
+fi
+
+usage() {
+ pcaut "\n"
+ perr "Not enough parameters or parameter error!\n"
+ pout "Unpack Image & Decompress Ramdisk : \n $($busybox basename $0) [image] [output dir]"
+ pout " $($busybox basename $0) boot.img project_folder\n"
+ pout "Repack Image & Compress Ramdisk : \n $($busybox basename $0) [unpacked dir] [new image]"
+ pout " $($busybox basename $0) project_folder newboot.img\n"
+ clean
+}
+
+print_info() {
+ [[ $boot_magic_addr -gt 0 ]] && pout " boot magic : ANDROID!"
+ [[ $boot_magic_addr -gt 0 ]] && pout " magic address : $boot_magic ($boot_magic_addr)"
+ [ ! -z "$board" ] && pout " board : $board"
+ pout " kernel : $kernel"
+ pout " ramdisk : $ramdisk"
+ pout " page size : $page_size"
+ pout " kernel size : $kernel_size"
+ pout " ramdisk size : $ramdisk_size"
+ [ ! -z $second_size ] && [ $second_size -gt 0 ] && \
+ pout " second size : $second_size"
+ [ $dtb_size -gt 0 ] && pout " dtb size : $dtb_size"
+ if [ ! -z $base_warning ]; then
+ pcaut " base : $base_addr (Non Standard)"
+ else
+ pout " base : $base_addr"
+ fi
+ if [ ! -z $kernel_offset_warning ]; then
+ pcaut " kernel offset : $kernel_offset (Non Standard)"
+ else
+ pout " kernel offset : $kernel_offset"
+ fi
+ if [ ! -z $ramdisk_offset_warning ]; then
+ pcaut " ramdisk offset : $ramdisk_offset (Non Standard)"
+ else
+ pout " ramdisk offset : $ramdisk_offset"
+ fi
+ if [ ! -z $second_offset_warning ]; then
+ [ -z $second_offset ] || pcaut " second offset : $second_offset (Non Standard)"
+ else
+ [ -z $second_offset ] || pout " second offset : $second_offset"
+ fi
+ if [ ! -z $tags_offset_warning ]; then
+ pcaut " tags offset : $tags_offset (Non Standard)"
+ else
+ pout " tags offset : $tags_offset"
+ fi
+ [ $dtb_size -gt 0 ] && pout " dtb offset : $qcdt_offset"
+ [ $dtb_size -gt 0 ] && pout " dtb img : $dt"
+ [ $second_size -gt 0 ] && pout " second img : $second"
+ pout " cmd line : $cmd_line"
+}
+
+mkboot_img() {
+ error=0
+ [ $second_size -gt 0 ] && second="--second ${second}"
+ [ $dtb_size -gt 0 ] && dtb="--dt ${dt}"
+ [ ! -z $second_offset ] && second_offset="--second_offset ${second_offset}"
+ [ ! -z "$board" ] && board="--board $board"
+
+ $mkbootimg --kernel $kernel --ramdisk $ramdisk $board \
+ --base $base_addr --ramdisk_offset $ramdisk_offset \
+ --kernel_offset $kernel_offset $second_offset \
+ --tags_offset $tags_offset --cmdline "$cmd_line" \
+ --pagesize $page_size $second $dtb -o $new_img 2>/dev/null || error=1
+
+ [ $error -eq 1 ] && return $error
+ ramdisk_size=$($busybox stat -c "%s" $ramdisk)
+ boot_size=$($busybox stat -c "%s" $new_img)
+ pout "\nKernel size: $kernel_size, new ramdisk size: $ramdisk_size, $($busybox basename $new_img): $boot_size."
+ pout "\n$($busybox basename $new_img) has been created.\n"
+
+ # Check if new build is larger than original
+ # Give caution if it is to insure size is not larger than the partition
+ # A courtesy warning
+ if [[ "$image_size" -lt "$boot_size" ]]; then
+ beefed_up=$(( $boot_size - $image_size ))
+ pcaut "\n****** CAUTION ******* CAUTION ******* CAUTION ******"
+ pout "\n$($busybox basename $new_img) is $beefed_up bytes larger than"
+ pout "the original build! Make sure this new"
+ pout "size is not larger than the actual partition!"
+ pcaut "\n****** CAUTION ******* CAUTION ******* CAUTION ******\n"
+ fi
+}
+
+#decide action
+[ $# -lt 2 ] || [ $# -gt 3 ] && usage
+if [ $# -eq 2 ] && [ -d $1 ]; then
+ mkboot_from_dir=1
+elif [ $# -eq 2 ] && [ -s $1 ]; then
+ split_boot_to_dir=1
+else
+ usage
+fi
+
+#mkboot_from_dir, img_info
+if [ ! -z $mkboot_from_dir ]; then
+ pout "\nmkbootimg from $1/img_info.\n"
+ unpacked_dir=$1
+ new_img=$2
+ cd $unpacked_dir
+ if [ ! -s img_info ]; then
+ perr "Missing img_info file! Can't rebuild $2."
+ clean
+ fi
+ eval $(cat img_info)
+
+ if [ -z $kernel ] || [ -z $ramdisk ] || [ -z $base_addr ]; then
+ perr "Lacking parameters in img_info."
+ clean
+ fi
+ [ -z $second_size ] && second_size=0
+ [ -z $dtb_size ] && dtb_size=0
+
+ if [ -d $ramdisk ]; then
+ compression_type=$($file -m $magic ./ramdisk.* | $busybox cut -d: -f2 | $busybox cut -d" " -f2)
+ case $compression_type in
+ gzip) compression_warning=$compression_type; compression_ext=gzip; compression_repack=$gzip;;
+ XZ) compression_warning=$compression_type; compression_ext=xz; compression_repack="$xz -1 --check=crc32";;
+ LZMA) compression_warning=$compression_type; compression_ext=lzma; compression_repack="$xz --format=lzma";;
+ LZ4) compression_warning=$compression_type; compression_ext=lz4; compression_repack="$lz4 -l -9";;
+ lzop) compression_warning=$compression_type; compression_ext=lzop; compression_repack="$lzop -f -9";;
+ esac;
+ if [ -z $compression_warning ]; then
+ perr "\n****** HAZARD ******* HAZARD ******* HAZARD ******"
+ pout "\nRamdisk is $compression_type format. Can't repack ramdisk."
+ pout "This tool currently does not support $compression_type."
+ perr "\n****** HAZARD ******* HAZARD ******* HAZARD ******\n"
+ exit
+ fi
+ if [ $compression_type != "gzip" ] && [ $compression_type != "LZMA" ] && [ $compression_type != "LZ4" ] && [ $compression_type != "lzop" ] && [ $compression_type != "XZ" ]; then
+ perr "\nRamdisk is unknown format. Can't repack ramdisk."
+ exit 0
+ else
+ # XZ GZIP LZMA LZ4 LZOP
+ $mkbootfs $ramdisk | $compression_repack > new_ramdisk.$compression_ext
+ ramdisk=new_ramdisk.$compression_ext
+ ramdisk_size=$($busybox stat -c "%s" $ramdisk)
+ fi
+ fi
+ #cd $unpacked_dir
+ print_info
+ $busybox rm -f $new_img
+ mkboot_img $new_img || perr "Make $new_img Error! pls check img_info file."
+ #pout "Add SEANDROIDENFORCE tag."
+ #printf SEANDROIDENFORCE >> $new_img
+ $busybox rm -f new_ramdisk.gz
+ clean
+fi
+
+#split boot.img to dir.
+if [ -e $2 ]; then
+ read -p "$2 exists, delete?(N/y)" reply
+ case $reply in
+ y | Y)
+ $busybox rm -rf $2
+ ;;
+ *)
+ exit
+ ;;
+ esac
+fi
+tempdir=$2
+$busybox mkdir -p "$tempdir"
+pout "\nUnpack & decompress $1 to $2\n"
+
+#get boot.img info
+cp -f $1 $tempdir/
+cd $tempdir
+bootimg=$($busybox basename $1)
+# Find BOOT_MAGIC address in dec and hex
+boot_magic_addr=$($grep -abo ANDROID! $bootimg | $busybox cut -f 1 -d : | head -1)
+boot_magic=`printf 0x%08x $boot_magic_addr`
+# Find standard QCDT address in hex
+qcdt_addr=$($grep -abo QCDT $bootimg | $busybox cut -f 1 -d : | head -1)
+if [ ! -z $qcdt_addr ]; then
+ qcdt_addr=`printf 0x%x $qcdt_addr`
+fi
+[ -z $boot_magic_addr ] && clean
+if [ $boot_magic_addr -gt 0 ]; then
+ $busybox dd if=$bootimg of=bootimg bs=$boot_magic_addr skip=1 2>/dev/null
+ bootimg=bootimg
+fi
+
+kernel_addr=0x$($od -A n -X -j 12 -N 4 $bootimg | $busybox sed 's/ //g' | $busybox sed 's/^0*//g')
+ramdisk_addr=0x$($od -A n -X -j 20 -N 4 $bootimg | $busybox sed 's/ //g' | $busybox sed 's/^0*//g')
+second_addr=0x$($od -A n -X -j 28 -N 4 $bootimg | $busybox sed 's/ //g' | $busybox sed 's/^0*//g')
+tags_addr=0x$($od -A n -X -j 32 -N 4 $bootimg | $busybox sed 's/ //g' | $busybox sed 's/^0*//g')
+
+kernel_size=$($od -A n -D -j 8 -N 4 $bootimg | $busybox sed 's/ //g')
+#base_addr=0x$($od -A n -x -j 14 -N 2 $bootimg | $busybox sed 's/ //g')0000
+ramdisk_size=$($od -A n -D -j 16 -N 4 $bootimg | $busybox sed 's/ //g')
+second_size=$($od -A n -D -j 24 -N 4 $bootimg | $busybox sed 's/ //g')
+page_size=$($od -A n -D -j 36 -N 4 $bootimg | $busybox sed 's/ //g')
+dtb_size=$($od -A n -D -j 40 -N 4 $bootimg | $busybox sed 's/ //g')
+#cmd_line=$($od -A n --strings -j 64 -N 512 $bootimg)
+#board=$($od -A n --strings -j 48 -N 16 $bootimg)
+cmd_line=$($od -A n -S1 -j 64 -N 512 $bootimg)
+board=$($od -A n -S1 -j 48 -N 16 $bootimg)
+
+base_addr=$((kernel_addr-0x00008000))
+kernel_offset=$((kernel_addr-base_addr))
+ramdisk_offset=$((ramdisk_addr-base_addr))
+second_offset=$((second_addr-base_addr))
+tags_offset=$((tags_addr-base_addr))
+qcdt_offset=$((qcdt_addr-base_addr))
+
+base_addr=$(printf "%08x" $base_addr)
+kernel_offset=$(printf "%08x" $kernel_offset)
+ramdisk_offset=$(printf "%08x" $ramdisk_offset)
+second_offset=$(printf "%08x" $second_offset)
+tags_offset=$(printf "%08x" $tags_offset)
+qcdt_offset=$(printf "%08x" $qcdt_offset)
+
+base_addr=0x${base_addr:0-8}
+kernel_offset=0x${kernel_offset:0-8}
+ramdisk_offset=0x${ramdisk_offset:0-8}
+second_offset=0x${second_offset:0-8}
+tags_offset=0x${tags_offset:0-8}
+qcdt_offset=0x${qcdt_offset:0-8}
+
+#########################################################
+# BELOW SECTION HANDLES NON STANDARD IMAGES
+
+if [ $base_addr != 0x10000000 ]; then
+ base_warning=$base_addr
+fi
+
+if [ $kernel_offset != 0x00008000 ]; then
+ kernel_offset_warning=$kernel_offset
+fi
+
+if [ $ramdisk_offset != 0x01000000 ]; then
+ ramdisk_offset_warning=$ramdisk_offset
+fi
+
+if [ $second_offset != 0x00f00000 ]; then
+ second_offset_warning=$second_offset
+fi
+
+if [ $tags_offset != 0x00000100 ]; then
+ tags_offset_warning=$tags_offset
+fi
+
+# Below are the known offsets for non standard mkbootimg.c
+if [[ ! -z $base_warning ]] || [[ ! -z $kernel_offset_warning ]] || [[ ! -z $ramdisk_offset_warning ]] || [[ ! -z $second_offset_warning ]] || [[ ! -z $tags_offset_warning ]]; then
+ perr "****** WARNING ******* WARNING ******* WARNING ******\n"
+ pout "This image is built using NON-standard mkbootimg!\n"
+fi
+if [ ! -z $base_warning ]; then
+ pout "BASE is $base_warning"
+fi
+if [ ! -z $kernel_offset_warning ]; then
+ pout "KERNEL_OFFSET is $kernel_offset_warning"
+fi
+if [ ! -z $ramdisk_offset_warning ]; then
+ pout "RAMDISK_OFFSET is $ramdisk_offset_warning"
+fi
+if [ ! -z $second_offset_warning ]; then
+ pout "SECOND_OFFSET is $second_offset_warning"
+fi
+if [ ! -z $tags_offset_warning ]; then
+ pout "TAGS_OFFSET is $tags_offset_warning"
+fi
+if [[ ! -z $base_warning ]] || [[ ! -z $kernel_offset_warning ]] || [[ ! -z $ramdisk_offset_warning ]] || [[ ! -z $second_offset_warning ]] || [[ ! -z $tags_offset_warning ]]; then
+ pout "\nYou can modify mkbootimg.c with the above value(s)"
+ perr "\n****** WARNING ******* WARNING ******* WARNING ******\n"
+fi
+
+# ABOVE SECTION HANDLES NON STANDARD IMAGES
+#########################################################
+
+k_count=$(((kernel_size+page_size-1)/page_size))
+r_count=$(((ramdisk_size+page_size-1)/page_size))
+s_count=$(((second_size+page_size-1)/page_size))
+d_count=$(((dtb_size+page_size-1)/page_size))
+k_offset=1
+r_offset=$((k_offset+k_count))
+s_offset=$((r_offset+r_count))
+d_offset=$((s_offset+s_count))
+
+#zImage
+$busybox dd if=$bootimg of=zImage_tmp bs=$page_size skip=$k_offset count=$k_count 2>/dev/null
+$busybox dd if=zImage_tmp of=zImage bs=$kernel_size count=1 2>/dev/null
+
+#ramdisk.gz
+$busybox dd if=$bootimg of=ramdisk_tmp bs=$page_size skip=$r_offset count=$r_count 2>/dev/null
+$busybox dd if=ramdisk_tmp of=ramdisk.gz bs=$ramdisk_size count=1 2>/dev/null
+
+#second image
+if [ $second_size -gt 0 ]; then
+ $busybox dd if=$bootimg of=second.img_tmp bs=$page_size skip=$s_offset count=$s_count 2>/dev/null
+ $busybox dd if=second.img_tmp of=second.img bs=$second_size count=1 2>/dev/null
+ second="$tempdir/second.img"
+ second=$($busybox basename $second)
+ secondb_name="second=$second"
+ secondb_size="second_size=$second_size"
+fi
+
+#dtb
+if [ $dtb_size -gt 0 ]; then
+ $busybox dd if=$bootimg of=dt.img_tmp bs=$page_size skip=$d_offset count=$d_count 2>/dev/null
+ $busybox dd if=dt.img_tmp of=dt.img bs=$dtb_size count=1 2>/dev/null
+ dt="$tempdir/dt.img"
+ dt=$($busybox basename $dt)
+ dt_name="dt=$dt"
+ dt_size="dtb_size=$dtb_size"
+fi
+$busybox rm -f *_tmp $($busybox basename $1) $bootimg
+
+kernel=zImage
+ramdisk=ramdisk
+[ ! -s $kernel ] && clean
+#print boot.img info
+print_info
+
+# Properly escape double quotes
+# Keep double quotes intact
+cmd_line=$(echo $cmd_line | sed -e "s/'/'\\\\''/g; 1s/^/'/; \$s/\$/'/")
+
+#write info to img_info,decompression ramdisk.gz
+
+[ ! -z "$board" ] && liveboard="board=\"$board\""
+
+$busybox printf "kernel=zImage\nramdisk=ramdisk\n${secondb_name}\n${dt_name}\npage_size=$page_size\n\
+kernel_size=$kernel_size\nramdisk_size=$ramdisk_size\n${secondb_size}\n${dt_size}\nbase_addr=$base_addr\nkernel_offset=$kernel_offset\n\
+ramdisk_offset=$ramdisk_offset\nsecond_offset=$second_offset\ntags_offset=$tags_offset\nqcdt_offset=$qcdt_offset\ncmd_line=$cmd_line\n$liveboard\n" > img_info
+
+# Include original image size in bytes to img_info
+# Allow script to read second command argument despite the path
+# This should help the script be allow to read images from another directory such as /sdcard/recovery.img, ~/sdcard/recovery.img or simply recovery.img if in the project directory itself
+if [ -f $1 ]; then
+ # Path to file exists outside of project directory
+ image_size=$($busybox stat -c "%s" $1)
+else
+ # Path to file exists in the project directory
+ image_size=$($busybox stat -c "%s" '../'$1)
+fi
+$busybox printf "image_size=$image_size" >> img_info
+
+$busybox mkdir ramdisk
+cd ramdisk
+
+compression_type=$($file -m $magic ../ramdisk.gz | $busybox cut -d: -f2 | $busybox cut -d" " -f2)
+compression_warning=$compression_type
+
+case $compression_type in
+ gzip) compression_type=$gzip; compression_ext=gz;;
+ XZ) compression_type=$xz; compression_ext=xz;;
+ LZMA) compression_type=$lzma; compression_ext=lzma;;
+ LZ4) compression_type=$lz4; compression_ext=lz4;;
+ lzop) compression_type=$lzop; compression_ext=lzop;;
+esac;
+
+decomp_ramdisk="$compression_type -d -c"
+decomp_ramdisk2="$cpio -i -d -m --no-absolute-filenames"
+
+if [ -z $compression_ext ]; then
+ perr "\n****** HAZARD ******* HAZARD ******* HAZARD ******"
+ pout "\nRamdisk is $compression_warning format. Can't unpack ramdisk."
+ pout "This tool currently does not support $compression_warning."
+ perr "\n****** HAZARD ******* HAZARD ******* HAZARD ******\n"
+ exit
+fi
+$busybox mv ../ramdisk.gz ../ramdisk.$compression_ext # This is simply to remind the user if they view the folder afterwards
+pout "\nRamdisk is $compression_warning format."
+$decomp_ramdisk "../ramdisk.$compression_ext" | $decomp_ramdisk2 $extra
+
+#Unpack Finish to exit.
+pout "Unpack completed.\n"
+exit
diff --git a/bootimgpack/internal/qcom/ARM/xz b/bootimgpack/internal/qcom/ARM/xz
new file mode 100755
index 0000000..b897b81
Binary files /dev/null and b/bootimgpack/internal/qcom/ARM/xz differ
diff --git a/bootimgpack/internal/qcom/README.md b/bootimgpack/internal/qcom/README.md
new file mode 100644
index 0000000..e64db85
--- /dev/null
+++ b/bootimgpack/internal/qcom/README.md
@@ -0,0 +1,74 @@
+mkbootimg_tools
+===============
+
+### Unpack and repack boot.img,support dtb(dt.img):
+ xiaolu@xiaolu-ubuntu64:~/e330s$ mkboot recoveryksuamg5.img ksuamg
+ Unpack & decompress recoveryksuamg5.img to ksuamg
+ kernel : /home/xiaolu/work/initramfs/s4/e330s/ksuamg5/zImage
+ ramdisk : /home/xiaolu/work/initramfs/s4/e330s/ksuamg5/ramdisk.gz
+ page_size : 2048
+ base_addr : 0x00000000
+ kernel size : 6911360
+ kernel_addr : 0x00008000
+ ramdisk_size : 2685222
+ ramdisk_addr : 0x02000000
+ second_size : 0
+ second_addr : 0x00f00000
+ dtb_size : 1427456
+ tags_addr : 0x01e00000
+ cmdline : console=null androidboot.hardware=qcom user_debug=31 maxcpus=2 msm_rtb.filter=0x3F
+ Unpack completed.
+
+ xiaolu@xiaolu-ubuntu64:~/e330s$ mkboot ksuamg5 recovery.img
+ mkbootimg from ksuamg5/img_info.
+ kernel : /home/xiaolu/work/initramfs/s4/e330s/ksuamg5/zImage
+ ramdisk : /home/xiaolu/work/initramfs/s4/e330s/ksuamg5/new_ramdisk.gz
+ page_size :
+ base_addr : 0x00000000
+ kernel size : 6911360
+ kernel_addr : 0x00008000
+ ramdisk_size : 2685222
+ ramdisk_addr : 0x02000000
+ second_size :
+ second_addr :
+ dtb_size : 1427456
+ dtb_img : dt.img
+ tags_addr : 0x01e00000
+ cmdline : console=null androidboot.hardware=qcom user_debug=31 maxcpus=2 msm_rtb.filter=0x3F
+ Kernel size: 6911360, new ramdisk size: 3416778, recovery.img: 11759616.
+ recovery.img has been created.
+ ...
+
+### Create a dt.img:
+ xiaolu@xiaolu-ubuntu64:/media/diskd/kernel/SHV-E330S_JB_Opensource/Kernel$ scripts/dtbTool -s 2048 -o arch/arm/boot/dt.img -p scripts/dtc/ arch/arm/boot/
+ DTB combiner:
+ Input directory: '/media/diskd/kernel/SHV-E330S_JB_Opensource/Kernel/arch/arm/boot/'
+ Output file: '/media/diskd/kernel/SHV-E330S_JB_Opensource/Kernel/arch/arm/boot/dt.img'
+ Found file: msm8974-sec-ks01-r03.dtb ... chipset: 2114015745, platform: 3, rev: 0
+ Found file: msm8974-sec-ks01-r07.dtb ... chipset: 2114015745, platform: 7, rev: 0
+ Found file: msm8974-sec-ks01-r06.dtb ... chipset: 2114015745, platform: 6, rev: 0
+ Found file: msm8974-sec-ks01-r04.dtb ... chipset: 2114015745, platform: 4, rev: 0
+ Found file: msm8974-sec-ks01-r11.dtb ... chipset: 2114015745, platform: 11, rev: 0
+ Found file: msm8974-sec-ks01-r02.dtb ... chipset: 2114015745, platform: 2, rev: 0
+ Found file: msm8974-sec-ks01-r00.dtb ... chipset: 2114015745, platform: 0, rev: 0
+ Found file: msm8974-sec-ks01-r05.dtb ... chipset: 2114015745, platform: 5, rev: 0
+ Found file: msm8974-sec-ks01-r01.dtb ... chipset: 2114015745, platform: 1, rev: 0
+ => Found 9 unique DTB(s)
+
+ Generating master DTB... completed
+
+
+### dtbToolCM support dt-tag & dtb v2(https://github.com/CyanogenMod/android_device_qcom_common/tree/cm-11.0/dtbtool):
+
+ dtbToolCM -s 2048 -d "htc,project-id = <" -o arch/arm/boot/dt.img -p scripts/dtc/ arch/arm/boot/
+
+
+### bootimgpack
+
+mkboot is an open source project on https://github.com/xiaolu/mkbootimg_tools.
+
+We make some changes on mkboot:
+1. Capitalize ramdisk
+2. Suppress logs
+
+Support QCOM 4.3+, especially for dt.img of QCOM
diff --git a/bootimgpack/internal/qcom/dtbTool b/bootimgpack/internal/qcom/dtbTool
new file mode 100755
index 0000000..f9b7cb8
Binary files /dev/null and b/bootimgpack/internal/qcom/dtbTool differ
diff --git a/bootimgpack/internal/qcom/dtbToolCM b/bootimgpack/internal/qcom/dtbToolCM
new file mode 100755
index 0000000..822ecae
Binary files /dev/null and b/bootimgpack/internal/qcom/dtbToolCM differ
diff --git a/bootimgpack/internal/qcom/dtbtool.txt b/bootimgpack/internal/qcom/dtbtool.txt
new file mode 100644
index 0000000..9fcd819
--- /dev/null
+++ b/bootimgpack/internal/qcom/dtbtool.txt
@@ -0,0 +1,161 @@
+Refer to device/*/dtbtool.txt
+
+---------------------------------------------------------------------
+Copyright (c) 2012, The Linux Foundation. All rights reserved.
+
+Redistribution and use in source form and compiled forms (SGML, HTML,
+PDF, PostScript, RTF and so forth) with or without modification, are
+permitted provided that the following conditions are met:
+
+Redistributions in source form must retain the above copyright
+notice, this list of conditions and the following disclaimer as the
+first lines of this file unmodified.
+
+Redistributions in compiled form (transformed to other DTDs,
+converted to PDF, PostScript, RTF and other formats) must reproduce
+the above copyright notice, this list of conditions and the following
+disclaimer in the documentation and/or other materials provided with
+the distribution.
+
+THIS DOCUMENTATION IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE AND
+NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE FREEBSD
+DOCUMENTATION PROJECT BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE.
+
+
+Android - Table of Device Tree
+==============================
+
+0) Document revision
+ v1.0 - Initial version (dng)
+
+1) Android boot image:
+----------------------
+1.1) Header:
+ 1) Magic (8B)
+ 2) kernel size (4B)
+ 3) kernel addr (4B)
+ 4) ramdisk size (4B)
+ 5) ramdisk addr (4B)
+ 6) 2ndary size (4B)
+ 7) 2ndary addr (4B)
+ 8) tags addr (4B)
+ 9) page size (4B)
+ 10) unused #1 (4B) (zero in standard Android)
+ 11) unused #2 (4B) (zero in standard Android)
+ 12) product name (16B)
+ 13) kernel cmdline (512B)
+ 14) id (8B)
+
+1.2) Layout:
+ A) header (as above - 1 page)
+ B) kernel (n pages)
+ C) ramdisk (m pages)
+ D) second stage (o pages)
+
+2) QC table of device tree
+--------------------------
+2.1) Changes:
+ i) use "unused #1, #2" members in existing boot image
+ header to point to new table of device tree
+ (#1 - size of QC table of DT)
+ ii) append table of device tree (described later)
+ after "D) second stage"
+
+2.2) Format:
+ size
+ x +------------------+
+ | | MAGIC ("QCDT") | 4B
+ | +------------------+
+ header | VERSION | uint32 (initial version 1)
+ | +------------------+
+ | | num of DTBs | uint32 (number of DTB entries)
+ x +------------------+
+ | | platform id #1 | uint32 (e.g. ID for MSM8974)
+ | +------------------+
+ | | variant id #1 | uint32 (e.g. ID for CDP, MTP)
+ device +------------------+
+ #1 | soc rev #1 | uint32 (e.g. MSM8974 v2)
+ entry +------------------+
+ | | offset #1 | uint32 (byte offset from start/before MAGIC
+ | +------------------+ to DTB entry)
+ | | size #1 | uint32 (size in bytes of DTB blob)
+ x +------------------+
+ . .
+ . . (repeat)
+ . .
+
+ x +------------------+
+ | | platform id #Z | uint32 (e.g. ID for MSM8974)
+ | +------------------+
+ device | variant id #Z | uint32 (e.g. ID for CDP, MTP)
+ #Z +------------------+
+ entry | soc rev #Z | uint32 (e.g. MSM8974 v2)
+ (last) +------------------+
+ | | offset #Z | uint32 (byte offset from start/before MAGIC
+ x +------------------+ to DTB entry)
+ | 0 ("zero") | uint32 (end of list delimiter)
+ +------------------+ to DTB entry)
+ | padding | variable length for next DTB to start on
+ +------------------+ page boundary
+ | DTB #1 | variable (start is page aligned)
+ | |
+ | |
+ +------------------+
+ | padding | variable length for next DTB to start on
+ +------------------+ page boundary
+ .
+ .
+ .
+
+ +------------------+
+ | DTB #Z (last) | variable (start is page aligned)
+ | |
+ | |
+ +------------------+
+
+3) Operations
+-------------
+3.1) Build-time:
+ 1) Each DTS per device will add a "qcom,msm-id" triplet
+ e.g. for msm8974-sim.dts, add
+ qcom,msm-id = ;
+ x = ID for msm8974
+ y = ID for CDP, MTP, etc.
+ z = ID for soc revision
+ The triplet can optionally be an array of triplets:
+ qcom,msm-id = , , ...;
+ 2) Kernel compile will generate the DTB
+ 3) Android build will run a new tool (dtbTool)
+ a) scan the DTB output directory for all compiled DTB
+ b) decompile the DTB for "qcom,msm-id"
+ c) generate the QC table of device tree in sorted
+ order (platform, variant, soc rev)
+ d) modified mkbootimg will merge new table of DT
+
+3.2) Run-time:
+ 1) LK bootloader will obtain MSM id/variant/soc rev info
+ either from early bootloaders or via other means
+ 2) LK bootloader will check entries #10 for non-zero
+ value (set to zero for standard boot.img). If the
+ value is non-zero, refer to page section after
+ the "second stage" in the boot.img layout
+ 3) Check QCDT magic
+ 4) Check QCDT version (optional LK to handle multiple
+ QCDT version)
+ 5) LK scans through the QCDT table to look for matching
+ entry. Search order is:
+ 1) platform ID exact match
+ 2) variant ID exact match
+ 3) select the highest soc rev in QCDT that is
+ equal to or lower than the runtime detected soc rev
+ 6) Load the matching DTB blob to the tags addr
+ 7) LK pass the correct DTB to the kernel
diff --git a/bootimgpack/internal/qcom/dtc b/bootimgpack/internal/qcom/dtc
new file mode 100755
index 0000000..22cdad7
Binary files /dev/null and b/bootimgpack/internal/qcom/dtc differ
diff --git a/bootimgpack/internal/qcom/lz4 b/bootimgpack/internal/qcom/lz4
new file mode 100755
index 0000000..d28446b
Binary files /dev/null and b/bootimgpack/internal/qcom/lz4 differ
diff --git a/bootimgpack/internal/qcom/mkboot b/bootimgpack/internal/qcom/mkboot
new file mode 100755
index 0000000..7ec3963
--- /dev/null
+++ b/bootimgpack/internal/qcom/mkboot
@@ -0,0 +1,315 @@
+#!/bin/bash
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#boot.img tool
+#by xiaolu
+trap "clean" 2 3 4
+workdir=$(pwd)
+toolpath=$(readlink -f $0)
+tooldir=$(dirname $toolpath)
+mkbootimg=$tooldir/mkbootimg
+mkbootfs=$tooldir/mkbootfs
+C_OUT="\033[0;1m"
+C_ERR="\033[31;1m"
+C_CLEAR="\033[0;0m"
+
+pout() {
+ printf "${C_OUT}${*}${C_CLEAR}\n"
+}
+perr() {
+ printf "${C_ERR}${*}${C_CLEAR}\n"
+}
+clean()
+{
+ rm -rf /tmp/mkboot.*
+ pout "..."
+ exit
+}
+
+unpack_complete()
+{
+ [ ! -z $format ] && echo format=$format >> ../img_info
+ pout "Unpack completed."
+ exit
+}
+
+zip_command()
+{
+ if [ "$1" == lzop ]; then
+ zcommand="lzop -n -f -9"
+ elif [ "$1" == lz4 ]; then
+ # Coron start: fix bug of lz4 can not be found
+ zcommand="$tooldir/lz4 -f -9"
+ # Coron end
+ elif [ "$1" == lzma ]; then
+ zcommand="lzma -f -c"
+ elif [ "$1" == xz ]; then
+ zcommand="xz -f -c"
+ else
+ zcommand="gzip -n -f"
+ fi
+}
+
+usage()
+{
+ pout ""
+ pout "----------------------------------------------------------------------"
+ pout "Not enough parameters or parameter error!"
+ pout "unpack boot.img & decompress ramdisk:\n $(basename $0) [img] [output dir]"
+ pout " $(basename $0) boot.img boot20130905"
+ pout "Use the unpacked directory repack boot.img(img_info):\n $(basename $0) [unpacked dir] [newbootfile]"
+ pout " $(basename $0) boot20130905 newboot.img"
+ clean
+}
+
+print_info()
+{
+ [ ! -z "$board" ] && pout " board : $board"
+ pout " kernel : $kernel"
+ pout " ramdisk : $ramdisk"
+ pout " page size : $page_size"
+ pout " kernel size : $kernel_size"
+ pout " ramdisk size : $ramdisk_size"
+ [ ! -z $second_size ] && [ $second_size -gt 0 ] && \
+ pout " second_size : $second_size"
+ [ $dtb_size -gt 0 ] && pout " dtb size : $dtb_size"
+ pout " base : $base_addr"
+ pout " kernel offset : $kernel_offset"
+ pout " ramdisk offset : $ramdisk_offset"
+ [ ! -z $second_size ] && [ $second_size -gt 0 ] && \
+ pout " second_offset : $second_offset"
+ pout " tags offset : $tags_offset"
+ [ $dtb_size -gt 0 ] && pout " dtb img : $dt"
+ pout " cmd line : $cmd_line"
+}
+
+mkboot_img()
+{
+ error=0
+ [ $second_size -gt 0 ] && second="--second ${second}"
+ [ $dtb_size -gt 0 ] && dtb="--dt ${dt}"
+
+ $mkbootimg --kernel $kernel --ramdisk $ramdisk --board "$board" \
+ --base $base_addr --ramdisk_offset $ramdisk_offset \
+ --tags_offset $tags_offset --cmdline "$cmd_line" \
+ --pagesize $page_size $second $dtb -o $1 || error=1
+
+ [ $error -eq 1 ] && return $error
+ ramdisk_size=$(stat -c "%s" $ramdisk)
+ boot_size=$(stat -c "%s" $1)
+ pout "Kernel size: $kernel_size, new ramdisk size: $ramdisk_size, $(basename $1): $boot_size."
+ pout "$(basename $1) has been created."
+}
+
+#decide action
+[ $# -lt 2 ] || [ $# -gt 3 ] && usage
+if [ $# -eq 2 ] && [ -d $1 ]; then
+ mkboot_from_dir=1
+elif [ $# -eq 2 ] && [ -s $1 ]; then
+ split_boot_to_dir=1
+else
+ usage
+fi
+
+#mkboot_from_dir, img_info
+if [ ! -z $mkboot_from_dir ]; then
+ pout "mkbootimg from $1/img_info."
+ unpacked_dir=$(readlink -f $1)
+ new_img=$(readlink -f $2)
+ cd $unpacked_dir
+ if [ ! -s img_info ]; then
+ pout "not found img_info file! can't rebuild img."
+ clean
+ fi
+ eval $(cat img_info)
+ if [ -z $kernel ] || [ -z $ramdisk ] || [ -z $base_addr ]; then
+ pout "img_info file have not enough parameters."
+ clean
+ fi
+ [ -z $second_size ] && second_size=0
+ [ -z $dtb_size ] && dtb_size=0
+ if [ -d $ramdisk ]; then
+ [ -z $format ] && format=gzip
+ zip_command $format
+ #cd $ramdisk; find . | fakeroot cpio -R 0:0 -H newc -o 2>/dev/null \
+ # | $zcommand > $unpacked_dir/new_ramdisk; cd $unpacked_dir
+ $mkbootfs $ramdisk | $zcommand > new_ramdisk
+ ramdisk=new_ramdisk
+ ramdisk_size=$(stat -c "%s" $ramdisk)
+ fi
+ print_info
+ pout "ramdisk is $format format."
+ rm -f $new_img
+ mkboot_img $new_img || perr "Make boot.img Error! pls check img_info file."
+ #pout "Add SEANDROIDENFORCE tag."
+ #printf SEANDROIDENFORCE >> $new_img
+ rm -f new_ramdisk
+ clean
+fi
+
+# Coron Start: No need to ask
+#split boot.img to dir.
+#if [ -e $2 ]; then
+# read -p "$2 exists, delete?(N/y)" reply
+# case $reply in
+# y | Y)
+# rm -rf $2
+# ;;
+# *)
+# exit
+# ;;
+# esac
+#fi
+# Coron End
+
+tempdir="$(readlink -f $2)"
+# Coron start: Using $2 as tempdir
+[ -z $tempdir ] && tempdir=$2
+# Coron end
+mkdir -p $tempdir
+pout "Unpack & decompress $1 to $2"
+
+#get boot.img info
+cp -f $1 $tempdir/
+cd $tempdir
+bootimg=$(basename $1)
+offset=$(grep -abo ANDROID! $bootimg | cut -f 1 -d :)
+[ -z $offset ] && clean
+if [ $offset -gt 0 ]; then
+ dd if=$bootimg of=bootimg bs=$offset skip=1 2>/dev/null
+ bootimg=bootimg
+fi
+
+kernel_addr=0x$(od -A n -X -j 12 -N 4 $bootimg | sed 's/ //g' | sed 's/^0*//g')
+ramdisk_addr=0x$(od -A n -X -j 20 -N 4 $bootimg | sed 's/ //g' | sed 's/^0*//g')
+second_addr=0x$(od -A n -X -j 28 -N 4 $bootimg | sed 's/ //g' | sed 's/^0*//g')
+tags_addr=0x$(od -A n -X -j 32 -N 4 $bootimg | sed 's/ //g' | sed 's/^0*//g')
+
+kernel_size=$(od -A n -D -j 8 -N 4 $bootimg | sed 's/ //g')
+#base_addr=0x$(od -A n -x -j 14 -N 2 $bootimg | sed 's/ //g')0000
+ramdisk_size=$(od -A n -D -j 16 -N 4 $bootimg | sed 's/ //g')
+second_size=$(od -A n -D -j 24 -N 4 $bootimg | sed 's/ //g')
+page_size=$(od -A n -D -j 36 -N 4 $bootimg | sed 's/ //g')
+dtb_size=$(od -A n -D -j 40 -N 4 $bootimg | sed 's/ //g')
+#cmd_line=$(od -A n --strings -j 64 -N 512 $bootimg)
+#board=$(od -A n --strings -j 48 -N 16 $bootimg)
+cmd_line=$(od -A n -S1 -j 64 -N 512 $bootimg)
+board=$(od -A n -S1 -j 48 -N 16 $bootimg)
+
+base_addr=$((kernel_addr-0x00008000))
+kernel_offset=$((kernel_addr-base_addr))
+ramdisk_offset=$((ramdisk_addr-base_addr))
+second_offset=$((second_addr-base_addr))
+tags_offset=$((tags_addr-base_addr))
+
+base_addr=$(printf "%08x" $base_addr)
+kernel_offset=$(printf "%08x" $kernel_offset)
+ramdisk_offset=$(printf "%08x" $ramdisk_offset)
+second_offset=$(printf "%08x" $second_offset)
+tags_offset=$(printf "%08x" $tags_offset)
+
+base_addr=0x${base_addr:0-8}
+kernel_offset=0x${kernel_offset:0-8}
+ramdisk_offset=0x${ramdisk_offset:0-8}
+second_offset=0x${second_offset:0-8}
+tags_offset=0x${tags_offset:0-8}
+
+k_count=$(((kernel_size+page_size-1)/page_size))
+r_count=$(((ramdisk_size+page_size-1)/page_size))
+s_count=$(((second_size+page_size-1)/page_size))
+d_count=$(((dtb_size+page_size-1)/page_size))
+k_offset=1
+r_offset=$((k_offset+k_count))
+s_offset=$((r_offset+r_count))
+d_offset=$((s_offset+s_count))
+
+#kernel
+dd if=$bootimg of=kernel_tmp bs=$page_size skip=$k_offset count=$k_count 2>/dev/null
+dd if=kernel_tmp of=kernel bs=$kernel_size count=1 2>/dev/null
+#ramdisk.packed
+dd if=$bootimg of=ramdisk_tmp bs=$page_size skip=$r_offset count=$r_count 2>/dev/null
+dd if=ramdisk_tmp of=ramdisk.packed bs=$ramdisk_size count=1 2>/dev/null
+#second
+if [ $second_size -gt 0 ]; then
+ dd if=$bootimg of=second.img.tmp bs=$page_size skip=$s_offset count=$s_count 2>/dev/null
+ dd if=second.img.tmp of=second.img bs=$second_size count=1 2>/dev/null
+ s_name="second=second.img\n"
+ s_size="second_size=$second_size\n"
+fi
+#dtb
+if [ $dtb_size -gt 0 ]; then
+ dd if=$bootimg of=dt.img_tmp bs=$page_size skip=$d_offset count=$d_count 2>/dev/null
+ dd if=dt.img_tmp of=dt.img bs=$dtb_size count=1 2>/dev/null
+ dt="$tempdir/dt.img"
+ dt=$(basename $dt)
+ dt_name="dt=$dt\n"
+ dt_size="dtb_size=$dtb_size\n"
+fi
+rm -f *_tmp $(basename $1) $bootimg
+
+kernel=kernel
+ramdisk=ramdisk
+[ ! -s $kernel ] && clean
+#print boot.img info
+print_info
+
+esq="'\"'\"'"
+escaped_cmd_line=`echo $cmd_line | sed "s/'/$esq/g"`
+
+#write info to img_info,decompression ramdisk.packed
+# Coron start: capitalize ramdisk
+printf "kernel=kernel\nramdisk=RAMDISK\n${s_name}${dt_name}page_size=$page_size\n\
+kernel_size=$kernel_size\nramdisk_size=$ramdisk_size\n${s_size}${dt_size}base_addr=$base_addr\nkernel_offset=$kernel_offset\n\
+ramdisk_offset=$ramdisk_offset\ntags_offset=$tags_offset\ncmd_line=\'$escaped_cmd_line\'\nboard=\"$board\"\n" > img_info
+mkdir RAMDISK
+cd RAMDISK
+# Coron end: capitalize ramdisk
+
+gzip -t ../ramdisk.packed 2>/dev/null
+if [ $? -eq 0 ]; then
+ pout "ramdisk is gzip format."
+ format=gzip
+ gzip -d -c ../ramdisk.packed | cpio -i -d -m --no-absolute-filenames 2>/dev/null
+ unpack_complete
+fi
+lzma -t ../ramdisk.packed 2>/dev/null
+if [ $? -eq 0 ]; then
+ pout "ramdisk is lzma format."
+ format=lzma
+ lzma -d -c ../ramdisk.packed | cpio -i -d -m --no-absolute-filenames 2>/dev/null
+ unpack_complete
+fi
+xz -t ../ramdisk.packed 2>/dev/null
+if [ $? -eq 0 ]; then
+ pout "ramdisk is xz format."
+ format=xz
+ xz -d -c ../ramdisk.packed | cpio -i -d -m --no-absolute-filenames 2>/dev/null
+ unpack_complete
+fi
+lzop -t ../ramdisk.packed 2>/dev/null
+if [ $? -eq 0 ]; then
+ pout "ramdisk is lzo format."
+ format=lzop
+ lzop -d -c ../ramdisk.packed | cpio -i -d -m --no-absolute-filenames 2>/dev/null
+ unpack_complete
+fi
+$tooldir/lz4 -d ../ramdisk.packed 2>/dev/null | cpio -i -d -m --no-absolute-filenames 2>/dev/null
+if [ $? -eq 0 ]; then
+ pout "ramdisk is lz4 format."
+ format=lz4
+else
+ pout "ramdisk is unknown format,can't unpack ramdisk"
+fi
+unpack_complete
+
diff --git a/bootimgpack/internal/qcom/mkbootfs b/bootimgpack/internal/qcom/mkbootfs
new file mode 100755
index 0000000..18f88c9
Binary files /dev/null and b/bootimgpack/internal/qcom/mkbootfs differ
diff --git a/bootimgpack/internal/qcom/mkbootimg b/bootimgpack/internal/qcom/mkbootimg
new file mode 100755
index 0000000..4e8bdb8
Binary files /dev/null and b/bootimgpack/internal/qcom/mkbootimg differ
diff --git a/bootimgpack/internal/qcom/pack.sh b/bootimgpack/internal/qcom/pack.sh
new file mode 100755
index 0000000..5d11b96
--- /dev/null
+++ b/bootimgpack/internal/qcom/pack.sh
@@ -0,0 +1,39 @@
+#!/bin/bash
+
+# unpack_boot.sh
+# Unpack the standard boot.img or recovery.img of Android
+#
+# @author: duanqizhi(duanqz@gmail.com)
+#
+
+BOOTDIR=$1
+OUTPUT=$2
+
+function usage()
+{
+ echo "Usage pack_boot.sh BOOTDIR [OUTPUT]"
+ echo " BOOTDIR: the directory containing boot files to be pack"
+ echo " OUTPUT: the output directory. if not present, the out.img will be used"
+}
+
+function init_tools()
+{
+ local old_pwd=`pwd`
+ TOOL_DIR=`cd $(dirname $0); pwd`
+ MKBOOT=$TOOL_DIR/mkboot
+ cd $old_pwd
+}
+
+function pack_bootimg()
+{
+ $MKBOOT $BOOTDIR $OUTPUT
+}
+
+### Start Script ###
+
+# Check parameters
+[ $# -eq 0 ] && usage && exit 1;
+[ -z $2 ] && OUTPUT=out.img;
+
+init_tools;
+pack_bootimg;
diff --git a/bootimgpack/internal/qcom/unpack.sh b/bootimgpack/internal/qcom/unpack.sh
new file mode 100755
index 0000000..23f1995
--- /dev/null
+++ b/bootimgpack/internal/qcom/unpack.sh
@@ -0,0 +1,51 @@
+#!/bin/bash
+
+# unpack_boot.sh
+# Unpack the standard boot.img or recovery.img of Android
+#
+# @author: duanqizhi(duanqz@gmail.com)
+#
+
+BOOTIMG=$1
+OUTPUT=$2
+
+function usage()
+{
+ echo "Usage unpack_boot.sh BOOTIMG [OUTPUT]"
+ echo " BOOTIMG: the file path of the boot.img to be unpack"
+ echo " OUTPUT: the output directory. if not present, the OUT/ directory will be used"
+}
+
+function init_tools()
+{
+ local old_pwd=`pwd`
+ TOOL_DIR=`cd $(dirname $0); pwd`
+ MKBOOT=$TOOL_DIR/mkboot
+ cd $old_pwd
+}
+
+function unpack_bootimg()
+{
+ # Unpack boot image
+ $MKBOOT $BOOTIMG $OUTPUT
+ [ $? != 0 ] && exit 1
+}
+
+function check_result()
+{
+ #[ ! -e $OUTPUT/dt.img ] && exit 1
+ [ ! -e $OUTPUT/kernel ] && exit 1
+ [ ! -e $OUTPUT/RAMDISK/init.rc ] && exit 1
+}
+
+
+### Start Script ###
+
+# Check parameters
+[ $# -eq 0 ] && usage && exit 1;
+[ -z $2 ] && OUTPUT="OUT/";
+
+init_tools;
+unpack_bootimg;
+check_result;
+exit 0
\ No newline at end of file
diff --git a/bootimgpack/internal/sony/README.md b/bootimgpack/internal/sony/README.md
new file mode 100644
index 0000000..5d22a09
--- /dev/null
+++ b/bootimgpack/internal/sony/README.md
@@ -0,0 +1,2 @@
+
+Support boot.img of Sony 2.3~4.2
diff --git a/bootimgpack/internal/sony/mkelf.py b/bootimgpack/internal/sony/mkelf.py
new file mode 100755
index 0000000..a1d7050
--- /dev/null
+++ b/bootimgpack/internal/sony/mkelf.py
@@ -0,0 +1,182 @@
+#!/usr/bin/env python
+#
+#
+# Copyright (c) 2012, Sony Mobile Communications AB
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Sony Mobile Communications AB nor the names
+# of its contributors may be used to endorse or promote products
+# derived from this software without specific prior written
+# permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+# DESCRIPTION
+#
+# Combine one or more files to a elf file.
+#
+# SYNOPSIS
+#
+# mkelf.py -o elf file@addr[,name] ...
+#
+# elf The path to the output file
+# file Input file
+# addr ELF vaddr and paddr of this file
+# name Can be any of kernel, ramdisk, ipl, cmdline, appsbl, rpm.
+# If not specified, it will be 'kernel'.
+#
+# The entry point will be set to the first segment provided unless -e
+# is provided, which can override this value.
+import os
+import re
+import struct
+import sys
+from optparse import OptionParser
+
+
+class Segment(object):
+ def __init__(self, name, type, flags):
+ self.name = name
+ self.type = type
+ self.flags = flags
+ self.addr = 0
+ self.file = ''
+ self.offset = 0
+ self.size = 0
+
+ def get_phdr(self):
+ return struct.pack('> sys.stderr, "%s: %s" % (os.path.basename(sys.argv[0]), message)
+ sys.exit(1)
+
+
+def find_segments(args):
+ segs = []
+ for arg in args:
+ m = re.match('(.*?)@(0x[0-9a-fA-F]+)?,?(.*?)$', arg)
+ if not m:
+ fatal("Invalid format of input parameter: " + arg)
+ tokens = m.groups()
+
+ try:
+ obj = get_segment(tokens[2])
+ except:
+ fatal("Invalid section name: %s" % tokens[2])
+
+ obj.file = tokens[0]
+ if tokens[1]:
+ obj.addr = long(tokens[1], 16)
+ segs.append(obj)
+ return segs
+
+
+def write_elf_header(elf, entrypoint, phnum):
+ elfhdr = {
+ 'e_ident': '\x7fELF\x01\x01\x01\x61',
+ 'e_type': 2,
+ 'e_machine': 40,
+ 'e_version': 1,
+ 'e_entry': entrypoint,
+ 'e_phoff': 52,
+ 'e_shoff': 0,
+ 'e_flags': 0,
+ 'e_ehsize': 52,
+ 'e_phentsize': 32,
+ 'e_phnum': phnum,
+ 'e_shentsize': 0,
+ 'e_shnum': 0,
+ 'e_shstrndx': 0,
+ }
+
+ elf.write(struct.pack('<8s8xHHLLLLLHHHHHH',
+ elfhdr['e_ident'], elfhdr['e_type'],
+ elfhdr['e_machine'], elfhdr['e_version'],
+ elfhdr['e_entry'], elfhdr['e_phoff'],
+ elfhdr['e_shoff'], elfhdr['e_flags'],
+ elfhdr['e_ehsize'], elfhdr['e_phentsize'],
+ elfhdr['e_phnum'], elfhdr['e_shentsize'],
+ elfhdr['e_shnum'], elfhdr['e_shstrndx']))
+
+
+def main(args):
+ parser = OptionParser("usage: %prog options")
+ parser.add_option('-o', dest='outputfile', help="path to the output file")
+ parser.add_option('-e', dest='entrypoint', help="entrypoint (optional)")
+ (opts, args) = parser.parse_args()
+
+ if not opts.outputfile:
+ fatal("Missing -o on command line")
+ if len(args) < 1:
+ fatal("Missing input files")
+
+ offset = 4096
+ segments = find_segments(args)
+ for seg in segments:
+ size = os.path.getsize(seg.file)
+ seg.offset = offset
+ seg.size = size
+ offset += size
+
+ with open(opts.outputfile, 'wb') as elf:
+ entrypoint = segments[0].addr
+ if (opts.entrypoint):
+ entrypoint = int(opts.entrypoint, 16)
+
+ write_elf_header(elf, entrypoint, len(segments))
+ for seg in segments:
+ elf.write(seg.get_phdr())
+
+ for seg in segments:
+ elf.seek(seg.offset)
+
+ with open(seg.file, 'rb') as f:
+ data = f.read()
+ elf.write(data)
+
+
+if __name__ == '__main__':
+ main(sys.argv[1:])
diff --git a/bootimgpack/internal/sony/pack.sh b/bootimgpack/internal/sony/pack.sh
new file mode 100755
index 0000000..cc15649
--- /dev/null
+++ b/bootimgpack/internal/sony/pack.sh
@@ -0,0 +1,78 @@
+#!/bin/bash
+#**************************************************#
+# Make sony's boot.img
+# Xperia
+#**************************************************#
+
+BOOTDIR=$1
+OUTPUT=$2
+
+function usage()
+{
+ echo "Usage pack_boot_sony.sh BOOTDIR [OUTPUT]"
+ echo " BOOTDIR: the directory containing boot files to be pack"
+ echo " OUTPUT: the output directory. if not present, the out.img will be used"
+}
+
+function init_tools()
+{
+ local old_pwd=`pwd`
+ TOOL_DIR=`cd $(dirname $0); pwd`
+ TOOL_MKELF=$TOOL_DIR/mkelf.py
+ cd $old_pwd
+}
+
+function pack_bootimg()
+{
+ local old_pwd=`pwd`
+ cd $BOOTDIR
+
+ # Files
+ ADDR_TAG='addr'
+
+ ls kernel 2>&1 > /dev/null
+ if [ "$?" != "0" ];then
+ echo ">>> ERROR, kernel doesn't exist!"
+ exit 1
+ fi
+
+ FILE_KERNEL="kernel"
+ FILE_KERNEL_ADDR=$(cat "$ADDR_TAG"_"$FILE_KERNEL")
+
+ DIR_RAMDISK="RAMDISK"
+ FILE_RAMDISK="ramdisk.img"
+ FILE_RAMDISK_ADDR=$(cat "$ADDR_TAG"_"ramdisk.img")
+
+ FILE_RPM="RPM.bin"
+ FILE_RPM_ADDR=$(cat "$ADDR_TAG"_"$FILE_RPM")
+
+ OTHER_PARAM=""
+ for file in `ls *-[0-9]*`
+ do
+ addrValue=$(cat "$ADDR_TAG"_"$file")
+ OTHER_PARAM="$OTHER_PARAM $file\@$addrValue"
+ done
+
+ # Generate ramdisk file
+ cd $DIR_RAMDISK && find . | cpio -o -H newc | gzip > ../$FILE_RAMDISK && cd ..
+
+ # Make kernel-updated.elf
+ python $TOOL_MKELF -o pack.img \
+ $FILE_KERNEL@$FILE_KERNEL_ADDR \
+ $FILE_RAMDISK@$FILE_RAMDISK_ADDR,ramdisk \
+ $FILE_RPM@$FILE_RPM_ADDR,rpm \
+ $OTHER_PARAM
+ [ $? != 0 ] && exit 1
+
+ # Clear temporary files
+ rm $FILE_RAMDISK
+ cd $old_pwd
+ mv $BOOTDIR/pack.img $OUTPUT
+}
+
+# Check parameters
+[ $# -eq 0 ] && usage && exit 1;
+[ -z $2 ] && OUTPUT=out.img;
+
+init_tools;
+pack_bootimg;
diff --git a/bootimgpack/internal/sony/unpack.sh b/bootimgpack/internal/sony/unpack.sh
new file mode 100755
index 0000000..d2b79ca
--- /dev/null
+++ b/bootimgpack/internal/sony/unpack.sh
@@ -0,0 +1,52 @@
+#!/bin/bash
+
+#!/bin/bash
+
+# unpack_boot_sony.sh
+# Unpack the boot.img or recovery.img of MTK format
+#
+# @author: duanqz@gmail.com
+#
+
+BOOTIMG=$1
+OUTPUT=$2
+
+function usage()
+{
+ echo "Usage unpack_boot_sony.sh BOOTIMG [OUTPUT]"
+ echo " BOOTIMG: the file path of the boot.img to be unpack"
+ echo " OUTPUT: the output directory. if not present, the current OUT/ will be used"
+}
+
+function init_tools()
+{
+ local old_pwd=`pwd`
+ TOOL_DIR=`cd $(dirname $0); pwd`
+ UNPACKBOOTIMG=$TOOL_DIR/unpack_boot_sony.py
+ cd $old_pwd
+}
+
+function unpack_bootimg()
+{
+ # Unpack boot image
+ $UNPACKBOOTIMG $BOOTIMG $OUTPUT
+ [ $? != 0 ] && exit 1
+}
+
+function check_result()
+{
+ [ ! -e $OUTPUT/kernel ] && exit 1
+ [ ! -e $OUTPUT/RAMDISK/init.rc ] && exit 1
+}
+
+
+### Start Script ###
+
+# Check parameters
+[ $# -eq 0 ] && usage && exit 1;
+[ -z $2 ] && OUTPUT="OUT/";
+
+init_tools;
+unpack_bootimg;
+check_result;
+exit 0
diff --git a/bootimgpack/internal/sony/unpack_boot_sony.py b/bootimgpack/internal/sony/unpack_boot_sony.py
new file mode 100755
index 0000000..40de394
--- /dev/null
+++ b/bootimgpack/internal/sony/unpack_boot_sony.py
@@ -0,0 +1,64 @@
+#!/usr/bin/env python
+# vim:fileencoding=utf-8
+
+import sys
+import os
+import struct
+import shutil
+
+outName = ("kernel","ramdisk.img","RPM.bin")
+
+def getSegNum(f):
+ f.seek(44)
+ d = f.read(2)
+ return struct.unpack(' 100:
+ return -1 # not a sony img, just return
+ f.seek(52)
+
+ fBaseName = os.path.split(fname)[1]
+ segs = [readSegInfo(f) for i in range(n)]
+ for i, seg in enumerate(segs):
+ f.seek(seg[1])
+ data = f.read(seg[0])
+
+ if i < 3:
+ outFileName = "%s" %(outName[i])
+ else:
+ outFileName = "%s-%s" %(fBaseName,i)
+
+ with open(outFileName, 'wb') as wf:
+ wf.write(data)
+
+ addrFileName="addr_%s" %(outFileName)
+ with open(addrFileName, 'wb') as wfaddr:
+ wfaddr.write(hex(seg[2]))
+
+ if i == 1:
+ ramdiskDir = "RAMDISK"
+ os.mkdir(ramdiskDir)
+ os.system("cd %s && gunzip < ../%s | cpio -i" %(ramdiskDir, outFileName))
+ os.remove(outFileName)
+
+if __name__ == '__main__':
+ if len(sys.argv) < 2:
+ print('Which boot.img need to be unpack')
+ else:
+ main(*sys.argv[1:])
diff --git a/bootimgpack/internal/toolkit.xml b/bootimgpack/internal/toolkit.xml
new file mode 100644
index 0000000..2272f26
--- /dev/null
+++ b/bootimgpack/internal/toolkit.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+ common/unpack.sh
+ common/pack.sh
+
+
+ common-v1/unpack.sh
+ common-v1/pack.sh
+
+
+ qcom/unpack.sh
+ qcom/pack.sh
+
+
+ mtk/unpack.sh
+ mtk/pack.sh
+
+
+ mtk-v1/unpack.sh
+ mtk-v1/pack.sh
+
+
+ sony/unpack.sh
+ sony/pack.sh
+
+
diff --git a/bootimgpack/pack_bootimg.py b/bootimgpack/pack_bootimg.py
new file mode 100755
index 0000000..77abb4c
--- /dev/null
+++ b/bootimgpack/pack_bootimg.py
@@ -0,0 +1,51 @@
+#!/usr/bin/python
+
+# Copyright 2015 duanqz
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+"""
+Usage: pack_bootimg.py BOOT_IMG_DIR [OUTPUT_IMG]
+ - BOOT_IMG_DIR : the directory of boot image files.
+ - OUTPUT_IMG : the output image after pack. If not present, BOOT_IMG_DIR.img will be used
+"""
+
+
+__author__ = 'duanqz@gmail.com'
+
+
+from internal import bootimg
+import sys
+import traceback
+
+
+if __name__ == '__main__':
+ argc = len(sys.argv)
+ if argc <= 1:
+ print __doc__
+ exit(1)
+
+ if argc > 1:
+ boot_dir = sys.argv[1]
+ output = boot_dir + ".img"
+
+ if argc > 2:
+ output = sys.argv[2]
+
+ try:
+ bootimg.pack(boot_dir, output)
+ except ValueError as ve:
+ traceback.print_exc()
+ # See help.xml ERR_PACK_BOOTIMG_FAILED
+ sys.exit(154)
diff --git a/bootimgpack/pull/__init__.py b/bootimgpack/pull/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/bootimgpack/pull/command.py b/bootimgpack/pull/command.py
new file mode 100644
index 0000000..18eb2f5
--- /dev/null
+++ b/bootimgpack/pull/command.py
@@ -0,0 +1,253 @@
+'''
+Created on Jul 31, 2014
+
+@author: tangliuxiang
+'''
+
+import subprocess
+import tempfile
+import os
+import sys
+
+sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
+from formatters.log import Log
+
+DEBUG = False
+DD_BLOCK_SIZE = 512
+
+class Shell(object):
+
+ """ Subprocess to run shell command
+ """
+
+ def run(self, cmd, out=None, printout=DEBUG):
+ """ Run command in shell.
+ """
+ if printout is False:
+ cmd = "%s 2>&1 > /dev/null" % cmd
+
+ subp = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+
+ while True:
+ buff = subp.stdout.readline().strip('\n')
+ if printout:
+ print buff
+ if out is not None:
+ out.write(buff)
+ if buff == '' and subp.poll() != None:
+ break
+
+ if subp.returncode > 0:
+ return False
+
+ return True
+
+class AdbShell(Shell):
+ """ Subprocess to run shell command
+ """
+ ANDROID_TMP = "/data/local/tmp"
+ TAG = "adb"
+ def run(self, cmd, out=None, printout=DEBUG):
+ """ Run command in shell.
+ """
+ outTmp = None
+ if out is not None:
+ outTmp = tempfile.mktemp(dir=AdbShell.ANDROID_TMP)
+ cmd = 'echo \'%s > %s && chmod 777 %s; exit $?\'| adb shell' % (cmd, outTmp, outTmp)
+ else:
+ cmd = 'echo \'%s; exit $?\' | adb shell' % cmd
+ ret = self.__superrun__(cmd, None, printout)
+
+ if outTmp is not None and out is not None:
+ self.pull(outTmp, out, False)
+ self.__superrun__("rm -r %s" % outTmp, None, False)
+
+ return ret
+
+ def __superrun__(self, cmd, out=None, printout=DEBUG):
+ self.waitdevices()
+ return super(AdbShell, self).run(cmd, out, printout)
+
+ def push(self, inFile, outFile, printout=DEBUG):
+ cmd = "adb push %s %s" % (inFile, outFile)
+ return self.__superrun__(cmd)
+
+ def pull(self, inFile, outFile, printout=DEBUG):
+ cmd = "adb pull %s %s" % (inFile, outFile)
+ return self.__superrun__(cmd)
+
+ def waitdevices(self, printout=DEBUG):
+ if printout:
+ Log.i(AdbShell.TAG, "waiting for devices....")
+ return super(AdbShell, self).run("adb wait-for-device", None, printout)
+
+class SuShell(AdbShell):
+ '''
+ classdocs
+ '''
+
+ def run(self, cmd, out=None, printout=DEBUG):
+ """ Run command in shell.
+ """
+ cmd = "su -c \"%s\"" % (cmd)
+ return super(SuShell, self).run(cmd, out, printout)
+
+class ShellFactory(object):
+ mShell = None
+
+ @staticmethod
+ def __getRootShell__():
+ subp = subprocess.Popen(["check-su"], stdout=subprocess.PIPE)
+ subp.communicate()
+ if subp.returncode == 0:
+ Log.i("AdbShell", "use su to root")
+ return SuShell()
+ else:
+ Log.i("AdbShell", "Can not use su to root, assume your phone has already been root with modify default.prop in boot!")
+ Log.i("AdbShell", "Try adb root, it may be blocked!")
+ subp = subprocess.Popen(["adb", "root"], stdout=subprocess.PIPE)
+ subp.communicate()
+ Log.i("AdbShell", "Root successfull")
+ return AdbShell()
+
+ @staticmethod
+ def getDefaultShell():
+ if ShellFactory.mShell is None:
+ ShellFactory.mShell = ShellFactory.__getRootShell__()
+ return ShellFactory.mShell
+
+class AndroidFile():
+
+ def __init__(self, path, shell=None):
+ self.mPath = path
+ if shell is None:
+ self.mShell = ShellFactory.getDefaultShell()
+ else:
+ self.mShell = shell
+
+ def getPath(self):
+ return self.mPath
+
+ def read(self, start=0, size= -DD_BLOCK_SIZE):
+ outStr = None
+
+ skip = start / DD_BLOCK_SIZE
+ count = size / DD_BLOCK_SIZE + 1
+
+ phoneTmp = self.__readToPhoneTmp__(skip, count)
+ if phoneTmp is not None:
+ pcTmp = self.__pullToPc__(phoneTmp)
+
+ if pcTmp is not None and os.path.isfile(pcTmp):
+ pcTmpFile = file(pcTmp)
+
+ if size > 0:
+ pcStart = start % DD_BLOCK_SIZE
+ pcEnd = (size % DD_BLOCK_SIZE - DD_BLOCK_SIZE) % DD_BLOCK_SIZE
+
+ pcTmpFile.seek(pcStart)
+ outStr = file(pcTmp).read()[:pcEnd]
+
+ else:
+ outStr = file(pcTmp).read()
+
+ os.remove(pcTmp)
+ AndroidFile(phoneTmp).remove()
+
+ return outStr
+
+ def pull(self, dst, start=0, size= -DD_BLOCK_SIZE):
+ skip = start / DD_BLOCK_SIZE
+ count = size / DD_BLOCK_SIZE + 1
+
+ phoneTmp = self.__readToPhoneTmp__(skip, count)
+ if phoneTmp is not None:
+ self.__pullToPc__(phoneTmp, dst)
+
+ AndroidFile(phoneTmp).remove()
+ if os.path.isfile(dst):
+ return True
+
+ return False
+
+ def remove(self):
+ return self.mShell.run("rm -r %s" % (self.mPath))
+
+ def append(self, fstr):
+ return self.__writeinternal__(fstr, True)
+
+ def write(self, fstr):
+ return self.__writeinternal__(fstr, False)
+
+ def dd_write(self, pcFile, start=0, size= -DD_BLOCK_SIZE):
+ if size > os.path.getsize(pcFile):
+ size = -DD_BLOCK_SIZE
+
+ skip = start / DD_BLOCK_SIZE
+ count = size / DD_BLOCK_SIZE + 1
+ inFile = self.__pushToPhoneTmp__(pcFile)
+
+ if count <= 0:
+ cmd = "dd if=%s of=%s seek=%s; chmod 777 %s" % (inFile, self.mPath, skip, self.mPath)
+ else:
+ cmd = "dd if=%s of=%s seek=%s count=%s; chmod 777 %s" % (inFile, self.mPath, skip, count, self.mPath)
+
+ print cmd
+ return self.mShell.run(cmd)
+
+ def __writeToPcTmp__(self, fstr):
+ inFilePath = tempfile.mktemp()
+ inFile = file(inFilePath, "w+")
+ inFile.write(fstr)
+ inFile.close()
+
+ return inFilePath
+
+ def exist(self):
+ outTmp = tempfile.mktemp()
+ ret = False
+ if self.mShell.run(r'if [ -e %s ]; then echo True; else echo False; fi' % (self.mPath), outTmp):
+ if os.path.isfile(outTmp) and file(outTmp).read().strip("\n") == "True":
+ ret = True
+ os.remove(outTmp)
+ return True
+
+ def __readToPhoneTmp__(self, skip, count):
+ outFile = tempfile.mktemp(dir=AdbShell.ANDROID_TMP)
+ if count <= 0:
+ cmd = "dd if=%s of=%s skip=%s; chmod 777 %s" % (self.mPath, outFile, skip, outFile)
+ else:
+ cmd = "dd if=%s of=%s skip=%s count=%s; chmod 777 %s" % (self.mPath, outFile, skip, count, outFile)
+
+ if self.mShell.run(cmd):
+ return outFile
+ else:
+ return None
+
+ def __pullToPc__(self, phoneTmp, pcOut=None):
+ if pcOut is None:
+ pcOut = tempfile.mktemp()
+ self.mShell.pull(phoneTmp, pcOut)
+
+ return pcOut
+
+ def __pushToPhoneTmp__(self, inFilePath):
+ outFile = tempfile.mktemp(dir=AdbShell.ANDROID_TMP)
+ if self.mShell.push(inFilePath, outFile):
+ return outFile
+ else:
+ return None
+
+ def __writeinternal__(self, fstr, append=True):
+ inFilePath = self.__writeToPcTmp__(fstr)
+ outFile = self.__pushToPhoneTmp__(inFilePath)
+
+ if append:
+ ret = self.mShell.run("cat %s >> %s" % (outFile, self.mPath), str)
+ else:
+ ret = self.mShell.run("cat %s > %s" % (outFile, self.mPath), str)
+
+ AndroidFile(outFile).remove()
+ os.remove(inFilePath)
+ return ret
+
diff --git a/bootimgpack/pull/fstab.py b/bootimgpack/pull/fstab.py
new file mode 100644
index 0000000..75d22c7
--- /dev/null
+++ b/bootimgpack/pull/fstab.py
@@ -0,0 +1,152 @@
+'''
+Created on Jul 31, 2014
+
+@author: tangliuxiang
+'''
+
+from xml.dom import minidom
+import os
+from command import AndroidFile
+import sys
+
+sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
+from formatters.log import Log
+
+class entry(object):
+ ERROR_VALUE = -1
+
+ def __init__(self, strline, fsconfig):
+ self.mFsconfig = fsconfig
+ self.mSplitArr = strline.split()
+
+ def length(self):
+ return len(self.mSplitArr)
+
+ def getByKey(self, key):
+ return self.get(self.mFsconfig.position(key))
+
+ def get(self, idx=None):
+ if idx < 0 or idx >= len(self.mSplitArr):
+ return None
+ return self.mSplitArr[idx]
+
+class fstabconfig(object):
+ ARRAY_TAG = "array"
+ ITEM_TAG = "item"
+ BLOCK_TAG = "block"
+
+ ATTR_NAME = "attr"
+ NAME = "name"
+ ERROR_VALUE = -1
+
+ ATTR_BLOCK = "block"
+ ATTR_FSTYPE = "fstype"
+ ATTR_MP = "mp"
+ ATTR_LENGTH = "length"
+ ATTR_SIZE = "size"
+ ATTR_START = "start"
+ ATTR_PROP = "prop"
+ ATTR_STAT = "stat"
+
+ def __init__(self, config):
+ self.mConfig = config
+ self.mParsed = False
+ self.mAttrArray = []
+ self.mBlockDict = {}
+ self.__parse__()
+
+ def __parse__(self):
+ if self.mParsed is True:
+ return
+
+ root = minidom.parse(self.mConfig).documentElement
+ for array in root.getElementsByTagName(fstabconfig.ARRAY_TAG):
+ if array.nodeType == minidom.Node.ELEMENT_NODE:
+ if array.getAttribute(fstabconfig.NAME) == fstabconfig.ATTR_NAME:
+ for item in array.getElementsByTagName(fstabconfig.ITEM_TAG):
+ if item.nodeType == minidom.Node.ELEMENT_NODE:
+ self.mAttrArray.append(item.childNodes[0].nodeValue)
+
+ for block in root.getElementsByTagName(fstabconfig.BLOCK_TAG):
+ if block.nodeType == minidom.Node.ELEMENT_NODE:
+ self.mBlockDict[block.getAttribute(fstabconfig.NAME)] = block.childNodes[0].nodeValue.strip('"')
+
+ self.mParsed = True
+
+ def position(self, name):
+ if self.mAttrArray is None or name is None:
+ return fstabconfig.ERROR_VALUE
+
+ idx = 0
+ while idx < len(self.mAttrArray):
+ if self.mAttrArray[idx] == name:
+ return idx
+ idx = idx + 1
+ return fstabconfig.ERROR_VALUE
+
+ def getRealName(self, block):
+ if self.mBlockDict.has_key(block):
+ return self.mBlockDict[block]
+ else:
+ return None
+
+ def getBlockByRealName(self, realName):
+ for bn in self.mBlockDict.keys():
+ if self.mBlockDict[bn] == realName:
+ return bn
+ return None
+
+
+class fstab(object):
+ '''
+ classdocs
+ '''
+
+ def __init__(self, fsfile, fsconfig):
+ '''
+ Constructor
+ '''
+ self.mFsconfig = fsconfig
+ self.mFile = fsfile
+ self.mEntryDict = {}
+ self.mParsed = False
+ self.parse()
+
+ def parse(self):
+ if self.mParsed:
+ return
+
+ for line in self.mFile.read().splitlines():
+ strpLine = line.strip()
+ if len(strpLine) > 0 and strpLine[0] != "#":
+ etr = entry(strpLine, self.mFsconfig)
+ realName = etr.getByKey(fstabconfig.ATTR_BLOCK)
+ if realName is not None:
+ block = self.mFsconfig.getBlockByRealName(realName)
+ if block is not None:
+ self.mEntryDict[block] = etr
+
+ self.mParsed = True
+
+ def getEntry(self, block):
+ if self.mEntryDict.has_key(block):
+ return self.mEntryDict[block]
+ else:
+ return None
+
+ def getMp(self, block):
+ return self.get(block, fstabconfig.ATTR_MP)
+
+ def get(self, block, attr):
+ etr = self.getEntry(block)
+ if etr is not None:
+ return etr.getByKey(attr)
+ else:
+ return None
+
+if __name__ == "__main__":
+ fsconfig = fstabconfig(os.path.join(os.path.dirname(os.path.abspath(__file__)), "mtk_fstab.xml"))
+ fs = fstab(AndroidFile("/proc/dumchar_info"), fsconfig)
+ print fs.getMp("boot")
+ print fs.getMp("recovery")
+ print fs.getMp("system")
\ No newline at end of file
diff --git a/bootimgpack/pull/imagetype.py b/bootimgpack/pull/imagetype.py
new file mode 100644
index 0000000..fe64821
--- /dev/null
+++ b/bootimgpack/pull/imagetype.py
@@ -0,0 +1,93 @@
+'''
+Created on Aug 1, 2014
+
+@author: tangliuxiang
+'''
+
+import tempfile
+import os
+import shutil
+
+import sys
+
+from internal import bootimg
+
+sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
+from common.andprop import andprop
+
+BOOT = "boot"
+RECOVERY = "recovery"
+SYSTEM = "system"
+CACHE = "cache"
+DATA = "data"
+
+class imagetype(object):
+ '''
+ classdocs
+ '''
+ STAT_NONE = 0
+ STAT_UNPACKED = 1
+ STAT_WRONG_IMG = -1
+
+ RAMDISK = "RAMDISK"
+ INIT = os.path.join(RAMDISK, "init")
+ INIT_RC = os.path.join(RAMDISK, "init.rc")
+ DEFAULT_PROP = os.path.join(RAMDISK, "default.prop")
+
+ ETC = os.path.join(RAMDISK, "etc")
+ RECOVERY_FSTAB = os.path.join(ETC, "recovery.fstab")
+
+ SBIN = os.path.join(RAMDISK, "sbin")
+ RECOVERY_BIN = os.path.join(SBIN, "recovery")
+
+ DEVICE_PROP = "ro.product.device"
+
+ def __init__(self, img):
+ '''
+ Constructor
+ '''
+ self.mImg = img
+ self.mUnpackDir = None
+ self.mStatus = imagetype.STAT_NONE
+
+ def unpack(self):
+ if self.mUnpackDir is None:
+ self.mUnpackDir = tempfile.mkdtemp()
+ os.removedirs(self.mUnpackDir)
+
+ try:
+ bootimg.unpack(self.mImg, self.mUnpackDir)
+ except:
+ self.mStatus = imagetype.STAT_WRONG_IMG
+
+ return self.mUnpackDir
+
+ def __getFile__(self, f):
+ return os.path.join(self.mUnpackDir, f)
+
+ def getunpackdir(self):
+ return self.mUnpackDir
+
+ def getType(self):
+ if self.mStatus == imagetype.STAT_NONE:
+ self.unpack()
+
+ if self.mStatus < imagetype.STAT_NONE:
+ return None
+
+ iType = None
+ if os.path.isdir(self.mUnpackDir):
+ if os.path.isfile(self.__getFile__(imagetype.INIT)) \
+ and os.path.isfile(self.__getFile__(imagetype.INIT_RC)) \
+ and os.path.isfile(self.__getFile__(imagetype.DEFAULT_PROP)):
+ if os.path.isfile(self.__getFile__(imagetype.RECOVERY_BIN)) \
+ and os.path.isfile(self.__getFile__(imagetype.RECOVERY_FSTAB)) \
+ and andprop(self.__getFile__(imagetype.DEFAULT_PROP)).get(imagetype.DEVICE_PROP) is not None:
+ iType = RECOVERY
+ else:
+ iType = BOOT
+ return iType
+
+ def exit(self):
+ if self.mUnpackDir is not None and os.path.isdir(self.mUnpackDir):
+ shutil.rmtree(self.mUnpackDir)
diff --git a/bootimgpack/pull/kk_fstab.xml b/bootimgpack/pull/kk_fstab.xml
new file mode 100644
index 0000000..4d9304c
--- /dev/null
+++ b/bootimgpack/pull/kk_fstab.xml
@@ -0,0 +1,18 @@
+
+
+
+
+ - mp
+ - block
+ - fstype
+ - prop
+ - stat
+
+
+ "/boot"
+ "/recovery"
+ "/system"
+ "/cache"
+ "/data"
+
+
\ No newline at end of file
diff --git a/bootimgpack/pull/mtk_fstab.xml b/bootimgpack/pull/mtk_fstab.xml
new file mode 100644
index 0000000..13c715f
--- /dev/null
+++ b/bootimgpack/pull/mtk_fstab.xml
@@ -0,0 +1,19 @@
+
+
+
+
+ - block
+ - size
+ - start
+ - fstype
+ - mp
+ - length
+
+
+ "bootimg"
+ "recovery"
+ "android"
+ "cache"
+ "usrdata"
+
+
\ No newline at end of file
diff --git a/bootimgpack/pull/mtkpull.py b/bootimgpack/pull/mtkpull.py
new file mode 100644
index 0000000..c7b4bee
--- /dev/null
+++ b/bootimgpack/pull/mtkpull.py
@@ -0,0 +1,109 @@
+'''
+Created on Jul 31, 2014
+
+@author: tangliuxiang
+'''
+import os
+import sys
+import imagetype
+
+from fstab import fstabconfig
+from fstab import fstab
+from command import AndroidFile
+from fstab import entry
+from pull import pull
+
+sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
+from formatters.log import Log
+
+LOG_TAG = "mtkpull"
+
+class mtkEntry(entry):
+ def __init__(self, name, etr):
+ self.mName = name
+ self.mEntry = etr
+
+ assert self.mEntry is not None, "Wrong block mount info ....."
+
+ self.mBlockName = self.getByKey(fstabconfig.ATTR_BLOCK)
+ self.mMp = self.getByKey(fstabconfig.ATTR_MP)
+ self.mSize = int(self.getByKey(fstabconfig.ATTR_SIZE), 16)
+ self.mStart = int(self.getByKey(fstabconfig.ATTR_START), 16)
+ self.mFstype = self.getByKey(fstabconfig.ATTR_FSTYPE)
+
+ def length(self):
+ return self.mEntry.length()
+
+ def getByKey(self, key):
+ return self.mEntry.getByKey(key)
+
+ def get(self, idx=None):
+ return self.mEntry.get(idx)
+
+class mtkpull(pull):
+ '''
+ classdocs
+ '''
+ MTK_DUMCHAR_INFO = "/proc/dumchar_info"
+ MTK_FSTAB_CONFIG = os.path.join(os.path.dirname(os.path.abspath(__file__)), "mtk_fstab.xml")
+ mPull = None
+
+ def __init__(self):
+ '''
+ Constructor
+ '''
+ super(mtkpull, self).__init__()
+ self.mFstabConfig = fstabconfig(mtkpull.getFstabconfigFile())
+ self.mFstab = fstab(AndroidFile(mtkpull.MTK_DUMCHAR_INFO), self.mFstabConfig)
+
+ Log.d(LOG_TAG, "work dir: %s" %(self.mWorkdir))
+
+ self.mBootImg = os.path.join(self.mWorkdir, "boot.img")
+ self.mRecoveryImg = os.path.join(self.mWorkdir, "recovery.img")
+
+ @staticmethod
+ def getFstabconfigFile():
+ return mtkpull.MTK_FSTAB_CONFIG
+
+ @staticmethod
+ def getInstance():
+ if mtkpull.mPull is None:
+ mtkpull.mPull = mtkpull()
+ return mtkpull.mPull
+
+ @staticmethod
+ def do(outDir=None):
+ p = mtkpull.getInstance()
+ p.__pull__()
+
+ if os.path.isfile(p.mBootImg):
+ bootimg = imagetype.imagetype(p.mBootImg)
+ assert bootimg.getType() == imagetype.BOOT, "Wrong boot.img....."
+ p.mImgDict[imagetype.BOOT] = p.mBootImg
+ bootimg.exit()
+
+ if os.path.isfile(p.mRecoveryImg):
+ recoveryimg = imagetype.imagetype(p.mRecoveryImg)
+ assert recoveryimg.getType() == imagetype.RECOVERY, "Wrong recovery.img....."
+ p.mImgDict[imagetype.RECOVERY] = p.mRecoveryImg
+ recoveryimg.exit()
+ p.out(outDir)
+ if os.path.isfile(os.path.join(outDir, "boot.img")) \
+ and os.path.isfile(os.path.join(outDir, "recovery.img")):
+ return True
+ return False
+
+ def __pull__(self):
+ bootEntry = mtkEntry(imagetype.BOOT, self.mFstab.getEntry(imagetype.BOOT))
+ Log.i(LOG_TAG, "Try to pull boot partition from device ...")
+ adBoot = AndroidFile(bootEntry.mMp)
+ adBoot.pull(self.mBootImg, bootEntry.mStart, bootEntry.mSize)
+
+ recoveryEntry = mtkEntry(imagetype.RECOVERY, self.mFstab.getEntry(imagetype.RECOVERY))
+ Log.i(LOG_TAG, "Try to pull recovery partition from device ...")
+ adRecovery = AndroidFile(recoveryEntry.mMp)
+ adRecovery.pull(self.mRecoveryImg, recoveryEntry.mStart, recoveryEntry.mSize)
+
+ @staticmethod
+ def isMtkDevice():
+ return AndroidFile(mtkpull.MTK_DUMCHAR_INFO).exist()
diff --git a/bootimgpack/pull/mtkpush.py b/bootimgpack/pull/mtkpush.py
new file mode 100644
index 0000000..40eccff
--- /dev/null
+++ b/bootimgpack/pull/mtkpush.py
@@ -0,0 +1,30 @@
+'''
+Created on Sep 17, 2014
+
+@author: tangliuxiang
+'''
+from bootimgpack.pull.fstab import fstab
+from bootimgpack.pull.command import AndroidFile
+from bootimgpack.pull.fstab import fstabconfig
+from bootimgpack.pull.mtkpull import mtkpull, mtkEntry
+
+class mtkpush(object):
+ '''
+ classdocs
+ '''
+ def __init__(self, device, fstabFile, inFile, fstab_version=1):
+ '''
+ Constructor
+ '''
+ self.mDevice = device
+ self.mFstab = fstab(AndroidFile(mtkpull.MTK_DUMCHAR_INFO), fstabconfig(mtkpull.getFstabconfigFile()))
+
+ self.mEntry = mtkEntry(self.mDevice, self.mFstab.getEntry(self.mDevice))
+ self.mInFile = inFile
+
+
+ def do(self):
+ mp = self.mEntry.mMp
+ adImage = AndroidFile(mp)
+ print ">>> start flash %s from %s to %s, start: %s, size: %s" %(self.mDevice, self.mInFile, mp, self.mEntry.mStart, self.mEntry.mSize)
+ return adImage.dd_write(self.mInFile, self.mEntry.mStart, self.mEntry.mSize)
diff --git a/bootimgpack/pull/normal_fstab.xml b/bootimgpack/pull/normal_fstab.xml
new file mode 100644
index 0000000..79465b8
--- /dev/null
+++ b/bootimgpack/pull/normal_fstab.xml
@@ -0,0 +1,17 @@
+
+
+
+
+ - block
+ - fstype
+ - mp
+ - length
+
+
+ "/boot"
+ "/recovery"
+ "/system"
+ "/cache"
+ "/data"
+
+
\ No newline at end of file
diff --git a/bootimgpack/pull/pull.py b/bootimgpack/pull/pull.py
new file mode 100644
index 0000000..7e35b0a
--- /dev/null
+++ b/bootimgpack/pull/pull.py
@@ -0,0 +1,99 @@
+'''
+Created on Jul 31, 2014
+
+@author: tangliuxiang
+'''
+from command import AndroidFile
+import tempfile
+import os
+import imagetype
+import shutil
+import string
+import sys
+
+sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
+from formatters.log import Log
+
+LOG_TAG = "pull"
+
+class pull(object):
+ '''
+ classdocs
+ '''
+ mPull = None
+ PROC_PARTITIONS = "/proc/partitions"
+ BLOCK_DIR = "/dev/block"
+
+ MIN_SIZE = 4096
+ MAX_SIZE = 51200
+
+ def __init__(self):
+ '''
+ Constructor
+ '''
+ self.mWorkdir = tempfile.mkdtemp()
+ self.mImgDict = {}
+ self.mOutDir = os.getcwd()
+
+ @staticmethod
+ def getInstance():
+ if pull.mPull is None:
+ pull.mPull = pull()
+ return pull.mPull
+
+ def getAdPartitions(self, minsize, maxsize):
+ adPt = AndroidFile(pull.PROC_PARTITIONS)
+ assert adPt.exist(), "File %s is not exist in phone!" %(pull.PROC_PARTITIONS)
+
+ outAdDict = {}
+ Log.i(LOG_TAG, "Try to create block of partitions ...")
+ for etr in adPt.read().splitlines():
+ stripEtr = etr.strip("\n")
+ if len(stripEtr) > 0 and stripEtr[0] != "#":
+ splitArray = stripEtr.split()
+ if len(splitArray) == 4:
+ try:
+ blkSize = string.atoi(splitArray[2])
+ except:
+ continue
+ blkName = splitArray[3]
+ adBlk = AndroidFile("%s/%s" %(pull.BLOCK_DIR, blkName))
+ if blkSize >= minsize and blkSize <= maxsize and adBlk.exist():
+ outAdDict[blkName] = adBlk
+ Log.i(LOG_TAG, "Create block of partitions done!")
+ return outAdDict
+
+ def __pull__(self, adDict):
+ Log.i(LOG_TAG, "Pull blocks from device ...")
+ for blkName in adDict.keys():
+ pcOut = os.path.join(self.mWorkdir, blkName)
+ Log.i(LOG_TAG, "Pull %s to %s" %(blkName, pcOut))
+ if adDict[blkName].pull(pcOut):
+ Log.i(LOG_TAG, "...")
+ img = imagetype.imagetype(pcOut)
+ itype = img.getType()
+ if itype is not None:
+ self.mImgDict[itype] = pcOut
+ img.exit()
+ if len(self.mImgDict.keys()) >= 2: # both boot and rec had found
+ return
+
+ def out(self, outDir = None):
+ if outDir is None:
+ outDir = self.mOutDir
+ for itype in self.mImgDict.keys():
+ outFile = os.path.join(outDir, "%s.img" %(itype))
+ shutil.copyfile(self.mImgDict[itype], outFile)
+ Log.i(LOG_TAG, "Out: %s" %(outFile))
+ shutil.rmtree(self.mWorkdir)
+
+ @staticmethod
+ def do(outDir=None):
+ p = pull.getInstance()
+ adDict = p.getAdPartitions(pull.MIN_SIZE, pull.MAX_SIZE)
+ p.__pull__(adDict)
+ p.out(outDir)
+ if os.path.isfile(os.path.join(outDir, "boot.img")) \
+ and os.path.isfile(os.path.join(outDir, "recovery.img")):
+ return True
+ return False
diff --git a/bootimgpack/pull/push.py b/bootimgpack/pull/push.py
new file mode 100644
index 0000000..2c3c8ea
--- /dev/null
+++ b/bootimgpack/pull/push.py
@@ -0,0 +1,37 @@
+'''
+Created on Sep 17, 2014
+
+@author: tangliuxiang
+'''
+from bootimgpack.pull.fstab import fstab
+from bootimgpack.pull.command import AndroidFile
+from bootimgpack.pull.fstab import fstabconfig, entry
+import os
+
+class push(object):
+ '''
+ classdocs
+ '''
+ NORMAL_FSTAB_CONFIG = os.path.join(os.path.dirname(os.path.abspath(__file__)), "normal_fstab.xml")
+ KK_FSTAB_CONFIG = os.path.join(os.path.dirname(os.path.abspath(__file__)), "kk_fstab.xml")
+
+ def __init__(self, device, fstabFile, inFile, fstab_version=1):
+ '''
+ Constructor
+ '''
+ self.mDevice = device
+
+ if fstab_version == 1:
+ self.mFstab = fstab(file(fstabFile), fstabconfig(push.NORMAL_FSTAB_CONFIG))
+ else:
+ self.mFstab = fstab(file(fstabFile), fstabconfig(push.KK_FSTAB_CONFIG))
+
+ self.mEntry = self.mFstab.getEntry(self.mDevice)
+ self.mInFile = inFile
+
+
+ def do(self):
+ mp = self.mEntry.getByKey(fstabconfig.ATTR_MP)
+ print ">>> start flash %s from %s to %s" %(self.mDevice, self.mInFile, mp)
+ adImage = AndroidFile(mp)
+ return adImage.dd_write(self.mInFile)
diff --git a/bootimgpack/pull/utils.py b/bootimgpack/pull/utils.py
new file mode 100644
index 0000000..9345b77
--- /dev/null
+++ b/bootimgpack/pull/utils.py
@@ -0,0 +1,65 @@
+'''
+Created on Jul 31, 2014
+
+@author: tangliuxiang
+'''
+import os
+import sys
+sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
+
+from bootimgpack.pull.push import push
+from bootimgpack.pull.mtkpush import mtkpush
+from command import AdbShell, SuShell
+from formatters.log import Log
+from mtkpull import mtkpull
+from pull import pull
+
+import tempfile
+
+LOG_TAG = "pull_boot_recovery"
+
+def check():
+ AdbShell().waitdevices(True)
+
+class PullUtils():
+
+ @staticmethod
+ def pull(outDir):
+ ret = False
+ Log.i(LOG_TAG, "Begin pull boot and recovery, make sure your phone was connected and adb devices is fine!")
+ Log.i(LOG_TAG, "It may take a few minutes, please wait....")
+ check()
+ Log.i(LOG_TAG, "adb connect success.")
+ #if mtkpull.isMtkDevice() and mtkpull.do(outDir):
+ # Log.d("pull_boot_recovery", "Success use mtkpull to pull images....")
+ # ret = True
+ #else:
+ if pull.do(outDir):
+ Log.d("pull_boot_recovery", "Success to pull images....")
+ ret = True
+ assert ret == True, "Failed to pull images....."
+
+class PushUtils():
+
+ @staticmethod
+ def push(device, fstabFile, inFile, fstab_version = 1):
+ ret = False
+ Log.i("flash boot or recovery", "It may take a few minutes, please wait....")
+ check()
+ if mtkpull.isMtkDevice() and mtkpush(device, fstabFile, inFile, fstab_version).do():
+ Log.d("flash boot or recovery", "Success use mtkpush to flash images....")
+ ret = True
+ else:
+ if push(device, fstabFile, inFile, fstab_version).do():
+ Log.d("flash boot or recovery", "Success to flash images....")
+ ret = True
+ assert ret == True, "Failed to flash image %s for %s" %(inFile, device)
+
+ return ret
+
+
+
+
+
+
diff --git a/bootimgpack/pull_boot_recovery.py b/bootimgpack/pull_boot_recovery.py
new file mode 100755
index 0000000..29039cb
--- /dev/null
+++ b/bootimgpack/pull_boot_recovery.py
@@ -0,0 +1,23 @@
+#!/usr/bin/python
+'''
+Created on Jul 31, 2014
+
+@author: tangliuxiang
+'''
+import sys
+import os
+import traceback
+from pull import utils
+
+if __name__ == '__main__':
+ if len(sys.argv) >= 2:
+ outDir = sys.argv[1]
+ else:
+ outDir = os.getcwd()
+
+ try:
+ utils.PullUtils.pull(outDir)
+ except Exception as e:
+ traceback.print_exc()
+ # See help.xml ERR_PULL_BOOT_RECOVERY_FAILED
+ sys.exit(156)
\ No newline at end of file
diff --git a/bootimgpack/ui/__init__.py b/bootimgpack/ui/__init__.py
new file mode 100755
index 0000000..e69de29
diff --git a/bootimgpack/ui/main.py b/bootimgpack/ui/main.py
new file mode 100755
index 0000000..887b776
--- /dev/null
+++ b/bootimgpack/ui/main.py
@@ -0,0 +1,122 @@
+#!/usr/bin/python
+# Filename main.py
+# Main UI of bootimgpack
+#
+
+__author__ = 'duanqz@gmail.com'
+
+import os
+from os import sys, path
+sys.path.append(path.dirname(path.dirname(path.abspath(__file__))))
+from internal import bootimg
+
+from Tkinter import *
+import tkFileDialog
+
+
+class Main:
+
+ def __init__(self):
+ root = Tk()
+ self.__layout(root)
+ root.mainloop()
+
+ def __layout(self, root):
+ root.title("BootimgPack")
+ root.geometry("500x220+400+400")
+ root.resizable(width=False, height=False)
+
+ self.__layoutBootImgFrame(root)
+ self.__layoutPackBtnFrame(root)
+ self.__layoutBootDirFrame(root)
+ self.__layoutResultFrame(root)
+ pass
+
+ def __layoutBootImgFrame(self, root):
+ frame = Frame(root)
+
+ Label(frame, width=12, text="Boot Image: ", anchor=W).pack(side=LEFT)
+
+ self.__bootImgText = StringVar()
+ Entry(frame, width=40, textvariable=self.__bootImgText).pack(side=LEFT)
+
+ self.__bootImgSelect = Button(frame, text="...")
+ self.__bindButtonAction(self.__bootImgSelect, self.onClick)
+ self.__bootImgSelect.pack(padx=5, side=LEFT)
+
+ frame.pack(padx=5, pady=15)
+
+ def __layoutPackBtnFrame(self, root):
+ frame = Frame(root)
+
+ self.__packBtn = Button(frame, text="PACK ^", width=7, height=1)
+ self.__bindButtonAction(self.__packBtn, self.onClick)
+ self.__packBtn.pack(padx=30, side=LEFT)
+
+ self.__unpackBtn = Button(frame, text ="UNPACK v", width=7, height=1)
+ self.__bindButtonAction(self.__unpackBtn, self.onClick)
+ self.__unpackBtn.pack(side=LEFT)
+
+ frame.pack(padx=5, pady=5)
+
+ def __layoutBootDirFrame(self, root):
+ frame = Frame(root)
+
+ Label(frame, width=12, text="Files Directory: ", anchor=W).pack(side=LEFT)
+
+ self.__bootDirText = StringVar()
+ Entry(frame, width=40, textvariable=self.__bootDirText).pack(side=LEFT)
+
+ self.__bootDirSelect = Button(frame, text="...")
+ self.__bindButtonAction(self.__bootDirSelect, self.onClick)
+ self.__bootDirSelect.pack(padx=5, side=LEFT)
+
+ frame.pack(padx=5, pady=5)
+
+ def __layoutResultFrame(self, root):
+ frame = Frame(root, relief=SUNKEN, borderwidth=1)
+
+ self.__resultText = StringVar()
+ Label(frame, height=3, textvariable=self.__resultText, wraplength=400, anchor=NW).pack(padx=10, side=LEFT)
+ self.__resultText.set("Result")
+
+ frame.pack(padx=10, pady=20, fill=X, expand=1)
+
+ def __bindButtonAction(self, btn, command):
+ btn.bind("", command)
+ btn.bind("", command)
+
+ def onClick(self, event):
+ if event.widget == self.__bootImgSelect:
+ filename = tkFileDialog.askopenfilename(initialdir=os.path.expanduser("~"))
+ if len(filename) > 0 :
+ self.__bootImgText.set(filename)
+
+ elif event.widget == self.__bootDirSelect:
+ directory = tkFileDialog.askdirectory(initialdir=os.path.expanduser("~"))
+ if len(directory) > 0 :
+ self.__bootDirText.set(directory)
+
+ elif event.widget == self.__unpackBtn:
+ bootfile = self.__bootImgText.get()
+ output = self.__bootDirText.get()
+
+ if len(bootfile) > 0 :
+ bootimg.unpack(bootfile, output)
+ result = "Unpack " + bootfile + " --> " + output
+ self.__resultText.set(result)
+
+ elif event.widget == self.__packBtn:
+ bootfile = self.__bootDirText.get()
+ output = self.__bootImgText.get()
+
+ if len(bootfile) > 0 :
+ bootimg.pack(bootfile, output)
+ result = "Pack " + bootfile + " --> " + output
+ self.__resultText.set(result)
+
+# End of class Main
+
+### Start
+if __name__ == '__main__':
+ Main()
\ No newline at end of file
diff --git a/bootimgpack/unpack_bootimg.py b/bootimgpack/unpack_bootimg.py
new file mode 100755
index 0000000..f5fca3e
--- /dev/null
+++ b/bootimgpack/unpack_bootimg.py
@@ -0,0 +1,50 @@
+#!/usr/bin/python
+
+# Copyright 2015 duanqz
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+"""
+Usage: pack_bootimg.py BOOT_IMG [OUTPUT_DIR]
+ - BOOT_IMG : the input boot image to be unpacked
+ - OUTPUT_DIR : the output director after unpack. If not present, BOOT_IMG.img will be used
+"""
+
+__author__ = 'duanqz@gmail.com'
+
+
+from internal import bootimg
+import sys
+import traceback
+
+
+if __name__ == '__main__':
+ argc = len(sys.argv)
+ if argc <= 1:
+ print __doc__
+ exit(1)
+
+ if argc > 1:
+ boot_img = sys.argv[1]
+ output = boot_img + ".out"
+
+ if argc > 2:
+ output = sys.argv[2]
+
+ try:
+ bootimg.unpack(boot_img, output)
+ except ValueError as ve:
+ traceback.print_exc()
+ # See help.xml ERR_UNPACK_BOOTIMG_FAILED
+ sys.exit(153)
diff --git a/check-su b/check-su
new file mode 120000
index 0000000..ad87a61
--- /dev/null
+++ b/check-su
@@ -0,0 +1 @@
+su-tools/check-su
\ No newline at end of file
diff --git a/classtobosp b/classtobosp
new file mode 120000
index 0000000..0e367b1
--- /dev/null
+++ b/classtobosp
@@ -0,0 +1 @@
+smaliparser/classtobosp
\ No newline at end of file
diff --git a/common/__init__.py b/common/__init__.py
new file mode 100644
index 0000000..f446f07
--- /dev/null
+++ b/common/__init__.py
@@ -0,0 +1 @@
+__author__ = 'tangliuxiang'
diff --git a/common/andprop.py b/common/andprop.py
new file mode 100644
index 0000000..93559c0
--- /dev/null
+++ b/common/andprop.py
@@ -0,0 +1,44 @@
+'''
+Created on Aug 1, 2014
+
+@author: tangliuxiang
+'''
+
+class andprop(object):
+ def __init__(self, prop):
+ self.mProp = prop
+ self.mParsed = False
+ self.mPropDict = {}
+ self.__parsed__()
+
+ def __parsed__(self):
+ if self.mParsed is False:
+ propfile = file(self.mProp)
+ for line in propfile.readlines():
+ stripline = line.strip()
+ if len(stripline) > 0 and stripline[0] != "#":
+ try:
+ idx = stripline.index('=')
+ self.mPropDict[stripline[:idx].strip()] = stripline[idx + 1:].strip()
+ except:
+ raise "Wrong properties: %s" % (stripline)
+
+ self.mParsed = True
+
+ def get(self, key, defValue = None):
+ if self.mPropDict.has_key(key):
+ return self.mPropDict[key]
+ else:
+ return defValue
+
+ def set(self, key, value):
+ self.mPropDict[key] = value
+
+ def out(self, outPath=None):
+ if outPath == None:
+ outPath = self.mProp
+
+ outFile = file(outPath, "w+")
+ for key in self.mPropDict.keys():
+ outFile.write("%s=%s" % (key, self.mPropDict[key]))
+ outFile.close()
diff --git a/config/Makefile.other b/config/Makefile.other
new file mode 100644
index 0000000..9968f29
--- /dev/null
+++ b/config/Makefile.other
@@ -0,0 +1,10 @@
+# Some inactive Configurations can be used in Makefile
+
+##############################################################################
+# Control whether convert all resource id of #namespace:type@name#t/a form in framework from name to id
+# But framework-res and mediatek-res except, as them have been supported.
+# The default is false.
+# Set true if you actrally use other framework res with name form.
+#-----------------------------------------------------------------------------
+# ALL_FRW_NAME_TO_ID := true
+
diff --git a/config/Makefile.template b/config/Makefile.template
new file mode 100644
index 0000000..37f35db
--- /dev/null
+++ b/config/Makefile.template
@@ -0,0 +1,186 @@
+# Makefile Reference
+# Please use this file as the project Makefile reference
+
+##############################################################################
+# The value decides which base device should choose for patchall and upgrade.
+# You can configure the property according to your device.
+# The default value is base.
+# Support values: base, base_cm, base_mt6592 and other devices in the future.
+#-----------------------------------------------------------------------------
+#BASE := base_cm
+
+##############################################################################
+# The value is used for resource adapter with the aapt tool.
+# It depends on the device's resolution.
+# The default value is hdpi.
+#-----------------------------------------------------------------------------
+DENSITY := xhdpi
+
+##############################################################################
+# The value is used to config the bootanimation.
+# It depends on the device's resolution, such as 480x854, 540x960, 720x1280, 1080x1920.
+# The default value is nothing.
+#-----------------------------------------------------------------------------
+RESOLUTION :=
+
+##############################################################################
+# The value decides whether use prebuilt image or pack from the BOOT or RECOVERY directory in the vendor directory.
+# Support values: boot, recovery or nothing.
+# The default value is boot.
+# When the value is boot or recovery, the system of build will pack boot.img or recovery.img
+# from the BOOT or vendor RECOVERY in the vendor directory which might contain your changes.
+# When the value is nothing, the system of build will check boot.img or recovery.img in the project root directory.
+# If the boot.img or recovery.img exists, the system of build will use a prebuilt boot.img or recovery.img.
+# If the boot.img or recovery.img doesn't exists, the system of build will do nothing.
+#-----------------------------------------------------------------------------
+vendor_modify_images := boot
+
+##############################################################################
+# The value decides the directory which you want to remove in the vendor directory for the ota package.
+# The default value is app or pri-app which not need to configure.
+# You can configure the directory name which relative to the vendor/system directory.
+#-----------------------------------------------------------------------------
+#vendor_remove_dirs := vendor/operator/app
+
+##############################################################################
+# The value decides the file which you want to remove in the vendor directory for the ota package.
+# The default value is nothing.
+# You can configure the file name which relative to the vendor/system directory.
+#-----------------------------------------------------------------------------
+#vendor_remove_files := bin/zchgd
+
+##############################################################################
+# The value decides the vendor apk which you want to save in the vendor directory for the ota package.
+# The default value is Bluetooth.
+# You can configure the apk name in the vendor/system/app or vendor/system/priv-app directory.
+#-----------------------------------------------------------------------------
+vendor_saved_apps := BackupRestoreConfirmation Bluetooth DefaultContainerService ExternalStorageProvider FusedLocation HTMLViewer InputDevices KeyChain PicoTts PrintSpooler ProxyHandler SharedStorageBackup Shell UserDictionaryProvider
+
+##############################################################################
+# The value decides which vendor apk you want to modify.
+# The modify jar will build from the directory named the vendor apk name in the current project root directory.
+# eg: vendor_modify_apps := FMRadio
+# You need ro decode FMRadio.apk to the project directory(use apktool d FMRadio.apk) first,
+# and then you can make it by: make FMRadio
+#-----------------------------------------------------------------------------
+#vendor_modify_apps := FMRadio
+
+##############################################################################
+# The value decides which vendor jar you want to modify.
+# The modify jar will build from the *.jar.out directory in the current project root directory.
+# eg: vendor_modify_jars := android.policy
+# You need to decode android.policy.jar to the project directory (use apktool d android.policy.jar) first,
+# and then you can make it by: make android.policy
+#-----------------------------------------------------------------------------
+vendor_modify_jars := framework services telephony-common wifi-service
+
+##############################################################################
+# The value decides which board system directory you want to save.
+# The default value is nothing.
+# You can configure the board system directory path which relative to the system directory in the board release.
+#-----------------------------------------------------------------------------
+#board_saved_dirs := media/audio/ui
+
+##############################################################################
+# The value decides which board system file you want to save.
+# The default value is nothing.
+# You can configure the board system file path which relative to the system directory in the board release.
+#-----------------------------------------------------------------------------
+#board_saved_files :=
+
+##############################################################################
+# The value decides which board system apk you want to remove.
+# The default value is nothing.
+# You can configure the board system apk name in the value.
+#-----------------------------------------------------------------------------
+#board_remove_apps := LogReport
+
+##############################################################################
+# The value decides which apk you want to modify, when the apk is based on the board system apk.
+# The default value is Phone.
+# You can configure the board system apk name in the value.
+# You can modify the apk with the smali.part file or other method.
+# You may need to check if the apk in the BOARD_MODIFY_RESID_APPS(you can see it in build/configs/board_default.mk).
+# If in, you may need to change the resource id to "#type@name#t" or "#type@name#a" by the command idtoname.
+# The command idtoname how to use: first use "apktool d source/system/framework/framework-res.apk other/TMP/framework-res",
+# and then use "idtoname other/TMP/framework-res/res/values/public_master.xml XXXX/smali"(XXXX is the directory where you decode board system apk).
+#-----------------------------------------------------------------------------
+#board_modify_apps := TeleService
+
+##############################################################################
+# The value decides which jar you want to modify, when the jar is based on the board framework jar.
+# The default value is nothing.
+# You can modify the jar with the smali.part file or other method.
+# You may need to check if the jar in the BOARD_MODIFY_RESID_APPS(you can see it in build/configs/board_default.mk).
+# If in, you may need to change the resource id to "#type@name#t" or "#type@name#a" by the command idtoname.
+# The command idtoname how to use: first use "apktool d source/system/framework/framework-res.apk other/TMP/framework-res",
+# and then use "idtoname other/TMP/framework-res/res/values/public_master.xml XXXX/smali"(XXXX is the directory where you decode board system jar).
+#-----------------------------------------------------------------------------
+#board_modify_jars :=
+
+##############################################################################
+# The value decides which property you will override in the build.prop.
+# The default value is nothing.
+# You can add the property name in the value from the build.prop.
+#-----------------------------------------------------------------------------
+
+# The property decide whether hide the soft mainkeys.
+# If 1, hide the soft mainkeys. If 0, display the soft mainkeys.
+# You should configure the property according to your device.
+#override_property += \
+# qemu.hw.mainkeys=0
+
+
+# The value of the property ro.flyme.romer will be contained in the ota package name.
+# The default value is Unofficial.
+# You should configure the property according to your ID, ie, replace "Unofficial" with your ID.
+# The property ro.product.model_romer decide your ID on the backend server which statistical data for your device.
+# The default value is Nexus-6P_Unofficial.
+# You should configure the property according to your device and your ID with replace the "Nexus-6P_Unofficial".
+override_property += \
+ ro.flyme.romer=Unofficial \
+ ro.product.model_romer=Nexus-6P_Unofficial
+
+##############################################################################
+# The value decides which property you will remove from the build.prop.
+# The default value is nothing.
+# You can add the property name in the value from the build.prop.
+#-----------------------------------------------------------------------------
+# remove_property += \
+# dev.defaultwallpaper
+
+##############################################################################
+# Defines whether uses assertions in /META-INF/com/google/android/updater-script of the OTA package.
+# Assertions is used to verify the validity of the OTA package.
+# Set it to be false when you want to escape the verification.
+# Default: true
+#-----------------------------------------------------------------------------
+#USE_ASSERTIONS_IN_UPDATER_SCRIPT := false
+
+##############################################################################
+# Defines whether reduces useless resources, only keep the resources of preferred configuration, like current density or locale.
+# If set to be true, it will cost much more time to build out a system.img of which the size is reduced.
+# Default: false
+#-----------------------------------------------------------------------------
+#REDUCE_RESOURCES := true
+
+##############################################################################
+# Defines whether produces an image zipfile suitable for use with 'fastboot update'.
+# Default: false
+#-----------------------------------------------------------------------------
+#PRODUCE_IMAGES_FOR_FASTBOOT := true
+
+##############################################################################
+# Defines whether generates a block-based OTA, system.img.dat in DAT format will be produced.
+# Will fall back to a file-based OTA if the target_files is older and doesn't support block-based OTAs.
+# Default: true
+#-----------------------------------------------------------------------------
+#PRODUCE_BLOCK_BASED_OTA := false
+
+##############################################################################
+# Defines whether build an international version of package.
+# Default: false
+#-----------------------------------------------------------------------------
+#PRODUCE_INTERNATIONAL_ROM := true
+
+include $(PORT_BUILD)/main.mk
diff --git a/config/cert.py b/config/cert.py
new file mode 100644
index 0000000..3d78193
--- /dev/null
+++ b/config/cert.py
@@ -0,0 +1,115 @@
+'''
+Created on Sep 3, 2014
+
+@author: tangliuxiang
+'''
+import re
+import os
+import sys
+
+NAME = "name"
+CERTIFICATE = "certificate"
+PRIVATE_KEY = "private_key"
+PRESIGNED = "PRESIGNED"
+
+class certentry(object):
+ def __init__(self, line):
+ self.mLine = re.sub("^[ \t]*|[ \t]*$", "", line)
+ self.mDict = {}
+ self.__parse__()
+
+ def __parse__(self):
+ if len(self.mLine) > 0:
+ splitArray = self.mLine.replace("=", " ").split()
+ assert len(splitArray) % 2 == 0, "Wrong apkcerts in line: %s" %(self.mLine)
+
+ i = 0
+ while (i + 1) < len(splitArray):
+ self.mDict[splitArray[i]] = splitArray[i + 1].replace('"','')
+ i = i + 2
+
+ def get(self, key, defValue = None):
+ if self.mDict.has_key(key):
+ return self.mDict[key]
+ else:
+ return defValue
+
+class cert(object):
+ '''
+ classdocs
+ '''
+
+ def __init__(self, cf):
+ '''
+ Constructor
+ '''
+ self.mApkCerts = cf
+ self.mEntryDict = {}
+ self.__parse__()
+
+ def __parse__(self):
+ for line in file(self.mApkCerts, 'r').readlines():
+ entry = certentry(line)
+ entryName = entry.get(NAME)
+ if entryName is not None:
+ self.mEntryDict[entryName] = entry
+ else:
+ print "Warning: Wrong apkcerts in line: %s" %(line)
+ assert entryName is not None, "Wrong apkcerts in line: %s" %(line)
+
+ def getApps(self, tag, value):
+ appList = []
+ for appName in self.mEntryDict.keys():
+ if self.mEntryDict[appName].get(tag) == value:
+ appList.append(appName)
+ return appList
+
+ def getPresignedApps(self):
+ return self.getApps(CERTIFICATE, PRESIGNED)
+
+ def getAppCertificate(self, appName):
+ appName = formatAppName(appName)
+ if self.mEntryDict.has_key(appName):
+ return self.mEntryDict[appName].get(CERTIFICATE)
+ else:
+ return None
+
+ def getAppPrivateKey(self, appName):
+ appName = formatAppName(appName)
+ if self.mEntryDict.has_key(appName):
+ return self.mEntryDict[appName].get(PRIVATE_KEY)
+ else:
+ return None
+
+ def getAppCertType(self, appName):
+ ct = self.getAppCertificate(appName)
+ return formatCertType(ct)
+
+def formatAppName(appName):
+ if appName is not None:
+ (root, ext) = os.path.splitext(os.path.basename(appName))
+ return "%s.apk" %root
+ else:
+ print "Warning: Wrong parameters, appName is None"
+ return None
+
+def formatCertType(ct):
+ if ct is not None:
+ return re.sub("\..*$", "", os.path.basename(ct))
+ else:
+ return None
+
+def getPresignedApps(cf):
+ return cert(cf).getPresignedApps()
+
+def main(argv):
+ if len(argv) >= 1:
+ ct = cert(argv[0])
+ print ct.getPresignedApps()
+ print ct.getAppCertType("BaiduGallery3D")
+ print ct.getAppCertType("BaiduGallery3D.apk")
+ print ct.getAppCertType("what/app/BaiduGallery3D.apk")
+ print ct.getAppCertType("BaiduBrowser.apk")
+
+if __name__ == '__main__':
+ main(sys.argv[1:])
diff --git a/config/config_prebuilt.py b/config/config_prebuilt.py
new file mode 100755
index 0000000..567c96d
--- /dev/null
+++ b/config/config_prebuilt.py
@@ -0,0 +1,187 @@
+#!/usr/bin/env python
+
+'''
+Created on Sep 2, 2014
+
+@author: tangliuxiang
+'''
+
+import os
+import sys
+import time
+import cert
+import re
+
+PORT_ROOT = os.getenv("PORT_ROOT", os.path.dirname(os.path.dirname(__file__)))
+DEFAULT_CERT_FILE = os.path.join(PORT_ROOT, "flyme", "release", "META", "apkcerts.txt")
+
+class andprop(object):
+ def __init__(self, prop):
+ self.mProp = prop
+ self.mParsed = False
+ self.mPropDict = {}
+ self.__parsed__()
+
+ def __parsed__(self):
+ if self.mParsed is False:
+ propfile = file(self.mProp)
+ for line in propfile.readlines():
+ stripline = line.strip()
+ if len(stripline) > 0 and stripline[0] != "#":
+ try:
+ idx = stripline.index('=')
+ self.mPropDict[stripline[:idx].strip()] = stripline[idx + 1:].strip()
+ except:
+ raise "Wrong properties: %s" % (stripline)
+
+ self.mParsed = True
+
+ def get(self, key, defValue = None):
+ if self.mPropDict.has_key(key):
+ return self.mPropDict[key]
+ else:
+ return defValue
+
+ def set(self, key, value):
+ self.mPropDict[key] = value
+
+ def out(self, outPath=None):
+ if outPath == None:
+ outPath = self.mProp
+
+ outFile = file(outPath, "w+")
+ for key in self.mPropDict.keys():
+ outFile.write("%s=%s" % (key, self.mPropDict[key]))
+ outFile.close()
+
+
+
+class prebuilt(object):
+ '''
+ classdocs
+ '''
+
+ def __init__(self, out="prebuilt.mk", bsys=None, vsys=None):
+ '''
+ Constructor
+ '''
+ if bsys is None:
+ bsys = os.path.join(PORT_ROOT, "flyme/release/arm/system")
+
+ if vsys is None:
+ vsys = os.path.join(PORT_ROOT, "devices/base/vendor/system")
+
+ self.mBsys = bsys
+ self.mVsys = vsys;
+ self.mOut = out;
+
+ self.mBbuildprop = andprop(os.path.join(self.mBsys, "build.prop"))
+ self.mVbuildprop = andprop(os.path.join(self.mVsys, "build.prop"))
+
+ self.mPresignedAppArr = []
+ self.mPbDirArr = []
+ self.mPbFileArr = []
+ self.mOutStr = ""
+
+ def __getVersion__(self, bprop):
+ return bprop.get("ro.build.version.incremental", bprop.get("ro.build.display.id"))
+
+ def __getModel__(self, bprop):
+ return bprop.get("ro.product.model", bprop.get("ro.product.device"))
+
+ def do(self):
+ for root, dirs, files in os.walk(self.mBsys):
+ relRoot = os.path.relpath(root, self.mBsys)
+
+ if self.__alreadyInPrebuiltDir__(relRoot):
+ continue
+
+ for d in dirs:
+ if not os.path.exists(os.path.join(self.mVsys, relRoot, d)):
+ Log.d("ADD dir: %s" % (os.path.relpath(os.path.join(root, d), self.mBsys)))
+ self.mPbDirArr.append(os.path.relpath(os.path.join(root, d), self.mBsys))
+
+ for f in files:
+ if not os.path.exists(os.path.join(self.mVsys, relRoot, f)):
+ Log.d("ADD file: %s" % (os.path.relpath(os.path.join(root, f), self.mBsys)))
+ self.mPbFileArr.append(os.path.relpath(os.path.join(root, f), self.mBsys))
+
+ self.mPresignedAppArr = self.__getPresignedApps__()
+ self.__out__()
+
+ def __getPresignedApps__(self):
+ certFile = os.path.join(os.path.dirname(self.mBsys), "META", "apkcerts.txt")
+ if not os.path.isfile(certFile):
+ certFile = DEFAULT_CERT_FILE
+ if not os.path.isfile(certFile):
+ print "Warning: %s doesn't exist! Can not generate the %s" % (certFile, self.mOut)
+ return []
+ return cert.getPresignedApps(certFile)
+
+ def __alreadyInPrebuiltDir__(self, d):
+ while len(d) > 0:
+ for item in self.mPbDirArr:
+ if item == d:
+ return True
+ d = os.path.dirname(d)
+ return False
+
+ def __appendMk__(self, arr):
+ for item in sorted(set(arr)):
+ self.mOutStr = self.mOutStr + " %s \\\n" % item
+
+ @staticmethod
+ def __equals__(str1, str2):
+ return re.sub("^#.*$", "", str1, 0, re.M) == re.sub("^#.*$", "", str2, 0, re.M)
+
+ def __out__(self):
+ self.mOutStr = self.mOutStr + "# This file is auto generate by %s\n" % os.path.relpath(__file__, PORT_ROOT)
+
+ self.mOutStr = self.mOutStr + "# version: from %s(%s) ==> %s(%s)\n" % \
+ (self.__getVersion__(self.mVbuildprop),
+ self.__getModel__(self.mVbuildprop),
+ self.__getVersion__(self.mBbuildprop),
+ self.__getModel__(self.mBbuildprop))
+ self.mOutStr = self.mOutStr + "# Date: %s\n\n" % (time.strftime("%Y/%m/%d %H:%M"))
+
+ self.mOutStr = self.mOutStr + "BOARD_PREBUILT_DIRS += \\\n"
+ self.__appendMk__(self.mPbDirArr)
+
+ self.mOutStr = self.mOutStr + "\n\nBOARD_PREBUILT += \\\n"
+ self.__appendMk__(self.mPbFileArr)
+
+ self.mOutStr = self.mOutStr + "\n\nBOARD_PRESIGNED_APPS += \\\n"
+ self.__appendMk__(self.mPresignedAppArr)
+ self.mOutStr = self.mOutStr + "\n\n # This is the end.\n"
+
+ if not os.path.isfile(self.mOut) or not prebuilt.__equals__(self.mOutStr, file(self.mOut, 'r').read()):
+ outF = file(self.mOut, 'w+')
+ outF.write(self.mOutStr)
+ outF.close()
+ else:
+ print "Already the newest prebuilt.mk, ignore....."
+
+class Log():
+ DEBUG = False
+ @staticmethod
+ def d(message):
+ if Log.DEBUG is True:
+ print message
+
+def configPrebuilt(out="prebuilt.mk", bsys=None, vsys=None):
+ prebuilt(out, bsys, vsys).do()
+
+def main(argv):
+ out = "prebuilt.mk"
+ bsys = None
+ vsys = None
+ if len(argv) >= 1:
+ out = argv[0]
+ if len(argv) >= 2:
+ bsys = argv[1]
+ if len(argv) >= 3:
+ vsys = argv[2]
+ configPrebuilt(out, bsys, vsys)
+
+if __name__ == '__main__':
+ main(sys.argv[1:])
diff --git a/config/density.cfg b/config/density.cfg
new file mode 100644
index 0000000..20fdd7b
--- /dev/null
+++ b/config/density.cfg
@@ -0,0 +1,6 @@
+120:ldpi
+160:mdpi
+240:hdpi
+320:xhdpi
+480:xxhdpi
+560:xxxhdpi
\ No newline at end of file
diff --git a/config/getresolution b/config/getresolution
new file mode 100755
index 0000000..2891e2e
Binary files /dev/null and b/config/getresolution differ
diff --git a/config/gitignore.template b/config/gitignore.template
new file mode 100644
index 0000000..0b04d48
--- /dev/null
+++ b/config/gitignore.template
@@ -0,0 +1,3 @@
+/board/*
+/autopatch/*
+/out/*
diff --git a/config/makeconfig b/config/makeconfig
new file mode 100755
index 0000000..626e646
--- /dev/null
+++ b/config/makeconfig
@@ -0,0 +1,409 @@
+#!/bin/bash
+
+##########################################
+#
+# author: coron
+# usage: used to generate Makefile for an coron project
+# date: 2013/11/21
+#
+##########################################
+
+TAG="makeconfig"
+
+WORK_DIR=$PWD
+NEW_MAKEFILE=$WORK_DIR/Makefile
+
+MAKECONFIG_ROOT=$PORT_ROOT/tools/config
+DENSITY_CFG=$MAKECONFIG_ROOT/density.cfg
+TEMPLATE_MAKEFILE=$MAKECONFIG_ROOT/Makefile.template
+VENDOR_MODIFY_JARS_SUGGEST="framework|services|telephony-common|wifi-service|android.policy"
+VENDOR_SAVED_APPS_SUGGEST="MtkBt|FMTransmitter|FMRadio|Bluetooth|FmRadio|.*Stk.*|Nfc"
+
+PULL_BOOT_RECOVERY=pull_boot_recovery
+
+GIT_IGNORE_CONFIG=$MAKECONFIG_ROOT/gitignore.template
+PRJ_GIT_IGNORE=$WORK_DIR/.gitignore
+
+FROM_OTA=0
+OTA_PACKAGE=$WORK_DIR/ota.zip
+OUT=$WORK_DIR/out
+OUT_OTA_DIR=$OUT/ota
+OUT_SYSTEM=$OUT_OTA_DIR/system/
+OUT_METAINF=$OUT_OTA_DIR/META-INF
+
+DENSITY=""
+RESOLUTION=""
+vendor_modify_jars=""
+vendor_saved_apps=""
+
+######## Error Exit Num ##########
+ERR_USB_NOT_CONNECTED=151
+ERR_DEVICE_NOT_ROOTED=152
+
+ERR_MAKEFILE_EXIST=201
+ERR_OTA_INCOMPATIBLE=202
+ERR_MISSION_FAILED=209
+
+################## adb ######################
+
+# check adb can find a device
+function checkAdbConnect()
+{
+ echo ">>> Check connecting state"
+ adb shell ls / > /dev/null 2>&1
+ if [ $? != "0" ];then
+ echo ">>> Device is not found, Please connect device and pc with USB cable, and open Adb Debug in device."
+ exit $ERR_USB_NOT_CONNECTED
+ fi
+}
+
+
+# wait for the device to be online or timeout
+function waitForDeviceOnline ()
+{
+ echo ">>> Wait for the device to be online..."
+
+ local timeout=30
+ while [ $timeout -gt 0 ]
+ do
+ if adb shell ls > /dev/null 2>&1; then
+ echo ">>> device is online"
+ break
+ fi
+ echo ">>> Device is not online, wait .."
+ sleep 3
+ timeout=$[$timeout - 3]
+ done
+ if [ $timeout -eq 0 ];then
+ echo ">>> Device not found, Please ensure adb can find your device and then rerun this script."
+ exit $ERR_USB_NOT_CONNECTED
+ fi
+}
+
+function checkAdbRoot()
+{
+ echo ">>> Check adb root state"
+ SECURE_PROP=$(adb shell cat /default.prop | grep -o "ro.secure=\w")
+ DEBUG_PROP=$(adb shell cat /default.prop | grep -o "ro.debuggable=\w")
+ if [ "$SECURE_PROP" = "ro.secure=0" -o "$DEBUG_PROP" = "ro.debuggable=1" ];then
+ echo ">>> Kernel root ready, run adb root"
+ adb root
+ waitForDeviceOnline
+ return 0
+ fi
+ return 1
+}
+
+function checkSuRoot()
+{
+ echo "exit" > exit_command
+ waitForDeviceOnline
+ adb push exit_command /data/local/tmp/
+ rm -f exit_command
+ if echo "su < /data/local/tmp/exit_command; exit" | adb shell | grep "not found" > /dev/null 2>&1;then
+ return 1
+ fi
+ return 0
+}
+
+# check device connecting state and adb root state
+function checkRootState()
+{
+ checkAdbConnect
+ checkAdbRoot
+ if [ $? == "0" ];then
+ echo ">>> Root State: Kernal Root"
+ return 0
+ fi
+ checkSuRoot
+ if [ $? == "0" ];then
+ echo ">>> Root State: System Root"
+ return 0
+ fi
+ echo ">>> Device is not a root phone."
+ exit $ERR_DEVICE_NOT_ROOTED
+}
+
+############# set up makefile ################
+
+function checkEnvironment()
+{
+ if [ -f $NEW_MAKEFILE ];then
+ echo ">>> $NEW_MAKEFILE already exist!"
+ exit $ERR_MAKEFILE_EXIST
+ fi
+
+ if [ ! -f $TEMPLATE_MAKEFILE ];then
+ echo ">>> $TEMPLATE_MAKEFILE doesn't exist! "
+ echo ">>> Make sure you do source ./build/envsetup first!"
+ exit $ERR_MISSION_FAILED
+ fi
+
+ adb shell ls / > /dev/null 2>&1
+ if [ $? != 0 -a -f $OTA_PACKAGE ];then
+ echo ">>> Device is not online, but ota.zip is exist."
+ echo ">>> Config Makefile from $OTA_PACKAGE."
+ FROM_OTA=1
+ fi
+}
+
+function checkOTAPackage()
+{
+ echo ">>> Unzip $OTA_PACKAGE ..."
+ mkdir -p $OUT
+ rm -rf $OUT_OTA_DIR
+ unzip -q $OTA_PACKAGE -d $OUT_OTA_DIR
+ if [ ! -e $OUT_SYSTEM -o ! -e $OUT_METAINF ];then
+ echo ">>> Can not find $OUT_SYSTEM or $OUT_METAINF."
+ echo ">>> Check whether $OTA_PACKAGE is a ota package."
+ exit $ERR_OTA_INCOMPATIBLE
+ fi
+ if [ -f $OUT_OTA_DIR/boot.img ];then
+ cp $OUT_OTA_DIR/boot.img $WORK_DIR/boot.img
+ fi
+ if [ -f $OUT_OTA_DIR/recovery.img ];then
+ cp $OUT_OTA_DIR/recovery.img $WORK_DIR/recovery.img
+ fi
+}
+
+function checkMtkPlatform()
+{
+ if [ $FROM_OTA != 0 ];then
+ if [ `cat $OUT_SYSTEM/build.prop | grep "mediatek" | wc -l` -gt 0 ]; then
+ MTK_PLATFORM=true
+ else
+ MTK_PLATFORM=false
+ fi
+ else
+ if [ `adb shell cat /system/build.prop | grep "mediatek" | wc -l` -gt 0 ]; then
+ MTK_PLATFORM=true
+ else
+ MTK_PLATFORM=false
+ fi
+ fi
+ echo ">>> Check MTK Platform: $MTK_PLATFORM"
+}
+
+function getResolution()
+{
+ if [ $FROM_OTA != 0 ];then
+ echo ">>> Set Resolution as default"
+ RESOLUTION="720x1280"
+ return 0
+ fi
+ RESOLUTION=$(adb shell wm size \
+ | awk 'BEGIN{FS=": "}{print $NF}')
+}
+
+function getDensity()
+{
+ if [ $FROM_OTA != 0 ];then
+ echo ">>> Set Density as default"
+ DENSITY="xhdpi"
+ return 0
+ fi
+ lcd_density=$(adb shell getprop \
+ | grep "ro.sf.lcd_density" \
+ | awk 'BEGIN{FS="["}{print $NF}' \
+ | awk 'BEGIN{FS="]"}{print $1}')
+
+ if [ $? == "0" ] && [ "x$lcd_density" != "x" ];then
+ density=$(grep "$lcd_density" $DENSITY_CFG | cut -d ':' -f2)
+ if [ $? == "0" ] && [ "x$density" != "x" ];then
+ DENSITY=$density
+ fi
+ fi
+}
+
+function getVendorModifyJars()
+{
+ frameworkListFile=$(mktemp -t -u frameworkList.XXXX)
+
+ if [ $FROM_OTA == 0 ];then
+ adb shell "if [ -f /data/local/tmp/framework-list ]; then rm /data/local/tmp/framework-list; fi"
+ adb shell "ls /system/framework/ > /data/local/tmp/framework-list"
+ adb pull /data/local/tmp/framework-list $frameworkListFile > /dev/null 2>&1
+ else
+ ls $OUT_SYSTEM/framework > $frameworkListFile
+ fi
+ if [ -e $frameworkListFile ]; then
+ vendor_modify_jars=$(grep "\.jar" $frameworkListFile \
+ | sed "s/\.jar//g" \
+ | egrep $VENDOR_MODIFY_JARS_SUGGEST \
+ | sed ":a;N;s/\n/ /g;ba")
+ rm $frameworkListFile
+ else
+ return 1
+ fi
+}
+
+function getConfigValueFromMakefile()
+{
+ local makefile=$1
+ local prop=$2
+ local result_line_count
+ local base
+
+ if [ ! -f $makefile ];then
+ echo ">>> $makefile doesn't exist!!";
+ return 1
+ fi
+
+ # get the base rom type of project
+ # if you doesn't config BOARD_BASE_DEVICE_PROP_NAME in project's makefile, just ignore this project
+ # if you config more than one BOARD_BASE_DEVICE_PROP_NAME in project's makefile, exit with error
+ result_line_count=`grep "^[ ]*$prop" $makefile -w | wc | awk '{print $1}'`
+ if [ $result_line_count = "0" ];then
+ echo ">>> $makefile's $prop does't config!!";
+ return 1
+ elif [ $result_line_count != "1" ];then
+ echo ">>> $makefile's $prop define is wrong!!";
+ exit $ERR_MISSION_FAILED;
+ else # $result_line_count == "1"
+ base=`grep "^[ ]*$prop" $makefile -w \
+ | awk -F= '{print $NF}' | sed 's/^ *//g;s/$ *//g'`
+
+ if [ $? != "0" ] || [ "x$base" = "x" ];then
+ echo ">>> Can't get base from $makefile"
+ echo ">>> Make sure \"$prop\" in $makefile is right"
+ return 1;
+ fi
+
+ echo "$base"
+ return 0
+ fi
+}
+
+function getvendorSavedApps()
+{
+ appListFile=$(mktemp -t -u appList.XXXX)
+
+ if [ $FROM_OTA == 0 ];then
+ adb shell "if [ -f /data/local/tmp/app-list ]; then rm /data/local/tmp/app-list; fi"
+ adb shell "ls /system/app/ > /data/local/tmp/app-list"
+ adb shell "ls /system/priv-app/ >> /data/local/tmp/app-list"
+ adb pull /data/local/tmp/app-list $appListFile > /dev/null 2>&1
+ else
+ ls $OUT_SYSTEM/app/ > $appListFile
+ ls $OUT_SYSTEM/priv-app/ >> $appListFile
+ fi
+
+ vendor_saved_apps_configed=$(getConfigValueFromMakefile $NEW_MAKEFILE "vendor_saved_apps")
+
+ if [ $? == "0" ] && [ "x$vendor_saved_apps_configed" != "x" ];then
+ vendor_saved_apps_configed=$(echo $vendor_saved_apps_configed \
+ | sed 's/^[ \t]*//g' \
+ | sed 's/[ \t]*$//g' \
+ | sed 's/[ \t][ \t]*/ /g' \
+ | sed 's/ /\|/g')
+
+ vendor_saved_apps_suggest=$VENDOR_SAVED_APPS_SUGGEST
+ if [ "x$vendor_saved_apps_configed" != "x" ];then
+ vendor_saved_apps_suggest="$vendor_saved_apps_suggest|$vendor_saved_apps_configed"
+ fi;
+
+ if [ -e $appListFile ]; then
+ vendor_saved_apps=$(cat $appListFile \
+ | egrep $vendor_saved_apps_suggest \
+ | sed ":a;N;s/\n/ /g;ba")
+ rm $appListFile
+ else
+ return 1
+ fi
+ fi
+}
+
+function setupMakefile()
+{
+ echo ">>> Setup the Makefile Begin!"
+
+ cp $TEMPLATE_MAKEFILE $NEW_MAKEFILE
+
+ getVendorModifyJars
+ if [ $? == "0" ] && [ "x$vendor_modify_jars" != "x" ]; then
+ echo ">>> Set vendor_modify_jars: $vendor_modify_jars"
+ sed -i "s/^[ \t]*vendor_modify_jars[ \t]*\:.*/vendor_modify_jars \:\= $vendor_modify_jars/g" $NEW_MAKEFILE
+ else
+ echo ">>> Get the files list in /system/framework failed! "
+ echo ">>> Please check the adb is ok! "
+ exit $ERR_MISSION_FAILED
+ fi
+
+ getDensity
+ if [ "x$DENSITY" != "x" ]; then
+ echo ">>> Set DENSITY: $DENSITY"
+ sed -i "s/^[ \t]*DENSITY[ \t]*\:.*/DENSITY \:\= $DENSITY/g" $NEW_MAKEFILE
+ fi
+
+ getResolution
+ if [ "x$RESOLUTION" != "x" ];then
+ echo ">>> Set RESOLUTION: $RESOLUTION"
+ sed -i "s/^[ \t]*RESOLUTION[ \t]*\:.*/RESOLUTION \:\= $RESOLUTION/g" $NEW_MAKEFILE
+ fi
+
+ getvendorSavedApps
+ if [ "x$vendor_saved_apps" != "x" ]; then
+ echo ">>> Set vendor_saved_apps: $vendor_saved_apps"
+ sed -i "s/^[ \t]*vendor_saved_apps[ \t]*\:.*/vendor_saved_apps \:\= $vendor_saved_apps/g" $NEW_MAKEFILE
+ fi
+
+ echo "===================================================="
+ echo "Makefile Configuration:"
+ echo " vendor_modify_jars := $vendor_modify_jars"
+ echo " vendor_saved_apps := $vendor_saved_apps"
+ echo " DENSITY := $DENSITY"
+ echo " RESOLUTION := $RESOLUTION"
+ echo "===================================================="
+
+ echo ">>> Setup the Makefile Done!"
+}
+
+############ init gitignore ###########
+function initGitIgnore()
+{
+ if [ -f $GIT_IGNORE_CONFIG ] && [ ! -f $PRJ_GIT_IGNORE ]; then
+ cp $GIT_IGNORE_CONFIG $PRJ_GIT_IGNORE
+ fi
+}
+
+############ pull boot and recovery ###########
+function prepare_boot_recovery()
+{
+ if [ ! -f $WORK_DIR/recovery.img ] && [ ! -f $WORK_DIR/recovery.fstab ]; then
+ pullTmp=`mktemp -d`
+ echo "Pull boot and recovery, It may take a few minutes, please wait...."
+ $PULL_BOOT_RECOVERY $pullTmp > /dev/null 2>&1
+ if [ -f $pullTmp/recovery.img ]; then
+ mv $pullTmp/recovery.img $WORK_DIR/recovery.img
+ fi
+ if [ -f $pullTmp/boot.img ] && [ ! -f $WORK_DIR/boot.img ] ; then
+ mv $pullTmp/boot.img $WORK_DIR/boot.img
+ fi
+ rm -rf $pullTmp
+ fi
+}
+
+############ make new project ###############
+
+# start a new project
+function newMakefile()
+{
+ checkRootState
+ setupMakefile
+ prepare_boot_recovery
+ initGitIgnore
+}
+
+function newMakefileFromOTA()
+{
+ checkOTAPackage
+ setupMakefile
+ initGitIgnore
+}
+
+checkEnvironment
+if [ $FROM_OTA == 0 ];then
+ newMakefile
+else
+ newMakefileFromOTA
+fi
diff --git a/decode_all b/decode_all
new file mode 120000
index 0000000..5b94c0f
--- /dev/null
+++ b/decode_all
@@ -0,0 +1 @@
+reverses/decode_all.sh
\ No newline at end of file
diff --git a/dedat b/dedat
new file mode 120000
index 0000000..c9c835e
--- /dev/null
+++ b/dedat
@@ -0,0 +1 @@
+reverses/de-dat/dedat.sh
\ No newline at end of file
diff --git a/fastboot b/fastboot
new file mode 100755
index 0000000..0ba5c8c
Binary files /dev/null and b/fastboot differ
diff --git a/flyme b/flyme
new file mode 120000
index 0000000..f8390a1
--- /dev/null
+++ b/flyme
@@ -0,0 +1 @@
+workflow/coron.py
\ No newline at end of file
diff --git a/formatters/__init__.py b/formatters/__init__.py
new file mode 100755
index 0000000..8b13789
--- /dev/null
+++ b/formatters/__init__.py
@@ -0,0 +1 @@
+
diff --git a/formatters/android_manifest.py b/formatters/android_manifest.py
new file mode 100644
index 0000000..40f8db1
--- /dev/null
+++ b/formatters/android_manifest.py
@@ -0,0 +1,33 @@
+'''
+Created on Aug 26, 2014
+
+@author: tangliuxiang
+'''
+from xml.dom import minidom
+import sys
+import re
+import os
+
+reload(sys)
+sys.setdefaultencoding("utf-8")
+
+def getPackageName(androidManifest):
+ manDom = minidom.parse(androidManifest)
+ return manDom.documentElement.getAttribute("package")
+
+def getPackageNameFromPublicXml(publicXml):
+ androidManifest = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(publicXml))), "AndroidManifest.xml")
+ if os.path.isfile(androidManifest):
+ pkgName = getPackageName(androidManifest)
+ else:
+ publicXml = minidom.parse(publicXml)
+ root = publicXml.documentElement
+ pkgName = ""
+ for item in root.childNodes:
+ if item.nodeType == minidom.Node.ELEMENT_NODE:
+ itemId = item.getAttribute("id").replace(r'0x0', r'')
+ if len(itemId) == 7 and itemId[0] == '1':
+ pkgName = 'android'
+ break
+ assert pkgName is not None and len(pkgName) > 0, "Wrong package name in %s, make sure %s is exist and correct!" % (publicXml, androidManifest)
+ return pkgName
diff --git a/formatters/common.py b/formatters/common.py
new file mode 100755
index 0000000..832c676
--- /dev/null
+++ b/formatters/common.py
@@ -0,0 +1,309 @@
+#!/usr/bin/env python
+
+__author__ = 'zhangweiping@baiyi-mobile.com'
+
+import re
+import os
+import string
+import hashlib
+
+class Smali:
+ """
+ A set of methods for processing smali file path
+ """
+ @staticmethod
+ def isSmali(path):
+ """
+ check if the path end of .smali
+ """
+ if re.compile(r'.*\.smali', re.S).match(path):
+ return True
+ return False
+
+ @staticmethod
+ def isRootSmali(path):
+ """
+ check if the path is a root smali
+ """
+ if Smali.isSmali(path) and cmp(Smali.getSmaliName(path), Smali.getSmaliRoot(path)) == 0:
+ return True
+ return False
+
+ @staticmethod
+ def getSmali(path):
+ """
+ get basename of path
+ input xx/xx/A$1.smali return A$1.smali
+ """
+ if Smali.isSmali(path):
+ return os.path.basename(path)
+ raise ValueError("Not a smali path:"+path)
+
+ @staticmethod
+ def getSmaliName(path):
+ """
+ get smali name of path
+ input xx/xx/A$1.smali return A$1
+ """
+ if Smali.isSmali(path):
+ basename = Smali.getSmali(path)
+ return os.path.splitext(basename)[0]
+ raise ValueError("Not a smali path:"+path)
+
+ @staticmethod
+ def getSmaliRoot(path):
+ """
+ get smali root name of path
+ input xx/xx/A$1.smali return A
+ """
+ if Smali.isSmali(path):
+ sName = Smali.getSmaliName(path)
+ return string.split(sName, "$")[0]
+ raise ValueError("Not a smali path:"+path)
+
+ @staticmethod
+ def getSmaliMain(path):
+ """
+ get root smali of path
+ input xx/xx/A$1.smali return A.smli
+ """
+ if Smali.isSmali(path):
+ sName = Smali.getSmaliRoot(path)
+ return sName+".smali"
+ raise ValueError("Not a smali path:"+path)
+
+ @staticmethod
+ def getSmaliMainPath(path):
+ """
+ get root smali file path of path
+ input xx/xx/A$1.smali return xx/xx/A.smali
+ """
+ if Smali.isSmali(path):
+ sName = Smali.getSmaliRoot(path)
+ return os.path.join(os.path.dirname(path), sName+".smali")
+ raise ValueError("Not a smali path:"+path)
+
+ @staticmethod
+ def getDataFilePath(path):
+ """
+ get data file path of path
+ input xx/xx/A$1.smali return xx/xx/A.data
+ """
+ if Smali.isSmali(path):
+ sName = Smali.getSmaliRoot(path)
+ return os.path.join(os.path.dirname(path), sName+".data")
+ raise ValueError("Not a smali path:"+path)
+#End of Smali
+
+
+class Java:
+ """
+ A Java include a smali file list which built from this java
+ """
+ def __init__(self, path, name):
+ self.path = path
+ self.name = name
+ self.smaliList = []
+ for root, dirs, files in os.walk(self.path):
+ dirs[:] = []
+ for f in files:
+ if Smali.isSmali(f) and self.isInJava(f):
+ self.add(f)
+
+ def add(self, sFile):
+ """
+ add a smali file to smali file list of this java
+ """
+ sName = Smali.getSmaliRoot(sFile)
+ if (cmp(self.name, sName) == 0):
+ self.smaliList.append(sFile)
+ else:
+ print sName+" not belongs java "+self.name
+
+ def printJava(self):
+ print self.path, self.name, "smaliList:"
+ print self.smaliList
+
+ def getListLen(self):
+ return len(self.smaliList)
+
+ def isInJava(self, sFile):
+ """
+ check if smali build from java
+ input xx/xx/A.$1.smali, self.name=A return true
+ """
+ if (cmp(self.name, Smali.getSmaliRoot(sFile)) == 0):
+ return True
+ return False
+#End of Java
+
+
+class File:
+ def __init__(self, path):
+ self.path = path
+
+ def replaces(self, sDict):
+ """
+ replace the key in dict with the value of the key in this File
+ """
+ sFile = open(self.path, 'r+')
+ fileStr = sFile.read()
+
+ for oldStr in sDict.keys():
+ fileStr = fileStr.replace(oldStr, sDict[oldStr])
+
+ sFile.seek(0, 0)
+ sFile.truncate()
+ sFile.write(fileStr)
+
+ def dump(self, dumpStr):
+ """
+ dump strings into File
+ """
+ sFile = open(self.path, 'w')
+ sFile.seek(0, 0)
+ sFile.truncate()
+ sFile.write(dumpStr)
+ sFile.close()
+#End of File
+
+
+class DataFile:
+ @staticmethod
+ def isDataFile(path):
+ """
+ check if the path end of .data
+ """
+ if re.compile(r'.*\.data', re.S).match(path):
+ return True
+ return False
+
+ @staticmethod
+ def getDataFile(path):
+ """
+ get basename of path
+ input xx/xx/A.data return A.data
+ """
+ if DataFile.isDataFile(path):
+ return os.path.basename(path)
+ raise ValueError("Not a data path:"+path)
+
+ @staticmethod
+ def getDataFileName(path):
+ """
+ get data name of path
+ input xx/xx/A.data return A$1
+ """
+ if DataFile.isDataFile(path):
+ basename = DataFile.getDataFile(path)
+ return os.path.splitext(basename)[0]
+ raise ValueError("Not a data path:"+path)
+#End of DataFile
+
+
+class Method:
+ """
+ .method static synthetic access$accessnum(parameterlist)returntype
+ iput|iget|sput|sget|aput|aget|invoke {..} classname;->operationobject
+ .end method
+
+ access$accessnum <==> access$operation-operationobject-methodlinehash
+ """
+ def __init__(self, accessNum, parameterList, returnType):
+ self.accessNum = accessNum
+ self.parameterList = parameterList
+ self.returnType = returnType
+ self.methodLine = self.parameterList+self.returnType
+
+ def addCallLine(self, operation, operationObject, callLine):
+ """
+ operation {..} classname;->operationobject
+ iput|iget|sput|sget|aput|aget|invoke {..} classname;->operationobject
+ """
+ self.operation = operation
+ self.operationObject = operationObject
+ self.methodLine = self.methodLine+callLine
+
+ def addMethodLine(self, methodLine):
+ self.methodLine = self.methodLine + methodLine
+
+ def endMethod(self):
+ self.createMethodName()
+
+ def createMethodName(self):
+ """
+ access$accessName = access$operation-operationobject-methodlinehash
+ """
+ self.methodLine = string.replace(self.methodLine, ' ', '')
+ self.methodLine = string.replace(self.methodLine, '\n', '')
+ self.accessName = self.operation+"-"+self.operationObject+"-"+hashlib.sha1(self.methodLine).hexdigest()[0:6]
+
+ def printMethod(self):
+ print "access$"+self.accessNum, "\t", "access$"+self.accessName
+#End of Method
+
+
+class AccessSmali:
+ """
+ methodNumSet {accessnum : accessname}
+ methodDefNumMap {.method static synthetic access$accessnum( : .method static synthetic access$accessname( }
+ methodCallNumMap {className;->access$accessnum : className;->access$accessname }
+
+ methodNameSet {accessname : accessnum}
+ methodDefNameMap {.method static synthetic access$accessname( : .method static synthetic access$accessnum( }
+ methodCallNameMap {className;->access$accessname : className;->access$accessnum }
+ """
+ def __init__(self, className):
+ self.className = className
+ self.methodDefBegin = ".method static synthetic access$"
+ self.methodDefEnd = "("
+ self.methodCallBegin = className+";->access$"
+ self.methodCallEnd = "("
+
+ self.methodNumSet = {}
+ self.methodDefNumMap = {}
+ self.methodCallNumMap = {}
+
+ self.methodNameSet = {}
+ self.methodDefNameMap = {}
+ self.methodCallNameMap = {}
+
+ def addMethod(self, num, name):
+ if num in self.methodNumSet.keys():
+ return
+ else:
+ self.methodNumSet[num] = name
+ if name not in self.methodNameSet.keys():
+ self.methodNameSet[name] = num
+
+ def readMethod(self, name, num):
+ if name not in self.methodNameSet.keys():
+ self.methodNameSet[name] = num
+ else:
+ return
+
+ def createNumMap(self):
+ for num in self.methodNumSet.keys():
+ self.methodDefNumMap[self.methodDefBegin+num+self.methodDefEnd] = self.methodDefBegin+self.methodNumSet[num]+self.methodDefEnd
+ self.methodCallNumMap[self.methodCallBegin+num+self.methodCallEnd] = self.methodCallBegin+self.methodNumSet[num]+self.methodCallEnd
+
+ def createNameMap(self):
+ for name in self.methodNameSet.keys():
+ self.methodDefNameMap[self.methodDefBegin+name+self.methodDefEnd] = self.methodDefBegin+self.methodNameSet[name]+self.methodDefEnd
+ self.methodCallNameMap[self.methodCallBegin+name+self.methodCallEnd] = self.methodCallBegin+self.methodNameSet[name]+self.methodCallEnd
+
+ def getMethodNumSetLen(self):
+ return len(self.methodNumSet)
+
+ def getMethodNameSetLen(self):
+ return len(self.methodNameSet)
+
+ def printMethodNumSet(self):
+ for k in self.methodNumSet.keys():
+ print self.className+": access$"+k+"\t"+" access$"+self.methodNumSet[k]
+
+ def printMethodNameSet(self):
+ for k in self.methodNameSet.keys():
+ print self.className+": access$"+k+"\t"+" access$"+self.methodNameSet[k]
+
+#End of AccessSmali
diff --git a/formatters/format.py b/formatters/format.py
new file mode 100755
index 0000000..3a4d6ee
--- /dev/null
+++ b/formatters/format.py
@@ -0,0 +1,304 @@
+#!/usr/bin/python
+
+### File Information ###
+"""
+Format the smali files, include following actions:
+ 1, remove the lines
+ 2, turn all resouces id to the name
+ 3, format all access method
+ 4, and so on....
+
+Usage: format.py [flags] -l smali_lib_dir [format_dir|format_files]
+
+ -l (--library)
+ make sure there is a decoded framework-res directory in smali_lib_dir
+
+ -r (--rollback)
+ rollback the format's before
+
+ -e (--rmline)
+ remove the lines
+
+ -i (--idtoname)
+ turn resouce id to name
+
+ -a (--accesstoname)
+ turn the access method to name
+
+ -u (--unifyfield)
+ unify the get/put field
+
+"""
+
+__author__ = 'duanqz@gmail.com'
+
+
+
+import commands
+import shutil
+import os, sys
+import getopt
+
+from name2num import NameToNumForOneFile
+from num2name import NumToNameForOneFile
+
+from idtoname import idtoname
+from nametoid import nametoid
+
+
+sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+from smaliparser import utils
+from smaliparser import SmaliLib
+from smaliparser import Smali
+
+
+class Format():
+
+ DEBUG = False
+
+ RELATIVE_PUBLIC_XML = "framework-res/res/values/public.xml"
+
+ DO = 0x01
+ UNDO_DO = 0x02
+
+ NONE = 0x00000000
+ REMOVE_LINE = 0x00000001
+ RESID_TO_NAME = 0x00000010
+ ACCESS_TO_NAME = 0x00000100
+ UNIFY_FIELD = 0x00001000
+ XX = 0x00010000
+ XXX = 0x00100000
+ XXXX = 0x01000000
+ XXXXX = 0x10000000
+
+ ALL_ACTION = REMOVE_LINE | RESID_TO_NAME | ACCESS_TO_NAME | UNIFY_FIELD
+ mLibDict = {}
+
+ @staticmethod
+ def getLib(libPath):
+ absPath = os.path.abspath(libPath)
+ if not Format.mLibDict.has_key(absPath):
+ Format.mLibDict[absPath] = SmaliLib.SmaliLib(libPath, 1)
+ return Format.mLibDict[absPath]
+
+ def __init__(self, root, smaliFile):
+ self.mRoot = root
+ self.mSmaliFile = smaliFile
+
+ self.mPublicXML = os.path.join(self.mRoot, Format.RELATIVE_PUBLIC_XML)
+ self.mAction = None
+ self.mLinePatch = None
+ self.mSmaliLib = Format.getLib(self.mRoot)
+
+ def setPublicXML(self, publicXML):
+ self.mPublicXML = publicXML
+ return self
+
+ def do(self, action = ALL_ACTION):
+ self.mAction = action
+
+ Format.log("DO")
+
+ # REMOVE_LINE -> ACCESS_TO_NAME -> RESID_TO_NAME
+ if self.mAction & Format.REMOVE_LINE:
+ Format.log(" REMOVE_LINE")
+ self.mLinePatch = Format.remLines(self.mSmaliFile)
+
+ if self.mAction & Format.ACCESS_TO_NAME:
+ Format.log(" ACCESS_TO_NAME")
+ NumToNameForOneFile(self.mSmaliFile)
+
+ if self.mAction & Format.RESID_TO_NAME:
+ Format.log(" RESID_TO_NAME")
+ if os.path.exists(self.mPublicXML):
+ idtoname(self.mPublicXML, self.mSmaliFile).idtoname()
+ else:
+ Format.log(" No such file or directory: %s" % self.mPublicXML)
+
+ if self.mAction & Format.UNIFY_FIELD:
+ Format.log(" UNIFY_FIELD")
+ Format.formatUsingField(self.mSmaliLib, self.mSmaliFile)
+
+ return self
+
+ def undo(self, action = None):
+ if action is None:
+ if self.mAction is not None:
+ action = self.mAction
+ else:
+ action = Format.ALL_ACTION
+
+ Format.log("UNDO")
+
+ # RESID_TO_NAME -> ACCESS_TO_NAME -> REMOVE_LINE
+ if action & Format.RESID_TO_NAME:
+ Format.log(" RESID_TO_NAME")
+ if os.path.exists(self.mPublicXML):
+ nametoid(self.mPublicXML, self.mSmaliFile).nametoid()
+ else:
+ Format.log(" No such file or directory: %s" % self.mPublicXML)
+
+ if action & Format.ACCESS_TO_NAME:
+ Format.log(" ACCESS_TO_NAME")
+ NameToNumForOneFile(self.mSmaliFile)
+
+ if action & Format.REMOVE_LINE:
+ Format.log(" REMOVE_LINE")
+ Format.addLines(self.mSmaliFile, self.mLinePatch)
+
+ if action & Format.UNIFY_FIELD:
+ Format.log(" UNIFY_FIELD")
+ # Can not undo now.....
+ #Format.undoFormatUsingField(self.mSmaliLib, self.mSmaliFile)
+
+ return self
+
+ @staticmethod
+ # format the replace methods
+ def formRepMethod(smaliFile):
+ return NumToNameForOneFile(smaliFile)
+
+ @staticmethod
+ # format the used fields
+ def formatUsingField(sLib, smaliFile):
+ cSmali = Smali.Smali(smaliFile)
+ sLib.setSmali(cSmali.getClassName(), cSmali)
+ sLib.formatUsingField(cSmali)
+
+ @staticmethod
+ # undo format the used fields
+ def undoFormatUsingField(sLib, smaliFile):
+ cSmali = Smali.Smali(smaliFile)
+ sLib.setSmali(cSmali.getClassName(), cSmali)
+ sLib.undoFormatUsingField(cSmali)
+
+ @staticmethod
+ def remLines(origFile):
+ """ Remove lines in original file
+ """
+
+ noLineFile = origFile + ".noline"
+
+ # Generate no line file
+ cmd = "cat %s | sed -e '/^\s*\.line.*$/d' | sed -e 's/\/jumbo//' > %s" % \
+ (commands.mkarg(origFile), commands.mkarg(noLineFile))
+ commands.getstatusoutput(cmd)
+
+ if not os.path.exists(noLineFile):
+ return None
+
+ # Generate line patch
+ linesPatch = origFile + ".linepatch"
+ cmd = "diff -B -u %s %s > %s" % \
+ (commands.mkarg(noLineFile), commands.mkarg(origFile), commands.mkarg(linesPatch))
+ commands.getstatusoutput(cmd)
+
+ shutil.move(noLineFile, origFile)
+
+ return linesPatch
+
+ @staticmethod
+ def addLines(smaliFile, linesPatch = None):
+ """ Add the lines back to no line file
+ """
+ if linesPatch is None:
+ linesPatch = '%s.linepatch' %(smaliFile)
+
+ if not os.path.isfile(linesPatch):
+ return
+
+ # Patch the lines to no line file
+ cmd = "patch -f %s -r /dev/null < %s > /dev/null" % \
+ (commands.mkarg(smaliFile), commands.mkarg(linesPatch))
+ commands.getstatusoutput(cmd)
+
+ os.remove(linesPatch)
+ origFile = smaliFile + ".orig"
+ if os.path.exists(origFile): os.remove(origFile)
+
+ return smaliFile
+
+ @staticmethod
+ def log(message):
+ if Format.DEBUG: print message
+
+ @staticmethod
+ def __doJob__(f, job, action):
+ if job == Format.DO:
+ f.do(action)
+ elif job == Format.UNDO_DO:
+ f.undo()
+ else:
+ Format.log("Error Action %s" % action)
+
+ @staticmethod
+ def format(job, libPath, smaliFileList = None, action = ALL_ACTION):
+ if smaliFileList is None:
+ smaliFileList = utils.getSmaliPathList(libPath)
+
+ idx = 0
+ while idx < len(smaliFileList):
+ if os.path.isdir(smaliFileList[idx]):
+ Format.format(job, libPath, utils.getSmaliPathList(libPath), action)
+ continue
+
+ f = Format(libPath, smaliFileList[idx])
+ Format.__doJob__(f, job, action)
+ idx = idx + 1
+
+def usage():
+ print __doc__
+
+class Options(object): pass
+OPTIONS = Options()
+OPTIONS.Job = Format.DO
+OPTIONS.Action = Format.NONE
+OPTIONS.LibPath = None
+DEFAULT_ACTION = Format.ALL_ACTION
+
+def main(argv):
+ options,args = getopt.getopt(argv[1:], "hrl:eiau", [ "help", "rollback", "library", "rmline", "idtoname", "accesstoname", "unifyfield"])
+ for name,value in options:
+ if name in ("-h", "--help"):
+ usage()
+ elif name in ("-r", "--rollback"):
+ OPTIONS.Job = Format.UNDO_DO
+ elif name in ("-l", "--library"):
+ OPTIONS.LibPath = value
+ elif name in ("-e", "--rmline"):
+ OPTIONS.Action = OPTIONS.Action | Format.REMOVE_LINE
+ elif name in ("-i", "--idtoname"):
+ OPTIONS.Action = OPTIONS.Action | Format.RESID_TO_NAME
+ elif name in ("-a", "--accesstoname"):
+ OPTIONS.Action = OPTIONS.Action | Format.ACCESS_TO_NAME
+ elif name in ("-u", "--unifyfield"):
+ OPTIONS.Action = OPTIONS.Action | Format.UNIFY_FIELD
+ else:
+ Format.log("Wrong parameters, see the usage....")
+ usage()
+ exit(1)
+ if OPTIONS.Action == Format.NONE:
+ OPTIONS.Action = DEFAULT_ACTION
+
+ if OPTIONS.LibPath is None:
+ if len(args) > 0:
+ OPTIONS.LibPath = args[0]
+ args = args[1:]
+ else:
+ usage()
+ exit(1)
+ if args is not None and len(args) <= 0:
+ args = None
+
+ Format.format(OPTIONS.Job, OPTIONS.LibPath, args, OPTIONS.Action)
+
+def test():
+ root = "/media/source/smali/smali-4.2/devices/demo/autopatch/vendor"
+ smaliFile = "/media/source/smali/smali-4.2/devices/demo/framework.jar.out/smali/android/widget/TextView.smali"
+ publicXML = "/media/source/smali/smali-4.2/devices/demo/framework-res/res/values/public.xml"
+
+ action = Format.REMOVE_LINE | Format.ACCESS_TO_NAME | Format.RESID_TO_NAME | Format.UNIFY_FIELD
+ Format(root, smaliFile).setPublicXML(publicXML).do(action).undo()
+
+if __name__ == "__main__":
+ main(sys.argv)
diff --git a/formatters/idtoname.py b/formatters/idtoname.py
new file mode 100755
index 0000000..a1bad37
--- /dev/null
+++ b/formatters/idtoname.py
@@ -0,0 +1,157 @@
+#!/usr/bin/env python
+
+'''
+Created on 2012-12-25
+
+@author: jock
+'''
+from xml.dom import minidom
+import sys
+import re
+import os
+import android_manifest
+
+reload(sys)
+sys.setdefaultencoding("utf-8")
+
+class idtoname(object):
+ '''
+ classdocs
+ '''
+ mIdToNameDict = {}
+
+ def __init__(self, xmlPath, inDir):
+ '''
+ Constructor
+ '''
+ self.smaliFileList = self.getInFileList(inDir)
+ self.idToNameMap = idtoname.getMap(xmlPath)
+
+ @staticmethod
+ def getMap(xmlPath):
+ absPath = os.path.abspath(xmlPath)
+ if not idtoname.mIdToNameDict.has_key(absPath):
+ idtoname.mIdToNameDict[absPath] = idtoname.getIdToNameMap(absPath)
+ return idtoname.mIdToNameDict[absPath]
+
+ def getInFileList(self, inDir):
+ if os.path.isfile(inDir):
+ return [inDir]
+
+ filelist = []
+ smaliRe = re.compile(r'(?:.*\.smali)')
+ for root, dirs, files in os.walk(inDir):
+ for fn in files:
+ if bool(smaliRe.match(fn)) is True:
+ filelist.append("%s/%s" % (root, fn))
+
+ return filelist
+
+ @staticmethod
+ def getIdToNameMap(xmlPath):
+ publicXml = minidom.parse(xmlPath)
+ root = publicXml.documentElement
+ idList = {}
+
+ pkgName = android_manifest.getPackageNameFromPublicXml(xmlPath)
+ Log.d("package name: %s" %pkgName)
+ pkgName = pkgName + ':'
+ for item in root.childNodes:
+ if item.nodeType == minidom.Node.ELEMENT_NODE:
+ itemType = item.getAttribute("type")
+ itemName = item.getAttribute("name")
+ itemId = item.getAttribute("id").replace(r'0x0', r'0x')
+ idList[itemId] = "%s%s@%s" % (pkgName, itemType, itemName)
+
+ return idList
+
+ def getArrayId(self, arrayIdStr):
+ idList = arrayIdStr.split()
+ arrayId = "%s%s%s%s" % (idList[3][-3:-1], idList[2][-3:-1], idList[1][-3:-1], idList[0][-3:-1])
+ arrayId = "0x%s" % (arrayId.replace('x', '0'))
+ return arrayId.replace('0x0', '0x')
+
+ def getIdByHigh16(self, high16Str):
+ idx = high16Str.index('0x')
+ rId = '%s%s' % (high16Str[idx:], '0000')
+ return (rId,high16Str[0:idx])
+
+ def getIdByApktool2High16(self, high16Str):
+ idx = high16Str.index('0x')
+ rId = high16Str[idx:]
+ return (rId,high16Str[0:idx])
+
+ def idtoname(self):
+ normalIdRule = re.compile(r'0x(?:[1-9a-f]|7f)[0-1][0-9a-f]{5}$', re.M)
+ arrayIdRule = re.compile(r'(?:0x[0-9a-f]{1,2}t ){3}0x(?:[1-9a-f]|7f)t')
+ high16IdRule = re.compile(r'const/high16[ ]*v[0-9][0-9]*,[ ]*0x(?:[1-9a-f]|7f)[0-1][0-9a-f]$', re.M)
+ apktool2High16IdRule = re.compile(r'const/high16[ ]*v[0-9][0-9]*,[ ]*0x(?:[1-9a-f]|7f)[0-1][0-9a-f]0000$', re.M)
+
+ for smaliFile in self.smaliFileList:
+ #print "start modify: %s" % smaliFile
+ sf = file(smaliFile, 'r+')
+ fileStr = sf.read()
+ modify = False
+
+ for matchApktool2Hight16IdStr in list(set(apktool2High16IdRule.findall(fileStr))):
+ (rId, preStr) = self.getIdByApktool2High16(matchApktool2Hight16IdStr)
+ name = self.idToNameMap.get(rId, None)
+ if name is not None:
+ fileStr = fileStr.replace(matchApktool2Hight16IdStr, r'%s#%s#i' % (preStr, name))
+ modify = True
+ Log.d("change id from %s to name %s" % (matchApktool2Hight16IdStr, name))
+
+ for matchId in list(set(normalIdRule.findall(fileStr))):
+ name = self.idToNameMap.get(matchId, None)
+ if name is not None:
+ fileStr = fileStr.replace(matchId, r'#%s#t' % name)
+ modify = True
+ Log.d("change id from %s to name %s" % (matchId, name))
+
+ for matchArrIdStr in list(set(arrayIdRule.findall(fileStr))):
+ matchArrId = self.getArrayId(matchArrIdStr)
+ arrName = self.idToNameMap.get(matchArrId, None)
+ if arrName is not None:
+ fileStr = fileStr.replace(matchArrIdStr, r'#%s#a' % arrName)
+ modify = True
+ Log.d("change array id from %s to name %s" % (matchArrIdStr, arrName))
+
+ for matchHigh16IdStr in list(set(high16IdRule.findall(fileStr))):
+ (rId, preStr) = self.getIdByHigh16(matchHigh16IdStr)
+ name = self.idToNameMap.get(rId, None)
+ if name is not None:
+ fileStr = fileStr.replace(matchHigh16IdStr, r'%s#%s#h' % (preStr, name))
+ modify = True
+ Log.d("change id from %s to name %s" % (matchHigh16IdStr, name))
+
+ if modify is True:
+ sf.seek(0, 0)
+ sf.truncate()
+ sf.write(fileStr)
+ sf.close()
+
+class Log:
+ DEBUG = False
+
+ @staticmethod
+ def d(message):
+ if Log.DEBUG: print message
+
+ @staticmethod
+ def i(message):
+ print message
+
+def main():
+ print "start change id to name...."
+ if len(sys.argv) == 3:
+ idtoname(sys.argv[1], sys.argv[2]).idtoname()
+ else:
+ print "USAGE: idtoname public.xml DIRECTORY"
+ print "eg: idtoname public.xml framework.jar.out"
+ print "change all of the id in framework.jar.out to type@name"
+ sys.exit(1)
+
+ print "change id to name done"
+
+if __name__ == '__main__':
+ main()
diff --git a/formatters/log.py b/formatters/log.py
new file mode 100755
index 0000000..83ad5f7
--- /dev/null
+++ b/formatters/log.py
@@ -0,0 +1,69 @@
+#!/usr/bin/python
+
+"""
+Usage: log LEVEL TAG MESSAGE
+ LEVEL : d(debug), i(info), w(warning), e(error)
+ TAG : log tag
+ MESSAGE : log message
+"""
+
+
+import sys
+
+class Log:
+
+ DEBUG = False
+
+ @staticmethod
+ def d(tag, message):
+ if Log.DEBUG: print "D/%s: %s" %(tag, message)
+
+ @staticmethod
+ def i(tag, message):
+ print "I/%s: %s" %(tag, message)
+
+ @staticmethod
+ def w(tag, message):
+ print "W/%s: %s" %(tag, message)
+
+ @staticmethod
+ def e(tag, message):
+ print "E/%s: %s" %(tag, message)
+
+
+class Paint:
+
+ @staticmethod
+ def bold(s):
+ return "%s[01m%s%s[0m" % (chr(27), s.rstrip(), chr(27))
+
+ @staticmethod
+ def red(s):
+ return "%s[33;31m%s%s[0m" % (chr(27), s.rstrip(), chr(27))
+
+ @staticmethod
+ def green(s):
+ return "%s[31;32m%s%s[0m" % (chr(27), s.rstrip(), chr(27))
+
+ @staticmethod
+ def blue(s):
+ return "%s[34;01m%s%s[0m" % (chr(27), s.rstrip(), chr(27))
+
+ @staticmethod
+ def yellow(s):
+ return "%s[31;33m%s%s[0m" % (chr(27), s.rstrip(), chr(27))
+
+
+if __name__ == "__main__":
+ if len(sys.argv) < 4:
+ print __doc__
+ sys.exit()
+
+ level = sys.argv[1]
+ tag = sys.argv[2]
+ message = sys.argv[3]
+
+ if level in ("d", "debug"): Log.d(tag, message)
+ elif level in ("i", "info") : Log.i(tag, message)
+ elif level in ("w", "warn") : Log.w(tag, message)
+ elif level in ("e", "error"): Log.e(tag, message)
diff --git a/formatters/name2num.py b/formatters/name2num.py
new file mode 100755
index 0000000..8a6c18b
--- /dev/null
+++ b/formatters/name2num.py
@@ -0,0 +1,105 @@
+#!/usr/bin/env python
+
+__author__ = 'zhangweiping@baiyi-mobile.com'
+
+import os
+import sys
+
+from common import Smali
+from common import Java
+from common import File
+from common import DataFile
+from common import AccessSmali
+
+class NameToNum:
+ def __init__(self, name, path, smaliList):
+ self.name = name
+ self.path = path
+ self.smaliList = smaliList
+ self.accessSmaliSet = {}
+ self.getAccessSmaliSet()
+
+ def getAccessSmaliSet(self):
+ dFile = open(os.path.join(self.path, self.name+".data"), 'r')
+ for line in dFile.readlines():
+ tList = line.split()
+ sName = tList[0]
+ name = tList[1]
+ num = tList[2]
+ if (sName not in self.accessSmaliSet.keys()):
+ self.accessSmaliSet[sName] = AccessSmali(sName)
+ self.accessSmaliSet[sName].readMethod(name, num)
+ dFile.close()
+
+ def printAccessSmaliSet(self):
+ for sName in self.accessSmaliSet.keys():
+ self.accessSmaliSet[sName].printMethodNameSet()
+
+ def doNameToNum(self):
+ allMethodCallNameMap = {}
+ for aSmali in self.accessSmaliSet.keys():
+ self.accessSmaliSet[aSmali].createNameMap()
+ callNameMap = self.accessSmaliSet[aSmali].methodCallNameMap
+ for callName in callNameMap.keys():
+ if callName not in allMethodCallNameMap.keys():
+ allMethodCallNameMap[callName] = callNameMap[callName]
+ else:
+ raise ValueError("method call name map duplicate")
+
+ for s in self.smaliList:
+ sFile = File(os.path.join(self.path, s))
+ sName = Smali.getSmaliName(s)
+ if sName in self.accessSmaliSet.keys():
+ sFile.replaces(self.accessSmaliSet[sName].methodDefNameMap)
+ sFile.replaces(allMethodCallNameMap)
+#End of NameToNum
+
+
+def NameToNumForOneFile(path):
+ if Smali.isSmali(path):
+ path = Smali.getDataFilePath(path) #change smali path to data file path
+ if DataFile.isDataFile(path) and os.path.exists(path):
+ fDir = os.path.dirname(path)
+ if cmp(fDir, "") == 0:
+ fDir = "."
+ name = DataFile.getDataFileName(path)
+ else:
+ return
+
+ java = Java(fDir, name)
+ #java.printJava()
+ if java.getListLen() == 0:
+ print "Can not find data file: "+os.path.join(java.path, java.name)+".data"
+ return
+
+ if False: print "NameToNum: "+os.path.join(java.path, java.name)+".data"
+ toNum = NameToNum(java.name, java.path, java.smaliList)
+ #toNum.printAccessSmaliSet()
+ toNum.doNameToNum()
+
+ os.remove(path)
+
+
+def Usage():
+ print "Usage: name2num.py aa/bb/A.data"
+ print " name2num.py aa/bb/A.smali"
+ print " name2num.py aa/bb"
+
+if __name__ == '__main__':
+ argLen = len(sys.argv)
+ if argLen == 2:
+ path = sys.argv[1]
+ if os.path.isfile(path) and (DataFile.isDataFile(path) or Smali.isSmali(path)):
+ NameToNumForOneFile(path)
+
+ elif os.path.isdir(path):
+ for root, dirs, files in os.walk(path):
+ for sfile in files:
+ fPath = os.path.join(root, sfile)
+ if DataFile.isDataFile(fPath):
+ NameToNumForOneFile(fPath)
+
+ else:
+ Usage()
+ else:
+ Usage()
diff --git a/formatters/nametoid.py b/formatters/nametoid.py
new file mode 100755
index 0000000..6056782
--- /dev/null
+++ b/formatters/nametoid.py
@@ -0,0 +1,174 @@
+#!/usr/bin/env python
+
+'''
+Created on 2012-12-25
+
+@author: jock
+'''
+from xml.dom import minidom
+import sys
+import re
+import os
+import android_manifest
+
+reload(sys)
+sys.setdefaultencoding("utf-8")
+
+class nametoid(object):
+ '''
+ classdocs
+ '''
+ mIdToNameDict = {}
+ CONST_LEN = len('const/high16')
+
+ def __init__(self, xmlPath, inDir):
+ '''
+ Constructor
+ '''
+ self.smaliFileList = self.getInFileList(inDir)
+ self.nameToIdMap = nametoid.getMap(xmlPath)
+
+ @staticmethod
+ def getMap(xmlPath):
+ absPath = os.path.abspath(xmlPath)
+ if not nametoid.mIdToNameDict.has_key(absPath):
+ nametoid.mIdToNameDict[absPath] = nametoid.getIdToNameMap(absPath)
+ return nametoid.mIdToNameDict[absPath]
+
+ def getInFileList(self, inDir):
+ if os.path.isfile(inDir):
+ return [inDir]
+
+ filelist = []
+ smaliRe = re.compile(r'.*\.smali')
+ for root, dirs, files in os.walk(inDir):
+ for fn in files:
+ if bool(smaliRe.match(fn)) is True:
+ filelist.append("%s/%s" % (root, fn))
+
+ return filelist
+
+ @staticmethod
+ def getIdToNameMap(xmlPath):
+ publicXml = minidom.parse(xmlPath)
+ root = publicXml.documentElement
+ idList = {}
+
+ pkgName = android_manifest.getPackageNameFromPublicXml(xmlPath)
+ Log.d("package name: %s" %pkgName)
+ pkgName = pkgName + ':'
+ for item in root.childNodes:
+ if item.nodeType == minidom.Node.ELEMENT_NODE:
+ itemType = item.getAttribute("type")
+ itemName = item.getAttribute("name")
+ itemId = item.getAttribute("id").replace(r'0x0', r'0x')
+ idList["%s%s@%s" % (pkgName, itemType, itemName)] = itemId
+ if pkgName == "android:":
+ idList["%s@%s" % (itemType, itemName)] = itemId
+
+ return idList
+
+ def getArrayId(self, arrayIdStr):
+ idList = arrayIdStr.split()
+ arrayId = "%s%s%s%s" % (idList[3][-3:-1], idList[2][-3:-1], idList[1][-3:-1], idList[0][-3:-1])
+ arrayId = "0x%s" % (arrayId.replace('x', '0'))
+ return arrayId.replace('0x0', '0x')
+
+ def getArrayStr(self, arrayId):
+ if cmp(arrayId[-8], "x") == 0:
+ arrayStr = '0x%st 0x%st 0x%st 0x%st' % (arrayId[-2:], arrayId[-4:-2], arrayId[-6:-4], arrayId[-7:-6])
+ else:
+ arrayStr = '0x%st 0x%st 0x%st 0x%st' % (arrayId[-2:], arrayId[-4:-2], arrayId[-6:-4], arrayId[-8:-6])
+ return arrayStr.replace('0x0', '0x')
+
+ def getHigh16Name(self, high16Str):
+ idx = high16Str.index('#')
+ hName = high16Str[idx:]
+ return (hName,high16Str[0:idx])
+
+ def getApktool2High16Name(self, high16Str):
+ idx = high16Str.index('#')
+ hName = high16Str[idx:]
+ return (hName,high16Str[0:idx])
+
+ def nametoid(self):
+ normalNameRule = re.compile(r'#[^ \t\n]*@[^ \t\n]*#t')
+ arrayNameRule = re.compile(r'#[^ \t\n]*@[^ \t\n]*#a')
+ high16NameRule = re.compile(r'const/high16[ ]*v[0-9][0-9]*,[ ]*#[^ \t\n]*@[^ \t\n]*#h')
+ apktool2High16IdRule = re.compile(r'const/high16[ ]*v[0-9][0-9]*,[ ]*#[^ \t\n]*@[^ \t\n]*#i')
+
+ for smaliFile in self.smaliFileList:
+# print "start modify: %s" % smaliFile
+ sf = file(smaliFile, 'r+')
+ fileStr = sf.read()
+ modify = False
+
+ for matchApktool2HightName in list(set(apktool2High16IdRule.findall(fileStr))):
+ (hName, preStr) = self.getApktool2High16Name(matchApktool2HightName)
+ newId = self.nameToIdMap.get(hName[1:-2], None)
+ if newId is not None:
+ if newId[-4:] != '0000':
+ newStr = r'const%s%s' % (preStr[nametoid.CONST_LEN:], newId)
+ else:
+ newStr = r'%s%s' % (preStr, newId)
+ fileStr = fileStr.replace(matchApktool2HightName, newStr)
+ modify = True
+ Log.d(">>> change name from %s to id %s" % (matchApktool2HightName, newStr))
+
+ for matchArrName in list(set(arrayNameRule.findall(fileStr))):
+ arrId = self.nameToIdMap.get(matchArrName[1:-2], None)
+ if arrId is not None:
+ newArrIdStr = self.getArrayStr(arrId)
+ fileStr = fileStr.replace(matchArrName, newArrIdStr)
+ modify = True
+ Log.d(">>> change array name from %s to id %s" % (matchArrName[1:-2], newArrIdStr))
+
+ for matchName in list(set(normalNameRule.findall(fileStr))):
+ newId = self.nameToIdMap.get(matchName[1:-2], None)
+ if newId is not None:
+ fileStr = fileStr.replace(matchName, newId)
+ modify = True
+ Log.d(">>> change name from %s to id %s" % (matchName[1:-2], newId))
+
+ for matchHighName in list(set(high16NameRule.findall(fileStr))):
+ (hName, preStr) = self.getHigh16Name(matchHighName)
+ newId = self.nameToIdMap.get(hName[1:-2], None)
+ if newId is not None:
+ if newId[-4:] != '0000':
+ newStr = r'const%s%s' % (preStr[nametoid.CONST_LEN:], newId)
+ else:
+ newStr = r'%s%s' % (preStr, newId[:-4])
+ fileStr = fileStr.replace(matchHighName, newStr)
+ Log.d(">>> change name from %s to id %s" % (matchHighName, newStr))
+ modify = True
+
+ if modify is True:
+ sf.seek(0, 0)
+ sf.truncate()
+ sf.write(fileStr)
+ sf.close()
+
+class Log:
+ DEBUG = False
+
+ @staticmethod
+ def d(message):
+ if Log.DEBUG: print message
+
+ @staticmethod
+ def i(message):
+ print message
+
+def main():
+ if len(sys.argv) == 3:
+ nametoid(sys.argv[1], sys.argv[2]).nametoid()
+ else:
+ print "USAGE: nametoid public.xml DIRECTORY"
+ print "eg: nametoid public.xml framework.jar.out"
+ print "change all of type@name in framework.jar.out to resource id"
+ sys.exit(1)
+
+ print ">>> change the name to id done"
+
+if __name__ == '__main__':
+ main()
diff --git a/formatters/num2name.py b/formatters/num2name.py
new file mode 100755
index 0000000..31c2adb
--- /dev/null
+++ b/formatters/num2name.py
@@ -0,0 +1,157 @@
+#!/usr/bin/env python
+
+__author__ = 'zhangweiping@baiyi-mobile.com'
+
+import re
+import os
+import sys
+
+from common import Smali
+from common import Java
+from common import File
+from common import AccessSmali
+from common import Method
+from common import DataFile
+
+# method pattern
+patternMethodBegin = re.compile(r'\.method static synthetic access\$(?P\d+)\((?P.*?)\)(?P.*?)\s', re.S)
+patternMethodAnnotation = re.compile(r' *\.| *$', re.S)
+patternMethodCall = re.compile(r'\s*(?Piput|iget|sput|sget|aput|aget|invoke).*? (?PL.*?);->(?P.*?)(:|\(.*\))', re.S)
+patternMethodEnd = re.compile(r'\.end method', re.S)
+
+class NumToName:
+ def __init__(self, name, path, smaliList):
+ self.name = name
+ self.path = path
+ self.smaliList = smaliList
+ self.accessSmaliSet = {}
+ self.getAccessSmaliSet()
+
+ def getAccessSmaliSet(self):
+ """ check smalilist to build access num to name map """
+ for s in self.smaliList:
+ sName = Smali.getSmaliName(s)
+ aSmali = AccessSmali(sName)
+ mode = 0
+ method = ""
+ for line in open(os.path.join(self.path, s), 'r'):
+ if (mode == 0):
+ match = patternMethodBegin.match(line)
+ if(match):
+ method = Method(match.group("accessnum"), match.group("parameterlist"), match.group("returntype"))
+ mode = 1
+ elif (mode == 1):
+ match = patternMethodEnd.match(line)
+ if(match):
+ method.endMethod()
+ mode = 0
+ aSmali.addMethod(method.accessNum, method.accessName)
+ else:
+ match = patternMethodAnnotation.match(line)
+ if(match):
+ continue
+ match = patternMethodCall.match(line)
+ if(match):
+ method.addCallLine(match.group("operation"), match.group("operationobject"), match.group(0))
+ else:
+ method.addMethodLine(line)
+ if (aSmali.getMethodNumSetLen() > 0):
+ aSmali.createNumMap()
+ self.accessSmaliSet[sName] = aSmali
+
+ def printAccessSmaliSet(self):
+ for sName in self.accessSmaliSet.keys():
+ self.accessSmaliSet[sName].printMethodNumSet()
+
+ def doNumToName(self):
+ allMethodCallNumMap = {}
+ for sName in self.accessSmaliSet.keys():
+ callNumMap = self.accessSmaliSet[sName].methodCallNumMap
+ for callNum in callNumMap.keys():
+ if callNum not in allMethodCallNumMap.keys():
+ allMethodCallNumMap[callNum] = callNumMap[callNum]
+ else:
+ raise ValueError("method call num map duplicate")
+
+ for s in self.smaliList:
+ sFile = File(os.path.join(self.path, s))
+ sName = Smali.getSmaliName(s)
+ if sName in self.accessSmaliSet.keys():
+ sFile.replaces(self.accessSmaliSet[sName].methodDefNumMap)
+ sFile.replaces(allMethodCallNumMap)
+
+ def dumpMap(self):
+ """
+ smalirootname.data:
+ smaliname accessname accessnum
+ """
+ dumpStr = ""
+ for sName in self.accessSmaliSet.keys():
+ nameSet = self.accessSmaliSet[sName].methodNameSet
+ for name in nameSet.keys():
+ dumpStr=(dumpStr+sName+" "+name+" "+nameSet[name]+"\n")
+ File(os.path.join(self.path, self.name+".data")).dump(dumpStr)
+#End of NumToName
+
+
+def NumToNameForOneFile(path):
+ if Smali.isSmali(path):
+ path = Smali.getSmaliMainPath(path)
+ else:
+ return
+ if os.path.exists(path):
+ fDir = os.path.dirname(path)
+ if cmp(fDir, "") == 0:
+ fDir = "."
+ name = Smali.getSmaliRoot(path)
+ else:
+ return
+
+ if os.path.exists(os.path.join(fDir, name)+".data"):
+ if False: print "NumToName: "+os.path.join(fDir, name)+".data is exist, ignore!"
+ return os.path.join(fDir, name) + ".data"
+
+ java = Java(fDir, name)
+ #java.printJava()
+ if java.getListLen() == 0:
+ if False: print "Can not find smali file: "+os.path.join(java.path, java.name)+"*.smali"
+ return
+
+ toName = NumToName(java.name, java.path, java.smaliList)
+ #toName.printAccessSmaliSet()
+ toName.doNumToName()
+
+ toName.dumpMap()
+
+ return os.path.join(java.path, java.name) + ".data"
+
+
+def Usage():
+ print "Usage: num2name.py aa/bb/A.smali"
+ print " num2name.py aa/bb/B$1.smali"
+ print " num2name.py aa/bb"
+
+if __name__ == '__main__':
+ argLen = len(sys.argv)
+ if argLen == 2:
+ path = sys.argv[1]
+ if Smali.isSmali(path):
+ NumToNameForOneFile(path)
+
+ elif os.path.isdir(path):
+ for root, dirs, files in os.walk(path):
+ for sfile in files:
+ fPath = os.path.join(root, sfile)
+ if Smali.isRootSmali(fPath):
+ NumToNameForOneFile(fPath)
+
+ else:
+ Usage()
+ else:
+ Usage()
+
+
+
+
+
+
diff --git a/formatters/rmline.sh b/formatters/rmline.sh
new file mode 100755
index 0000000..22625af
--- /dev/null
+++ b/formatters/rmline.sh
@@ -0,0 +1,46 @@
+#!/bin/bash
+
+who=`whoami`
+tmp_loc=/tmp/rmline_$who
+
+# $2 is the file name with the full absolute path
+function rm_line() {
+ local action=$1
+ local file=$2
+ local diff_file=$tmp_loc$file.line
+
+ if [ "$action" == "remove" ]; then
+ mv $file $file.original
+ more $file.original | sed -e '/^\s*\.line.*$/d' | sed -e 's/\/jumbo//' > $file
+ diff $file $file.original > /dev/null || {
+ mkdir -p `dirname $diff_file`
+ diff -B -c $file $file.original > $diff_file
+ }
+ rm $file.original
+ else
+ if [ -f $diff_file ]; then
+ patch -f $file -r /dev/null < $diff_file >/dev/null 2>&1
+ rm -f $diff_file
+ else
+ echo "Warning: line info file ($diff_file) does not exist" >&2
+ fi
+ fi
+}
+
+action=remove
+if [ "$1" == "-r" ]; then
+ action=add
+ shift
+fi
+
+p=`pwd`
+full=`echo $1 | sed -e "s#\(^[^\/]\)#$p/\1#"`
+if [ -f "$full" ]; then
+ echo $full | grep .smali$ > /dev/null && rm_line $action $full
+ exit 0
+fi
+
+for file in `find $full -name "*.smali"`
+do
+ rm_line $action $file
+done
diff --git a/helpdoc/README.md b/helpdoc/README.md
new file mode 100644
index 0000000..172b9e5
--- /dev/null
+++ b/helpdoc/README.md
@@ -0,0 +1,278 @@
+
+### An open source project for Android ROM porting.
+
+Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0).
+
+COMMANDs are:
+
+ * flyme help [ACTION]
+
+ Type `help name` to find out more about the `name`.
+
+ * flyme ACTION
+
+ config Genearate the configuration for a new device.
+ A Makefile containing all the configurations will be generated.
+
+ newproject Generate the new project for a new device.
+ Only used when setup your device project.
+
+ patchall Patch all the changes.
+ Incorpate all the changes from BOSP to AOSP into VENDOR.
+ BOSP : Code of Board Open Source Project
+ AOSP : Code Android Open Source Project
+ VENDOR: Code pulled out from the device
+
+ The codes of VENDOR are located in the root directory of your device.
+ The codes of AOSP and BOSP are located in the autopatch directory of your device.
+
+ fullota Fully make out the ota package.
+ An OTA package will be generated, you could flash it into your device.
+
+ upgrade Patch the upgrade changes. You could upgrade your device to the latest.
+ Using parameter "LAST_COMMIT=xx" to patch changes from LAST_COMMIT
+
+ porting Porting changes from an existing device to another.
+ Using parameter "COMMIT1=xx COMMIT2=xx" to patch changes from COMMIT1 to COMMIT2
+
+ clean Clean the project output.
+
+ cleanall Clean all the project unneccessary files, inluding board/ and out/.
+
+### Error Codes
+
+***ERR_USB_NOT_CONNECTED(151)***
+
+
+ Please make sure your device has been connected.
+
+
+***ERR_DEVICE_NOT_ROOTED(152)***
+
+***ERR_UNPACK_BOOTIMG_FAILED(153)***
+
+
+ Using the following command to check whether your image can be unpacked:
+ $ unpack_bootimg recovery.img
+
+ If unpack failed, use another recovery.img.
+
+
+***ERR_PACK_BOOTIMG_FAILED(154)***
+
+
+ Using the following command to check whether your image can be unpacked:
+ $ pack_bootimg image_out/
+
+ If pack failed, unpack your boot image again.
+
+
+***ERR_DEVICE_BASE_NOT_FOUND(155)***
+
+
+ Make sure you have synced the base from coron, and the path is devices/base.
+ If devices/base not exists, try to use "repo sync" to sync coron again.
+
+
+***ERR_PULL_BOOT_RECOVERY_FAILED(156)***
+
+
+ Check adb devices is fine and your phone is su root!
+
+
+***ERR_WRONG_PARAMETERS(157)***
+
+***ERR_AUTOCOM_FAILED(158)***
+
+
+ Please check you have Phone.apk and in vendor/system/app and it can be decode correctly. Try:
+ $ apktool d vendor/system/app/Phone.apk
+ If you don't have Phone.apk, it must be renamed to someoneelse, find it and rename to Phone.apk.
+ if the decode of Phone.apk is failed, just remove the Phone.apk in vendor/system/app, then go on!
+
+
+***ERR_METHODTOBOSP_FAILED(159)***
+
+
+ Make sure both of the vendor and bosp have this smali file!
+ And the method name is fine, such as
+ $ methodtobosp services.jar.out/smali/com/android/server/am/ActivityManagerService.smali 'moveTaskToFront(IILandroid/os/Bundle;)V'
+
+
+***ERR_SMALITOBOSP_FAILED(160)***
+
+
+ Make sure both of the vendor and bosp have this smali file!
+
+
+***ERR_APKTOOL_BUILD_FAILED(161)***
+
+
+ Make sure you don't install any framework resource after you decode the apk,
+ otherwise you should install the framework resource which match your apk, then build again
+ $ ifdir xxx/system/framework
+
+ Example:
+ If you want build out's apk, you better install out's framework first!
+ $ ifdir out/merged_target_files/SYSTEM/framework/
+
+
+***ERR_APKTOOL_DECODE_FAILED(162)***
+
+
+ Make sure the destination directory doesn't exist!
+ And install framework resource first
+ $ ifdir xxx/system/framework
+
+ Example:
+ If you want decode board's apk, you better install board's framework first!
+ $ ifdir board/system/framework
+
+
+***ERR_DEODEX_FAILED(163)***
+
+
+ You can try to update the smali.jar and baksmali.jar in tools, which can be download from http://code.google.com
+ If it doesn't work, you better find some other tools to deodex.
+
+---------------------------------------------------------------------------------------------------------------------------------
+
+### Flyme开源项目致力于为开发者提供业界一流的ROM适配工具.
+
+Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0).
+
+提供的命令:
+
+ * flyme help [ACTION]
+
+ 输入 `help [ACTION]` 去获取更多关于[ACTION]的帮助文档.
+
+ * flyme ACTION
+
+ config 为新设备生成一个Makefile,这个Makefile将会包含所有的配置.
+
+ newproject 为新设备建立一个新项目,只能用在为设备建立新项目上.
+
+ patchall 应用所有的Flyme的修改(插桩).
+ 将AOSP和BOSP产生的修改合并到原厂代码上.
+ BOSP : Flyme开源代码
+ AOSP : 安卓开源代码
+ VENDOR: 设备原厂代码
+
+ 原厂代码位于你的机型根目录.
+ AOSP和BOSP位于机型根目录的autopatch目录里.
+
+ fullota 制作一个完整的刷机包.
+ 一个完整的刷机包将会在out目录下生成,你应该把它刷入你的手机.
+
+ upgrade 应用所有的更新修改,你可以升级你的设备到最新.
+
+ porting 从现有的设备参考制作FlymeOS.
+ 使用语法: "COMMIT1=xx COMMIT2=xx" 应用修改从COMMIT1到COMMIT2
+
+ clean 清理项目输出的文件.
+
+ cleanall 清理项目不需要的文件,包括机型目录下的board目录和out目录.
+
+### 错误号
+
+***ERR_USB_NOT_CONNECTED(151)***
+
+
+ 请确认你的设备已经连接.
+
+
+***ERR_DEVICE_NOT_ROOTED(152)***
+
+***ERR_UNPACK_BOOTIMG_FAILED(153)***
+
+
+ 建议解决方案:
+ ----------------
+ 使用以下命令来确认镜像是否能被CORON解包:
+ $ unpack_bootimg recovery.img
+
+ 如果解包失败,请使用其他镜像或者上网搜索解包方式.
+
+
+***ERR_PACK_BOOTIMG_FAILED(154)***
+
+
+ 建议解决方案:
+ ----------------
+ 使用以下命令来确认镜像是否能被打包:
+ $ pack_bootimg image_out/
+
+ 如果打包失败,请重新解包你的boot.img或者recovery.img.
+
+
+***ERR_DEVICE_BASE_NOT_FOUND(155)***
+
+
+ 建议解决方案:
+ ----------------
+ 请确认devices/base有一个coron base.
+ 如果没有devices/base not, 请尝试使用 "repo sync" 去重新同步源码.
+ Make sure you have synced the base from coron, and the path is devices/base.
+ If devices/base not exists, try to use "repo sync" to sync coron again.
+
+
+***ERR_PULL_BOOT_RECOVERY_FAILED(156)***
+
+
+ 请确保adb正常工作并让你的设备获取root权限!
+
+
+***ERR_WRONG_PARAMETERS(157)***
+
+***ERR_AUTOCOM_FAILED(158)***
+
+
+ 请检查是否有 Phone.apk 在 vendor/system/app 里,还要确认其是否能被反编译.
+ 尝试输入 $ apktool d vendor/system/app/Phone.apk
+ 如果你没有 Phone.apk, 那么它一定是被某人或者厂商修改了,请找到它并改名为 Phone.apk.
+ 如果反编译失败,请删除它,然后继续.
+
+
+***ERR_METHODTOBOSP_FAILED(159)***
+
+
+ 请确认原厂代码和BOSP中都含有这个smali文件!
+ 如果函数方法是正确的, 就像这样
+ $ methodtobosp services.jar.out/smali/com/android/server/am/ActivityManagerService.smali 'moveTaskToFront(IILandroid/os/Bundle;)V'
+
+
+***ERR_SMALITOBOSP_FAILED(160)***
+
+
+ 请确认原厂代码和BOSP中都含有这个smali文件!
+
+
+***ERR_APKTOOL_BUILD_FAILED(161)***
+
+
+ 请确认你在反编译这个apk后没有再安装过任何资源框架,
+ 你可以使用以下命令重新安装资源框架
+ $ ifdir xxx/system/framework
+ 例子:
+ 如果你想回编译原厂apk,那么你必须使用以下命令安装原厂的资源框架!
+ $ ifdir out/merged_target_files/SYSTEM/framework/
+
+
+***ERR_APKTOOL_DECODE_FAILED(162)***
+
+
+ 请确认目标文件夹不存在!
+ 还有,在反编译之前,你必须先安装资源框架.
+ $ ifdir xxx/system/framework
+ 例子:
+ 如果你想反编译Flyme的apk,那么你必须先使用以下命令安装Flyme的资源框架!
+ $ ifdir board/system/framework
+
+
+***ERR_DEODEX_FAILED(163)***
+
+
+ 请更新在tools/apktools/里的smali.jar和baksmali.jar, 可以在这里下载 http://code.google.com
+ 如果它无法工作 那么你可以寻找其他合并odex的工具.
+
diff --git a/helpdoc/__init__.py b/helpdoc/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/helpdoc/en/help.xml b/helpdoc/en/help.xml
new file mode 100644
index 0000000..bceca66
--- /dev/null
+++ b/helpdoc/en/help.xml
@@ -0,0 +1,352 @@
+
+
+
+
+
+
+
+ -
+
+
+ Here is a long story, we have been telling it for a long time, and still believe somebody will continue it.
+
+ The tale of CORON will never stop.
+
+ ────────────────────────────────────────────────────────────────────────────────────
+
+ MMML MHHL "MMMMMMM' MMML M
+ IMM*CC*MMM IMLMHCMMML MMM*ZJH*MMH IMMM--CML ML H
+ IMMML MMM IMMC $MM MMM MMMC MMML MMM H
+ MMM 'MMM MMMH $MMI--IHMMM IMMM' MMMMH MMMM M
+ MMM MMMM MMMM$ MMM$MMMMM MMMM: MMMM$ IMMM MM MM
+ MMMMH: M HMMMH MMMM: MMMM ?MM HMMMH MMMMI MMMM MMM MML
+ IMMM*DQZ*MMM HMMM H*ZWP*M MMMM MMML HMMMMM-HMMMM$ MMMM MMMMMMM
+ HMMMMMMMC IMMMMMCL *TLX* IMMMMM ICMMMML IMMMMM IMMM
+ IMMMHHH IMIHL HMMMMM IMMMMM IMMH HMMMMM MMM
+
+ ────────────────────────────────────────────────────────────────────────────────────
+
+ What is CORON?
+ * CORON is an open source project for Android ROM porting.
+ * With plentiful tools of CORON, developers could board ROM on target device in one step.
+ * In CORON, courses of Android porting is available, including video and online tutorials.
+
+ Why named CORON?
+ * CORON stands for CO-operated RON(m), born to be cooperative with ROM developers;
+ * CORON stands for RON(m) Over Cloud, provides access of huge remote servers' ability;
+ * CORON is also an island, paradise of divers. It shows simple and tasteful feelings.
+
+ How to use CORON?
+ After setup enviornment of CORON, type command `coron` to see the usage.
+ The amazing tools may tell you it is so hard to put CORON down.
+ You local directory of CORON is like:
+
+ coron │ # The root of coron
+ ├── manifest │ # Introduce to coron, including tutorials
+ │ │
+ ├── board │ # Holding all the board concerned
+ │ ├── frameworks │
+ │ │ └── overlay │ # Overlay resources of framework
+ │ └── release │ # Released Apks and Jars
+ │ │
+ ├── devices │ # Holding all the porting devices
+ │ ├── base │ # The base of all other devices.
+ │ └── yourdevice │ # Your own device to be ported
+ │ │
+ ├── build │ # Build environment
+ └── tools │ # Tools
+
+ CORON open for you forever, hope some day you will join us.
+
+
+
+
+ -
+
+ Porting ROM in one step. All the progress of ROM porting will be concentrated in to one step.
+
+
+
+ -
+
+ Type `help name` to find out more about the `name`.
+
+
+
+ -
+
+ An open source project for Android ROM porting.
+
+
+
+ -
+
+ Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0).
+
+
+
+ -
+
+ COMMANDs are:
+
+
+
+ -
+
+ Error Codes
+
+
+
+ -
+
+ Genearate the configuration for a new device.
+ A Makefile containing all the configurations will be generated.
+
+
+
+ -
+
+ Generate the new project for a new device.
+ Only used when setup your device project.
+
+
+
+ -
+
+ Patch all the changes.
+ Incorpate all the changes from BOSP to AOSP into VENDOR.
+ BOSP : Code of Board Open Source Project
+ AOSP : Code Android Open Source Project
+ VENDOR: Code pulled out from the device
+
+ The codes of VENDOR are located in the root directory of your device.
+ The codes of AOSP and BOSP are located in the autopatch directory of your device.
+
+
+
+ -
+
+ Resolve conflicts.
+ After patchall, conflicts might happen, autofix could help you
+ to resolve some of them automatically.
+
+
+
+ -
+
+ Fully make out the ota package.
+ An OTA package will be generated, you could flash it into your device.
+
+
+
+ -
+
+ Patch the upgrade changes. You could upgrade your device to the latest.
+ Using parameter "LAST_COMMIT=xx" to patch changes from LAST_COMMIT
+
+
+
+ -
+
+ Porting changes from an existing device to another.
+ Using parameter "COMMIT1=xx COMMIT2=xx" to patch changes from COMMIT1 to COMMIT2
+
+
+
+ -
+
+ Clean the project output.
+
+
+
+ -
+
+ Clean all the project unneccessary files, inluding board/ and out/.
+
+
+
+
+
+ -
+
+ Advice:
+ 1. Although no conflict, mistakes still come out sometimes,
+ it depends on your device, VENDOR may change AOSP a lot.
+
+ 2. Fall through to fullota, flash the outcome into your device,
+ and then fix bugs depends on real-time logs.
+
+
+
+ -
+
+ Advice:
+ 1. Each conflict is marked out like:
+
+ <<<<<<< VENDOR
+ Codes of VENDOR
+ =======
+ Codes of BOSP
+ >>>>>>> BOSP
+
+ you'd better resolve all conflicts before going on with the following work.
+
+ 2. You might follow three steps to resolve conflicts manually:
+
+ - compare VENDOR and REJECT to find out where conflicts happened;
+ - compare AOSP and BOSP to find out why conflicts happened;
+ - resolve conflicts in VENDOR.
+
+
+
+ -
+
+ Can not find device
+
+
+ Please make sure your device has been connected.
+
+
+
+ -
+
+ Can not acquire ROOT permission
+
+
+
+ -
+
+ Unpack bootimg failed. Your boot.img or recovery.img might be imformal that could not be unpacked out.
+
+
+ Using the following command to check whether your image can be unpacked:
+ $ unpack_bootimg recovery.img
+
+ If unpack failed, use another recovery.img.
+
+
+
+ -
+
+ Pack bootimg failed. Your boot.img or recovery.img might be informal that could not be packed back.
+
+
+ Using the following command to check whether your image can be unpacked:
+ $ pack_bootimg image_out/
+
+ If pack failed, unpack your boot image again.
+
+
+
+ -
+
+ devices/base not found!
+
+
+ Make sure you have synced the base, and the path is devices/base.
+ If devices/base not exists, try to use "repo sync" to sync again.
+
+
+
+ -
+
+ Failed to pull boot.img and recovery.img from your phone.
+
+
+ Check adb devices is fine and your phone is su root!
+
+
+
+ -
+
+ Wrong parameters for this command....
+
+
+
+ -
+
+ Failed to autocomplete missed method in android.policy and Phone.
+
+
+ Please check you have Phone.apk and in vendor/system/app and it can be decode correctly. Try:
+ $ apktool d vendor/system/app/Phone.apk
+ If you don't have Phone.apk, it must be renamed to someoneelse, find it and rename to Phone.apk.
+ if the decode of Phone.apk is failed, just remove the Phone.apk in vendor/system/app, then go on!
+
+
+
+ -
+
+ Failed to replace method to bosp.
+
+
+ Make sure both of the vendor and bosp have this smali file!
+ And the method name is fine, such as
+ $ methodtobosp services.jar.out/smali/com/android/server/am/ActivityManagerService.smali 'moveTaskToFront(IILandroid/os/Bundle;)V'
+
+
+
+ -
+
+ Failed to replace smali file to bosp.
+
+
+ Make sure both of the vendor and bosp have this smali file!
+
+
+
+ -
+
+ Failed to replace smali file to bosp.
+
+
+ Make sure both of the vendor and bosp have this smali file!
+
+
+
+ -
+
+ Failed to use apktool build apk back.
+
+
+ Make sure you don't install any framework resource after you decode the apk,
+ otherwise you should install the framework resource which match your apk, then build again
+ $ ifdir xxx/system/framework
+
+ Example:
+ If you want build out's apk, you better install out's framework first!
+ $ ifdir out/merged_target_files/SYSTEM/framework/
+
+
+
+ -
+
+ Failed to use apktool d xxx.apk.
+
+
+ Make sure the destination directory doesn't exist!
+ And install framework resource first
+ $ ifdir xxx/system/framework
+
+ Example:
+ If you want decode board's apk, you better install board's framework first!
+ $ ifdir board/system/framework
+
+
+
+ -
+
+ Failed to deodex ota.zip/target-files.zip.
+
+
+ If it doesn't work, you better find some other tools to deodex.
+
+
+
+
diff --git a/helpdoc/en/help_autofix.xml b/helpdoc/en/help_autofix.xml
new file mode 100644
index 0000000..f72e9f1
--- /dev/null
+++ b/helpdoc/en/help_autofix.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/helpdoc/en/help_config.xml b/helpdoc/en/help_config.xml
new file mode 100644
index 0000000..a0b7f49
--- /dev/null
+++ b/helpdoc/en/help_config.xml
@@ -0,0 +1,50 @@
+
+
+
+
+ -
+
+ Error: makeconfig, command not found
+
+
+ 1. Make sure you have setup environment:
+ $ source build/envsetup.sh
+
+ 2. makeconfig must be executable, to check it:
+ $ ls -l build/tools/makeconfig
+
+ 3. you might make it executable by:
+ $ chmod a+x build/tools/makeconfig
+
+ You should download the tools by "git clone" instead of copying.
+
+
+
+ -
+
+ Makefile already exist! Did you already configure you device ?
+
+
+ If you want to configure again, just remove the existing Makefile.
+
+
+
+ -
+
+ The ota.zip is a incompatible ota package.
+
+
+ Check whether META-INF and system directory are in ota.zip
+
+
+
+ -
+
+ Mission Failed, as catching a Runtime Error.
+
+
+ Check the build log !
+
+
+
+
\ No newline at end of file
diff --git a/helpdoc/en/help_fullota.xml b/helpdoc/en/help_fullota.xml
new file mode 100644
index 0000000..ed77c10
--- /dev/null
+++ b/helpdoc/en/help_fullota.xml
@@ -0,0 +1,17 @@
+
+
+
+
+ -
+
+ Failed to run ota_from_target_files to generate an ota zip from target-files.zip
+
+
+
+ -
+
+ Failed to generate the system.img from target-files.zip.
+
+
+
+
\ No newline at end of file
diff --git a/helpdoc/en/help_newproject.xml b/helpdoc/en/help_newproject.xml
new file mode 100644
index 0000000..a7d3cfe
--- /dev/null
+++ b/helpdoc/en/help_newproject.xml
@@ -0,0 +1,67 @@
+
+
+
+ -
+
+ Can not find recovery.fstab !
+
+
+ Prepare a recovery.fstab in current project directory !
+
+
+
+ -
+
+ Can not find ota.zip !
+
+
+ Prepare a ota.zip in current project directory !
+
+
+
+ -
+
+ The ota.zip is a incompatible ota package.
+
+
+ Check whether META-INF and system directory are in ota.zip
+
+
+
+ -
+
+ Can not find out/oem_target_files.zip !
+
+
+ Make clean, and Retry !
+
+
+
+ -
+
+ Mission Failed, as catching a Runtime Error.
+
+
+ Check the build log !
+
+
+
+ -
+
+ Can not find recovery.img !
+
+
+ Prepare a recovery.img or recovery.fstab in current project directory !
+
+
+
+ -
+
+ Can not find boot.img !
+
+
+ Prepare a boot.img in current project directory, Or not set boot in vendor_modify_images[Makefile] !
+
+
+
+
\ No newline at end of file
diff --git a/helpdoc/en/help_patchall.xml b/helpdoc/en/help_patchall.xml
new file mode 100644
index 0000000..a3c0d62
--- /dev/null
+++ b/helpdoc/en/help_patchall.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/helpdoc/en/help_upgrade.xml b/helpdoc/en/help_upgrade.xml
new file mode 100644
index 0000000..77a7b24
--- /dev/null
+++ b/helpdoc/en/help_upgrade.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/helpdoc/help.py b/helpdoc/help.py
new file mode 100755
index 0000000..53eca1c
--- /dev/null
+++ b/helpdoc/help.py
@@ -0,0 +1,321 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright 2015 Coron
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+"""
+Type `help name' to find out more about the `name'.
+
+ e.g. help "config"
+ e.g. help "newproject"
+ e.g. help "patchall"
+
+"""
+
+
+import os
+import sys
+import types
+try:
+ import xml.etree.cElementTree as ET
+except ImportError:
+ import xml.etree.ElementTree as ET
+
+sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+from formatters.log import Paint
+
+reload(sys)
+sys.setdefaultencoding( "utf-8" )
+
+class Item:
+ """ Help Item Model
+ """
+
+ name = None
+ code = None
+ detail = None
+ solution = None
+
+ def __init__(self, code, name):
+ self.code = code
+ self.name = name
+
+
+class HelpModel:
+ """ Help Model
+ """
+
+ CODE_TO_NAME = {}
+ NAME_TO_CODE = {}
+ ITEM_TAGS = {}
+
+ def __init__(self):
+
+ self.help_xml = HelpModel.retrieve_xml_by_locale()
+ self.help_xml_dir = os.path.dirname(self.help_xml)
+
+ xml_dom = ET.parse(self.help_xml)
+
+ for tag in xml_dom.findall('item'):
+ name = tag.get('name')
+ code = int(tag.get('code'))
+
+ self.CODE_TO_NAME[code] = name
+ self.NAME_TO_CODE[name] = code
+ self.ITEM_TAGS[code] = tag
+
+ def get_help_xml_dir(self):
+ return self.help_xml_dir
+
+ @staticmethod
+ def retrieve_xml_by_locale():
+ d = os.path.dirname(os.path.realpath(__file__))
+ if "zh" in os.popen('echo $LANG').read():
+ f = open(os.path.join(d, "locale_cn"), "r")
+ else:
+ f = open(os.path.join(d, "locale"), "r")
+ locale = f.read().rstrip()
+ f.close()
+
+ return os.path.join(d, locale, "help.xml")
+
+ def get(self, key):
+ """ Get the help item by either code or name.
+ See the Help Item Model @Item
+ """
+
+ if key is None:
+ return None
+ elif type(key) is types.IntType:
+ code = key
+ name = self.CODE_TO_NAME.get(code)
+ elif key.isdigit():
+ code = int(key)
+ name = self.CODE_TO_NAME.get(code)
+ else:
+ name = key
+ code = self.NAME_TO_CODE.get(name)
+
+ tag = self.ITEM_TAGS.get(code)
+ if tag is None:
+ return None
+
+ item = Item(code, name)
+ for child in tag.getchildren():
+ if child.tag == "solution":
+ item.solution = child.text
+ elif child.tag == "detail":
+ item.detail = child.text
+
+ return item
+
+
+class HelpModelOverride(HelpModel):
+ """ Derived class of Help.
+ Override items if defined in Help
+ """
+
+ def __init__(self, xml):
+ HelpModel.__init__(self)
+
+ xml_dom = ET.parse(xml)
+
+ for tag in xml_dom.findall('item'):
+ name = tag.get('name')
+ code = int(tag.get('code'))
+
+ self.CODE_TO_NAME[code] = name
+ self.NAME_TO_CODE[name] = code
+ self.ITEM_TAGS[code] = tag
+
+# End of class HelpOverride
+
+
+def __get_help_xml(category):
+ help_xml = os.path.join(os.path.dirname(HelpModel.retrieve_xml_by_locale()), "help_%s.xml" % category)
+ if os.path.exists(help_xml):
+ return help_xml
+ else:
+ return None
+
+
+def __create(category=None):
+ """ Create a help model
+ :param category: help category, like config, newproject etc.
+ :return: HelpModel
+ """
+
+ help_xml = __get_help_xml(category)
+ if help_xml is not None:
+ return HelpModelOverride(help_xml)
+ else:
+ return HelpModel()
+
+
+def __get_item(key, category):
+ """
+ Get item by key in category
+ :param key: code or name of item
+ :param category: help category, like config, newproject
+ :return: Item
+ """
+
+ help_model = __create(category)
+ item = help_model.get(key)
+
+ if item is None and category is None:
+ all_help_xml = os.listdir(help_model.get_help_xml_dir())
+ for help_xml in all_help_xml:
+ if help_xml.startswith("help_") and help_xml.endswith(".xml"):
+ category = help_xml[5:-4]
+ help_model = __create(category)
+ item = help_model.get(key)
+
+ return item
+
+
+def show(key, category=None):
+ """ Show the help item
+ :param key: name or code of the help item
+ :param category: help category, like config, newproject
+ """
+
+ item = __get_item(key, category)
+ if item is None:
+ return
+
+ if item.detail is not None:
+ print "%s: %s" % (Paint.bold(item.name), item.detail.replace("\t", "").strip())
+
+ if item.solution is not None:
+ print Paint.green(item.solution.replace("\t", "").lstrip())
+
+ print ""
+
+
+def show_all():
+ """ Show all the help items
+ """
+
+ help_model = __create()
+ for code in sorted(help_model.CODE_TO_NAME.keys()):
+ if code >= 220:
+ continue
+
+ show(code)
+
+
+def main(argv):
+ """ Parse input arguments.
+ @:param argv[1]: name or code of the help item
+ @:param argv[2]: category
+ """
+
+ key = None
+ category = None
+ size = len(argv)
+
+ if size == 1:
+ show_all()
+
+ if size > 1:
+ key = argv[1]
+
+ if size > 2:
+ category = argv[2]
+
+ show(key, category)
+
+
+def usage():
+ help_model = __create()
+
+ print " "
+ print Paint.bold(help_model.get("help_usage").detail.strip())
+ print " "
+ print help_model.get("help_license").detail.strip()
+ print " "
+ print help_model.get("help_cmds").detail.strip()
+
+ # help
+ print Paint.bold("* flyme help [ACTION]")
+ print " "
+ print " ", help_model.get("help").detail.strip()
+ print " "
+
+ # action
+ print Paint.bold("* flyme [ACTION]")
+ print " "
+
+ actions = ("config", "newproject", "patchall", "fullota", "upgrade", "porting", "clean", "cleanall")
+ for action in actions:
+ print Paint.bold(action).rjust(20), "\t", help_model.get(action).detail.strip()
+ print " "
+
+ # fire
+ #print Paint.bold("* flyme fire")
+ #print " ", help_model.get("fire").detail.strip()
+ print ""
+
+def readme():
+
+ help_model = __create()
+
+ f = open("README.md", "w")
+ print >>f, " "
+ print >>f, "### ", help_model.get("help_usage").detail.strip()
+ print >>f, " "
+ print >>f, help_model.get("help_license").detail.strip()
+ print >>f, " "
+ print >>f, help_model.get("help_cmds").detail.strip()
+ print >>f, " "
+ print >>f, " * flyme help [ACTION]"
+ print >>f, " "
+ print >>f, " ", help_model.get("help").detail.strip()
+ print >>f, " "
+
+ # action
+ print >>f, " * flyme ACTION"
+ print >>f, " "
+
+ actions = ("config", "newproject", "patchall", "fullota", "upgrade", "porting", "clean", "cleanall")
+ for action in actions:
+ print >>f, " ", action, "\t", help_model.get(action).detail.strip()
+ print >>f, " "
+
+ # fire
+ #print >>f, " * flyme fire"
+ #print >>f, " ", help_model.get("fire").detail.strip()
+ #print >>f, " "
+
+ print >>f, "### ", help_model.get("err_code").detail.strip()
+ print >>f, " "
+ for code in sorted(help_model.CODE_TO_NAME.keys()):
+ if code < 150 or code > 220:
+ continue
+
+ item = help_model.get(code)
+ print >>f, "***%s(%d)***" % (item.name, item.code)
+ print >>f, " "
+ if item.solution is not None:
+ print >>f, "%s" % item.solution
+ print >>f, " "
+
+ f.close()
+
+if __name__ == "__main__":
+ readme()
+
diff --git a/helpdoc/locale b/helpdoc/locale
new file mode 100644
index 0000000..2c4c454
--- /dev/null
+++ b/helpdoc/locale
@@ -0,0 +1 @@
+en
\ No newline at end of file
diff --git a/helpdoc/locale_cn b/helpdoc/locale_cn
new file mode 100644
index 0000000..187dffb
--- /dev/null
+++ b/helpdoc/locale_cn
@@ -0,0 +1 @@
+zh_CN
diff --git a/helpdoc/zh_CN/help.xml b/helpdoc/zh_CN/help.xml
new file mode 100644
index 0000000..708b2e8
--- /dev/null
+++ b/helpdoc/zh_CN/help.xml
@@ -0,0 +1,352 @@
+
+
+
+
+
+
+
+ -
+
+
+ 这是一个很长的故事,我们已经讲了很长一段时间,但我们仍然相信有人会继续。
+
+ CORON的故事永远不会停止 .
+
+ ────────────────────────────────────────────────────────────────────────────────────
+
+ MMML MHHL "MMMMMMM' MMML M
+ IMM*CC*MMM IMLMHCMMML MMM*ZJH*MMH IMMM--CML ML H
+ IMMML MMM IMMC $MM MMM MMMC MMML MMM H
+ MMM 'MMM MMMH $MMI--IHMMM IMMM' MMMMH MMMM M
+ MMM MMMM MMMM$ MMM$MMMMM MMMM: MMMM$ IMMM MM MM
+ MMMMH: M HMMMH MMMM: MMMM ?MM HMMMH MMMMI MMMM MMM MML
+ IMMM*DQZ*MMM HMMM H*ZWP*M MMMM MMML HMMMMM-HMMMM$ MMMM MMMMMMM
+ HMMMMMMMC IMMMMMCL *TLX* IMMMMM ICMMMML IMMMMM IMMM
+ IMMMHHH IMIHL HMMMMM IMMMMM IMMH HMMMMM MMM
+
+ ────────────────────────────────────────────────────────────────────────────────────
+
+ 什么是CORON?
+ * CORON是一个开源的Android ROM porting项目.
+ * 利用CORON丰富的工具,开发者可以在最快的时间里为一台设备适配上最新的FlymeOS.
+ * 在CORON里,所有是课程都是可以利用的,包括视频课程和在线课程.
+
+ 为什么要叫CORON?(此处不作翻译)
+ * CORON stands for CO-operated RON(m), born to be cooperative with ROM developers;
+ * CORON stands for RON(m) Over Cloud, provides access of huge remote servers' ability;
+ * CORON is also an island, paradise of divers. It shows simple and tasteful feelings.
+
+ 怎样使用CORON?
+ 在初始化CORON环境之后,运行命令'coron'去查看使用方法.
+ 神器的工具会让你无法放得下CORON.
+ CORON的目录就像这样:
+
+ coron │ # 这是CORON的根目录
+ ├── manifest │ # CORON的介绍文件,包括教程的存放地点
+ │ │
+ ├── board │ # 所有有关FlymeOS的资源
+ │ ├── frameworks │
+ │ │ └── overlay │ # Flyme的框架资源存放地点
+ │ └── release │ # 已经发布的apk和jar,也叫Flyme底包
+ │ │
+ ├── devices │ # 所有有关Flyme的参考机型
+ │ ├── base │ # 其他所有机型的插桩基础
+ │ └── yourdevice │ # 这是你的机型根目录
+ │ │
+ ├── build │ # 环境工具
+ └── tools │ # 插桩工具
+
+ CORON永远为你敞开,希望有一天你会加入我们
+ * GitHub : https://github.com/flymeos/manifest
+
+
+
+
+ -
+
+ 一键适配FlymeOS,所有适配ROM的步骤都会集中在这一步!
+
+
+
+ -
+
+ 输入 `help [ACTION]` 去获取更多关于[ACTION]的帮助文档.
+
+
+
+ -
+
+ Flyme开源项目致力于为开发者提供业界一流的ROM适配工具.
+
+
+
+ -
+
+ Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0).
+
+
+
+ -
+
+ 提供的命令:
+
+
+
+ -
+
+ 错误号
+
+
+
+ -
+
+ 为新设备生成一个Makefile,这个Makefile将会包含所有的配置.
+
+
+
+ -
+
+ 为新设备建立一个新项目,只能用在为设备建立新项目上.
+
+
+
+ -
+
+ 应用所有的Flyme的修改(插桩).
+ 将AOSP和BOSP产生的修改合并到原厂代码上.
+ BOSP : Flyme开源代码
+ AOSP : 安卓开源代码
+ VENDOR: 设备原厂代码
+
+ 原厂代码位于你的机型根目录.
+ AOSP和BOSP位于机型根目录的autopatch目录里.
+
+
+
+ -
+
+ 解决冲突.
+ 运行patchall之后, reject冲突就会发生, autofix会自动帮你解决冲突.
+
+
+
+ -
+
+ 制作一个完整的刷机包.
+ 一个完整的刷机包将会在out目录下生成,你应该把它刷入你的手机.
+
+
+
+ -
+
+ 应用所有的更新修改,你可以升级你的设备到最新.
+
+
+
+ -
+
+ 从现有的设备参考制作FlymeOS.
+ 使用语法: "COMMIT1=xx COMMIT2=xx" 应用修改从COMMIT1到COMMIT2
+
+
+
+ -
+
+ 清理项目out目录的文件.
+
+
+
+ -
+
+ 清理项目不需要的文件,包括机型目录下的board目录和out目录.
+
+
+
+
+
+ -
+
+ 建议:
+ 1. 即使没有冲突(reject),但是错误有时候还是会出现的,
+ 这取决于你的手机厂商,他们通常都会对AOSP进行很多修改.
+
+ 2. 你应该去运行fullota,刷入make出来的包,
+ 然后利用实时日志(log)去分析并修复bug.
+
+
+
+ -
+
+ 建议:
+ 1. 每个冲突(conflict)都会像这样标记出来:
+
+ <<<<<<< VENDOR
+ 原厂代码
+ =======
+ BOSP
+ >>>>>>> BOSP
+
+ 你最好在解决所有的冲突之前进行如下操作.
+
+ 2. 你可以按照这3个步骤来解决冲突:
+ - 对比原厂代码和reject冲突来寻找哪里发生冲突;
+ - 对比autopatch目录下的AOSP和BOSP来寻找Flyme对AOSP作了什么修改;
+ - 对比原厂代码和BOSP来决定应该怎样去解决冲突.
+
+
+
+ -
+
+ 找不到设备
+
+
+ 请确认你的设备已经连接.
+
+
+
+ -
+
+ 无法获取root权限
+
+
+
+ -
+
+ 解包镜像失败,boot.img或者recovery.img非常规格式.
+
+
+ 建议解决方案:
+ ----------------
+ 使用以下命令来确认镜像是否能被解包:
+ $ unpack_bootimg recovery.img
+
+ 如果解包失败,请使用其他镜像或者上网搜索解包方式.
+
+
+
+ -
+
+ 打包镜像失败,boot.img或者recovery.img非常规格式
+
+
+ 建议解决方案:
+ ----------------
+ 使用以下命令来确认镜像是否能被打包:
+ $ pack_bootimg image_out/
+
+ 如果打包失败,请重新解包你的boot.img或者recovery.img.
+
+
+
+ -
+
+ 找不到devices/base!
+
+
+ 建议解决方案:
+ ----------------
+ 请确认devices/base存在.
+ 如果没有devices/base, 请尝试使用 "repo sync" 去重新同步源码.
+
+
+
+ -
+
+ 无法从手机里拉取 boot.img 和 recovery.img.
+
+
+ 请确保adb正常工作并让你的设备获取root权限!
+
+
+
+ -
+
+ 命令参数错误....
+
+
+
+ -
+
+ 未能为android.policy 和 Phone自动补全方法.
+
+
+ 请检查是否有 Phone.apk 在 vendor/system/app 里,还要确认其是否能被反编译.
+ 尝试输入 $ apktool d vendor/system/app/Phone.apk
+ 如果你没有 Phone.apk, 那么它一定是被某人或者厂商修改了,请找到它并改名为 Phone.apk.
+ 如果反编译失败,请删除它,然后继续.
+
+
+
+ -
+
+ 无法把函数方法替换成BOSP的.
+
+
+ 请确认原厂代码和BOSP中都含有这个smali文件!
+ 如果函数方法是正确的, 就像这样
+ $ methodtobosp services.jar.out/smali/com/android/server/am/ActivityManagerService.smali 'moveTaskToFront(IILandroid/os/Bundle;)V'
+
+
+
+ -
+
+ 无法把smali文件替换为BOSP的.
+
+
+ 请确认原厂代码和BOSP中都含有这个smali文件!
+
+
+
+ -
+
+ 无法把smali文件替换为BOSP的.
+
+
+ 请确认原厂代码和BOSP中都含有这个smali文件!
+
+
+
+ -
+
+ 无法使用apktool回编译这个apk.
+
+
+ 请确认你在反编译这个apk后没有再安装过任何资源框架,
+ 你可以使用以下命令重新安装资源框架
+ $ ifdir xxx/system/framework
+ 例子:
+ 如果你想回编译原厂apk,那么你必须使用以下命令安装原厂的资源框架!
+ $ ifdir out/merged_target_files/SYSTEM/framework/
+
+
+
+ -
+
+ 无法反编译这个apk.
+
+
+ 请确认目标文件夹不存在!
+ 还有,在反编译之前,你必须先安装资源框架.
+ $ ifdir xxx/system/framework
+ 例子:
+ 如果你想反编译Flyme的apk,那么你必须先使用以下命令安装Flyme的资源框架!
+ $ ifdir board/system/framework
+
+
+
+ -
+
+ 无法在ota.zip/target-files.zip里合并odex.
+
+
+ 如果它无法工作 那么你可以寻找其他合并odex的工具.
+
+
+
+
diff --git a/helpdoc/zh_CN/help_autofix.xml b/helpdoc/zh_CN/help_autofix.xml
new file mode 100644
index 0000000..f72e9f1
--- /dev/null
+++ b/helpdoc/zh_CN/help_autofix.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/helpdoc/zh_CN/help_config.xml b/helpdoc/zh_CN/help_config.xml
new file mode 100644
index 0000000..90c6e80
--- /dev/null
+++ b/helpdoc/zh_CN/help_config.xml
@@ -0,0 +1,50 @@
+
+
+
+
+ -
+
+ 未能进行: makeconfig, 找不到命令
+
+
+ 1. 请确认你是否运行了环境安装命令:
+ $ source build/envsetup.sh
+
+ 2. 请确保makeconfig文件必须是可执行的:
+ $ ls -l build/tools/makeconfig
+
+ 你可以这样让makeconfig变成可执行文件:
+ $ chmod a+x build/tools/makeconfig
+
+ 你应该使用git clone下载tools目录.
+
+
+
+ -
+
+ Makefile已经存在! 确认已经配置好你的设备?
+
+
+ 如果你想重新配置Makefile,请把Makefile文件删除并再次执行.
+
+
+
+ -
+
+ 这不是一个标准格式的卡刷包,不兼容该卡刷包!
+
+
+ 请检查META-INF和system文件夹是否在卡刷包里!
+
+
+
+ -
+
+ 任务失败了,抓到一个运行时发生的错误.
+
+
+ 请检查日志!
+
+
+
+
diff --git a/helpdoc/zh_CN/help_fullota.xml b/helpdoc/zh_CN/help_fullota.xml
new file mode 100644
index 0000000..3e81dd9
--- /dev/null
+++ b/helpdoc/zh_CN/help_fullota.xml
@@ -0,0 +1,17 @@
+
+
+
+
+ -
+
+ 未能运行ota_from_target_files从target-files.zip生成一个ota.zip
+
+
+
+ -
+
+ 未能从target-files.zip上生成一个system.img.
+
+
+
+
diff --git a/helpdoc/zh_CN/help_newproject.xml b/helpdoc/zh_CN/help_newproject.xml
new file mode 100644
index 0000000..56d219b
--- /dev/null
+++ b/helpdoc/zh_CN/help_newproject.xml
@@ -0,0 +1,67 @@
+
+
+
+ -
+
+ 找不到recovery分区表文件 recovery.fstab !
+
+
+ 请准备好一个recovery分区表文件(recovery.fstab)放入当前工程目录!
+
+
+
+ -
+
+ 找不到 ota.zip !
+
+
+ 请准备好一个recovery卡刷包(ota.zip)放在当前工程目录下 !
+
+
+
+ -
+
+ 这不是一个标准格式的卡刷包,不兼容该卡刷包!
+
+
+ 请检查META-INF和system文件夹是否在卡刷包里!
+
+
+
+ -
+
+ 找不到 out/oem_target_files.zip !
+
+
+ 输入make clean, 并再次执行 !
+
+
+
+ -
+
+ 任务失败了,抓到一个运行时发生的错误.
+
+
+ 请检查日志!
+
+
+
+ -
+
+ 找不到 recovery.img !
+
+
+ 请准备好一个recovery镜像文件(recovery.img)或者recovery分区表文件(recovery.fstab)放在当前工程目录 !
+
+
+
+ -
+
+ 找不到 boot.img !
+
+
+ 请准备好一个boot.img内核镜像放在当前工程目录或者不要在Makefile的vendor_modify_images上配置boot !
+
+
+
+
diff --git a/helpdoc/zh_CN/help_patchall.xml b/helpdoc/zh_CN/help_patchall.xml
new file mode 100644
index 0000000..a3c0d62
--- /dev/null
+++ b/helpdoc/zh_CN/help_patchall.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/helpdoc/zh_CN/help_upgrade.xml b/helpdoc/zh_CN/help_upgrade.xml
new file mode 100644
index 0000000..77a7b24
--- /dev/null
+++ b/helpdoc/zh_CN/help_upgrade.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/idtoname b/idtoname
new file mode 120000
index 0000000..25685d8
--- /dev/null
+++ b/idtoname
@@ -0,0 +1 @@
+formatters/idtoname.py
\ No newline at end of file
diff --git a/lib64/libc++.so b/lib64/libc++.so
new file mode 100755
index 0000000..0dc328e
Binary files /dev/null and b/lib64/libc++.so differ
diff --git a/log b/log
new file mode 120000
index 0000000..f576511
--- /dev/null
+++ b/log
@@ -0,0 +1 @@
+formatters/log.py
\ No newline at end of file
diff --git a/makeconfig b/makeconfig
new file mode 120000
index 0000000..6a5587d
--- /dev/null
+++ b/makeconfig
@@ -0,0 +1 @@
+config/makeconfig
\ No newline at end of file
diff --git a/methodtobosp b/methodtobosp
new file mode 120000
index 0000000..80eaf6c
--- /dev/null
+++ b/methodtobosp
@@ -0,0 +1 @@
+smaliparser/methodtobosp
\ No newline at end of file
diff --git a/name2num b/name2num
new file mode 120000
index 0000000..6640451
--- /dev/null
+++ b/name2num
@@ -0,0 +1 @@
+formatters/name2num.py
\ No newline at end of file
diff --git a/nametoid b/nametoid
new file mode 120000
index 0000000..ebb1dca
--- /dev/null
+++ b/nametoid
@@ -0,0 +1 @@
+formatters/nametoid.py
\ No newline at end of file
diff --git a/num2name b/num2name
new file mode 120000
index 0000000..ed88368
--- /dev/null
+++ b/num2name
@@ -0,0 +1 @@
+formatters/num2name.py
\ No newline at end of file
diff --git a/otadiff b/otadiff
new file mode 100755
index 0000000..adc9325
--- /dev/null
+++ b/otadiff
@@ -0,0 +1,131 @@
+#!/bin/bash
+
+REMOVE_PRE_ZIP=false
+BOARD_META=$PORT_ROOT/board/release/META
+OTA_FROM_TARGET=$PORT_BUILD/tools/releasetools/ota_from_target_files
+TEST_KEY=$PORT_BUILD/security/testkey
+OUT_DIR=out
+
+function getsignotapara()
+{
+ unzip -j -o $POST_ZIP META/misc_info.txt -d $OUT_DIR > /dev/null 2>&1
+ if [ $? == 0 -a -f $OUT_DIR/misc_info.txt ];then
+ if [ $(cat $OUT_DIR/misc_info.txt | grep "not_sign_ota=true" | wc -l) = 1 ];then
+# SIGN_OTA_PARA=--no_sign
+ echo ">>> Won't sign Incremental OTA Package."
+ return 0
+ fi
+ rm -f $OUT_DIR/misc_info.txt
+ fi
+ SIGN_OTA_PARA=""
+ return 1
+}
+
+function getversionnum()
+{
+ target_file=$1
+ version=""
+ name=""
+ unzip -j -o $target_file SYSTEM/build.prop -d $OUT_DIR > /dev/null 2>&1
+ if [ $? == 0 -a -f $OUT_DIR/build.prop ];then
+ if [ $(cat $OUT_DIR/build.prop | grep "ro.build.display.id" | wc -l) == 1 ];then
+ version=$(cat $OUT_DIR/build.prop | grep "ro.build.display.id" | awk -F= '{print $2}')
+ fi
+ if [ $(cat $OUT_DIR/build.prop | grep "ro.product.name" | wc -l) == 1 ];then
+ name=$(cat $OUT_DIR/build.prop | grep "ro.product.name" | awk -F= '{print $2}')
+ fi
+ rm -f $OUT_DIR/build.prop
+ fi
+ if [ x"$version" = x"" ];then
+ version=$(basename $target_file)
+ version=${version%%.zip*}
+ fi
+}
+
+function otadiff()
+{
+ getsignotapara
+ OUT_ZIP=${OUT_ZIP// /_}
+ echo "$OTA_FROM_TARGET $SIGN_OTA_PARA -n -k $TEST_KEY -i $PRE_ZIP $POST_ZIP $OUT_ZIP"
+ $OTA_FROM_TARGET $SIGN_OTA_PARA -n -k $TEST_KEY -i $PRE_ZIP $POST_ZIP $OUT_ZIP
+ echo ">>> Out ==> $OUT_ZIP"
+}
+
+function ota2target()
+{
+ unzip -t $PRE_ZIP SYSTEM/build.prop > /dev/null 2>&1
+ if [ $? == 0 ];then
+ return 0
+ fi
+
+ echo ">>> create a target package from ota package ..."
+ preTmpDir=`mktemp -dt pre.target.XXXX`
+ unzip -q -o $PRE_ZIP -d $preTmpDir
+
+ if [ -d $preTmpDir/system ]; then
+ mv $preTmpDir/system $preTmpDir/SYSTEM
+ ls $preTmpDir/*.img
+ if [ $? == 0 ]; then
+ mkdir $preTmpDir/BOOTABLE_IMAGES
+ mv $preTmpDir/*.img $preTmpDir/BOOTABLE_IMAGES
+ fi
+
+ postTmpDir=`mktemp -dt post.target.XXXX`
+ unzip -q -o $POST_ZIP -d $postTmpDir
+
+ if [ -d $postTmpDir/META ]; then
+ cp -rf $postTmpDir/META $preTmpDir
+ else
+ cp -rf $BOARD_META $preTmpDir
+ fi
+
+ newTarget=`mktemp -tu new.target.XXXX.zip`
+ cd $preTmpDir
+ zip $newTarget * -rqy
+ cd -
+
+ PRE_ZIP=$newTarget
+ REMOVE_PRE_ZIP=true
+
+ rm -rf $postTmpDir
+ fi
+ #echo "preTmpDir: $preTmpDir"
+ rm -rf $preTmpDir
+}
+
+function getotadiffname()
+{
+ if [ x"$OUT_ZIP" == x"" ]; then
+ getversionnum $PRE_ZIP
+ pre_version=$version
+ getversionnum $POST_ZIP
+ post_version=$version
+ OUT_ZIP=$OUT_DIR/ota-diff-$name-$pre_version-$post_version.zip
+ echo ">>> Create ota package name: $OUT_ZIP"
+ fi
+}
+
+function main()
+{
+ ota2target
+ getotadiffname
+ otadiff $PRE_ZIP $POST_ZIP $OUT_ZIP
+
+ if [ $REMOVE_PRE_ZIP == true ]; then
+ rm $PRE_ZIP
+ fi
+}
+
+if [ $# -lt 2 ]; then
+ echo "Usage: otadiff pre-target-files.zip/ota.zip target-files.zip [ota-diff.zip]"
+ echo " pre-target-files.zip: the previous target files or ota.zip"
+ echo " target-files.zip: the current target files"
+ echo " [ota-diff.zip]: the ota update zip"
+ exit 1
+fi
+
+PRE_ZIP=$1
+POST_ZIP=$2
+OUT_ZIP=$3
+
+main
diff --git a/otanormalize b/otanormalize
new file mode 120000
index 0000000..d0baae3
--- /dev/null
+++ b/otanormalize
@@ -0,0 +1 @@
+reverses/otanormalize.py
\ No newline at end of file
diff --git a/pack_bootimg b/pack_bootimg
new file mode 120000
index 0000000..6277631
--- /dev/null
+++ b/pack_bootimg
@@ -0,0 +1 @@
+bootimgpack/pack_bootimg.py
\ No newline at end of file
diff --git a/pull b/pull
new file mode 100755
index 0000000..beade9f
--- /dev/null
+++ b/pull
@@ -0,0 +1,31 @@
+#!/bin/bash
+# author: tangliuxiang
+
+function usage()
+{
+ echo "USAGE: pull "
+ echo " - copy file/dir from device to pc"
+ echo "Just like the usage of adb pull"
+}
+
+if [ "$#" -lt 2 ]; then
+ usage
+ exit 1
+fi
+
+progdir=`dirname $0`/su-tools
+
+check-su 2>&1 > /dev/null
+
+if [ $? == 0 ]; then
+ su-pull $@ || exit $?
+else
+ adb root
+ echo "wait for devices..."
+ adb wait-for-devices
+ adb remount
+ adb wait-for-devices
+ adb pull $@ || exit $?
+
+ echo ">>> Success pulled $1 to $2"
+fi
diff --git a/pull_boot_recovery b/pull_boot_recovery
new file mode 120000
index 0000000..91c2f4e
--- /dev/null
+++ b/pull_boot_recovery
@@ -0,0 +1 @@
+bootimgpack/pull_boot_recovery.py
\ No newline at end of file
diff --git a/push b/push
new file mode 100755
index 0000000..d409e3f
--- /dev/null
+++ b/push
@@ -0,0 +1,49 @@
+#!/bin/bash
+# author: tangliuxiang
+
+PERMISSION=""
+if [ "x$1" == "x-p" ]; then
+ PERMISSION=$2
+ shift
+ shift
+fi
+
+function usage()
+{
+ echo "USAGE: push "
+ echo " - copy file/dir to device"
+ echo "Just like the usage of adb push"
+}
+
+if [ "$#" -lt 2 ]; then
+ usage
+ exit 1
+fi
+
+progdir=`dirname $0`/su-tools
+
+check-su 2>&1 > /dev/null
+
+if [ $? == 0 ]; then
+ if [ "x$PERMISSION" != "x" ]; then
+ PERMISSION="-p $PERMISSION"
+ fi
+ su-push $PERMISSION $@ || exit $?
+else
+ adb root
+ echo "wait for devices..."
+ adb wait-for-devices
+ adb remount
+ adb wait-for-devices
+ adb push $@ || exit $?
+
+ if [ "x$PERMISSION" != "x" ]; then
+ p_chmod="$progdir/phone-chmod"
+ phone_chmod=/data/local/tmp/phone-chmod
+ adb push $p_chmod $phone_chmod
+ adb shell chmod 777 $phone_chmod
+
+ adb shell $phone_chmod --after-push $1 $PERMISSION $2 || exit $?
+ fi
+ echo ">>> Success pushed $1 to $2"
+fi
diff --git a/reverses/README.md b/reverses/README.md
new file mode 100644
index 0000000..1e8574c
--- /dev/null
+++ b/reverses/README.md
@@ -0,0 +1,25 @@
+Android 6.0 23 Marshmallow
+Android 5.1 22 Lollipop
+Android 5.0 21 Lollipop
+Android 4.4 19 KITKAT
+Android 4.3 18 JELLY_BEAN_MR2
+Android 4.2, 4.2.2 17 JELLY_BEAN_MR1
+Android 4.1, 4.1.1 16 JELLY_BEAN
+Android 4.0.3, 4.0.4 15 ICE_CREAM_SANDWICH_MR1
+Android 4.0, 4.0.1, 4.0.2 14 ICE_CREAM_SANDWICH
+Android 3.2 13 HONEYCOMB_MR2
+Android 3.1.x 12 HONEYCOMB_MR1
+Android 3.0.x 11 HONEYCOMB
+Android 2.3.4
+Android 2.3.3 10 GINGERBREAD_MR1
+Android 2.3.2
+Android 2.3.1
+Android 2.3 9 GINGERBREAD
+Android 2.2.x 8 FROYO
+Android 2.1.x 7 ECLAIR_MR1
+Android 2.0.1 6 ECLAIR_0_1
+Android 2.0 5 ECLAIR
+Android 1.6 4 DONUT
+Android 1.5 3 CUPCAKE
+Android 1.1 2 BASE_1_1
+Android 1.0 1 BASE
diff --git a/reverses/__init__.py b/reverses/__init__.py
new file mode 100755
index 0000000..8b13789
--- /dev/null
+++ b/reverses/__init__.py
@@ -0,0 +1 @@
+
diff --git a/reverses/apktool.jar b/reverses/apktool.jar
new file mode 100644
index 0000000..135b5a8
Binary files /dev/null and b/reverses/apktool.jar differ
diff --git a/reverses/apktool.sh b/reverses/apktool.sh
new file mode 100755
index 0000000..ce75cad
--- /dev/null
+++ b/reverses/apktool.sh
@@ -0,0 +1,88 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This script is a wrapper for smali.jar, so you can simply call "smali",
+# instead of java -jar smali.jar. It is heavily based on the "dx" script
+# from the Android SDK
+
+# Set up prog to be the path of this script, including following symlinks,
+# and set up progdir to be the fully-qualified pathname of its directory.
+prog="$0"
+while [ -h "${prog}" ]; do
+ newProg=`/bin/ls -ld "${prog}"`
+ #echo ${newProg}
+
+
+ newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
+ if expr "x${newProg}" : 'x/' >/dev/null; then
+ prog="${newProg}"
+ else
+ progdir=`dirname "${prog}"`
+ prog="${progdir}/${newProg}"
+ fi
+done
+oldwd=`pwd`
+progdir=`dirname "${prog}"`
+cd "${progdir}"
+progdir=`pwd`
+prog="${progdir}"/`basename "${prog}"`
+cd "${oldwd}"
+
+
+jarfile=apktool.jar
+libdir="$progdir"
+if [ ! -r "$libdir/$jarfile" ]
+then
+ echo `basename "$prog"`": can't find $jarfile"
+ exit 1
+fi
+
+javaOpts=""
+
+# If you want DX to have more memory when executing, uncomment the following
+# line and adjust the value accordingly. Use "java -X" for a list of options
+# you can pass here.
+#
+javaOpts="-Xmx512M"
+
+# Alternatively, this will extract any parameter "-Jxxx" from the command line
+# and pass them to Java (instead of to dx). This makes it possible for you to
+# add a command-line parameter such as "-JXmx256M" in your ant scripts, for
+# example.
+while expr "x$1" : 'x-J' >/dev/null; do
+ opt=`expr "$1" : '-J\(.*\)'`
+ javaOpts="${javaOpts} -${opt}"
+ shift
+done
+
+if [ "$OSTYPE" = "cygwin" ] ; then
+ jarpath=`cygpath -w "$libdir/$jarfile"`
+else
+ jarpath="$libdir/$jarfile"
+fi
+
+ERROR_NO=1
+if [ "x$1" == "xb" ]; then
+ ERROR_NO=161
+elif [ "x$1" == "xd" ]; then
+ ERROR_NO=162
+fi
+
+java $javaOpts -jar $jarpath $@
+RET=$?
+if [ $RET == 1 ]; then
+ exit $ERROR_NO
+fi
diff --git a/reverses/common.py b/reverses/common.py
new file mode 100644
index 0000000..b033e7b
--- /dev/null
+++ b/reverses/common.py
@@ -0,0 +1,163 @@
+#!/usr/bin/python
+
+# Copyright 2015 Coron
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+__author__ = 'duanqz@gmail.com'
+
+
+"""
+Android has different VMs, each might has its own format.
+From 2.3 to 4.3, DALVIK VM is used, and the format ODEX is introduced for efficiency.
+From 4.4, ART comes out, the format ELF is designed for ART.
+
+For easy use, CORON only treat standard OTA package, so we need to convert different OTA
+package to the unified format.
+
+Usage: $shell [-a|-f|-l|-c] -i input.zip -o output.zip
+ --app, -a: only format the system apps
+ --framework, -f: only format the framework jars
+ --apiLevel, -l: set the API Level to deodex
+ --classpath, -c: set the classpath to deodex
+"""
+
+
+import os
+import sys
+import getopt
+import subprocess
+
+TAG="reverse"
+
+class Options(object):
+
+ def usage(self):
+ print __doc__
+
+ def __init__(self):
+ self.apiLevel = None
+ self.classpath = None
+
+ # Only handle system APKs
+ self.formatApp = False
+
+ # Only handle framework JARS
+ self.formatFrw = False
+
+ self.inZip = None
+ self.outZip = None
+
+
+ def handle(self, argv):
+ """ Handle input arguments.
+ """
+
+ if len(argv) <= 1:
+ self.usage()
+ sys.exit(1)
+
+ try:
+ (opts, args) = getopt.getopt(argv[1:], "hafl:c:i:o:", \
+ [ "help", "app", "framework", "apilevel=", "classpath=", "input=", "output=" ])
+ Log.d(TAG, "Program args = %s" %args)
+ except getopt.GetoptError:
+ Options.usage()
+
+ for name, value in opts:
+ if name in ("--help", "-h"):
+ self.usage()
+ sys.exit(0)
+
+ elif name in ("--app", "-a"):
+ self.formatApp = True
+
+ elif name in ("--framework", "-f"):
+ self.formatFrw = True
+
+ elif name in ("--apilevel", "-l"):
+ self.apiLevel = value
+
+ elif name in ("--classpath", "-c"):
+ self.classpath = value
+
+ elif name in ("--input", "-i"):
+ self.inZip = os.path.abspath(value)
+
+ elif name in ("--output", "-o"):
+ self.outZip = os.path.abspath(value)
+
+ if self.inZip == None:
+ Log.e(TAG, "No ota package is presented, nothing to be normalized.")
+ self.usage
+ sys.exit(1)
+
+ if self.outZip == None:
+ self.outZip = self.inZip + ".std.zip"
+
+ if self.formatApp == False and self.formatFrw == False:
+ self.formatApp = self.formatFrw = True
+
+ def dump(self):
+ Log.d(TAG, "input=%s, output=%s, apiLevel=%s" % (self.inZip, self.outZip, self.apiLevel))
+
+
+
+class Utils:
+
+ @staticmethod
+ def runWithOutput(args):
+ subp = Utils.run(args, stdout=subprocess.PIPE)
+ Utils.printSubprocessOut(subp)
+
+
+ @staticmethod
+ def run(args, **kwargs):
+ """Create and return a subprocess.Popen object, printing the command
+ line on the terminal
+ """
+
+ return subprocess.Popen(args, **kwargs)
+
+
+ @staticmethod
+ def printSubprocessOut(subp):
+ while True:
+ buff = subp.stdout.readline().strip('\n')
+ if buff == '' and subp.poll() != None:
+ break
+ if len(buff) > 0:
+ Log.d(TAG, buff)
+
+
+
+class Log:
+
+ DEBUG = False
+
+ @staticmethod
+ def d(tag, message):
+ if Log.DEBUG: print "D/%s: %s" %(tag, message)
+
+ @staticmethod
+ def i(tag, message):
+ print "I/%s: %s" %(tag, message)
+
+ @staticmethod
+ def w(tag, message):
+ print "W/%s: %s" %(tag, message)
+
+ @staticmethod
+ def e(tag, message):
+ print "E/%s: %s" %(tag, message)
\ No newline at end of file
diff --git a/reverses/de-dat/README.md b/reverses/de-dat/README.md
new file mode 100644
index 0000000..3294e29
--- /dev/null
+++ b/reverses/de-dat/README.md
@@ -0,0 +1,49 @@
+Android 5.0 (Lollipop) compiled roms (aosp,cm,stock) are not compressed anymore the way they
+used to be on previous android versions. On previous versions all content inside /system folder
+that has to be extracted within our device was either uncompressed (simple /system folder inside our flashable zip)
+or compressed in a system.img file, which it is a ext4 compressed file; both of these,
+anyway, were readable and we could see all system files (app,framework, etc).
+
+Android 5.0 zip structure:
+* META-INF (folder containing scripts)
+* system.new.dat (compressed /system partition)
+* system.patch.dat
+* system.transfer.list (see explanation below)
+
+# sdat2img
+Convert sparse Android data image (.dat) to filesystem ext4 image (.img)
+
+# img2sdat
+img2sdat binary for Android
+
+## Requirements
+This binary requires Python 3.x installed on your system.
+
+It currently supports Windows x86/x64, Linux x86/x64 & arm/arm64 architectures.
+
+
+
+## Usage
+```
+sdat2img.py
+```
+- `transfer_list` = input, system.transfer.list from rom zip
+- `system_new_file` = input, system.new.dat from rom zip
+- `system_ext4` = output ext4 raw image file
+
+
+
+## Example
+This is a simple example on a Linux system:
+```
+~$ ./sdat2img.py system.transfer.list system.new.dat system.img
+```
+
+
+## Github
+
+
+
+
+## Info
+For more information about this binary, visit .
diff --git a/reverses/de-dat/blockimgdiff.py b/reverses/de-dat/blockimgdiff.py
new file mode 100755
index 0000000..8b179d5
--- /dev/null
+++ b/reverses/de-dat/blockimgdiff.py
@@ -0,0 +1,817 @@
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from __future__ import print_function
+
+from collections import deque, OrderedDict
+from hashlib import sha1
+import heapq
+import itertools
+import multiprocessing
+import os
+import pprint
+import re
+import subprocess
+import sys
+import threading
+import tempfile
+
+from rangelib import *
+
+__all__ = ["EmptyImage", "DataImage", "BlockImageDiff"]
+
+def compute_patch(src, tgt, imgdiff=False):
+ srcfd, srcfile = tempfile.mkstemp(prefix="src-")
+ tgtfd, tgtfile = tempfile.mkstemp(prefix="tgt-")
+ patchfd, patchfile = tempfile.mkstemp(prefix="patch-")
+ os.close(patchfd)
+
+ try:
+ with os.fdopen(srcfd, "wb") as f_src:
+ for p in src:
+ f_src.write(p)
+
+ with os.fdopen(tgtfd, "wb") as f_tgt:
+ for p in tgt:
+ f_tgt.write(p)
+ try:
+ os.unlink(patchfile)
+ except OSError:
+ pass
+ if imgdiff:
+ p = subprocess.call(["imgdiff", "-z", srcfile, tgtfile, patchfile],
+ stdout=open("/dev/null", "a"),
+ stderr=subprocess.STDOUT)
+ else:
+ p = subprocess.call(["bsdiff", srcfile, tgtfile, patchfile])
+
+ if p:
+ raise ValueError("diff failed: " + str(p))
+
+ with open(patchfile, "rb") as f:
+ return f.read()
+ finally:
+ try:
+ os.unlink(srcfile)
+ os.unlink(tgtfile)
+ os.unlink(patchfile)
+ except OSError:
+ pass
+
+class EmptyImage(object):
+ """A zero-length image."""
+ blocksize = 4096
+ care_map = RangeSet()
+ total_blocks = 0
+ file_map = {}
+ def ReadRangeSet(self, ranges):
+ return ()
+ def TotalSha1(self):
+ return sha1().hexdigest()
+
+
+class DataImage(object):
+ """An image wrapped around a single string of data."""
+
+ def __init__(self, data, trim=False, pad=False):
+ self.data = data
+ self.blocksize = 4096
+
+ assert not (trim and pad)
+
+ partial = len(self.data) % self.blocksize
+ if partial > 0:
+ if trim:
+ self.data = self.data[:-partial]
+ elif pad:
+ self.data += '\0' * (self.blocksize - partial)
+ else:
+ raise ValueError(("data for DataImage must be multiple of %d bytes "
+ "unless trim or pad is specified") %
+ (self.blocksize,))
+
+ assert len(self.data) % self.blocksize == 0
+
+ self.total_blocks = len(self.data) / self.blocksize
+ self.care_map = RangeSet(data=(0, self.total_blocks))
+
+ zero_blocks = []
+ nonzero_blocks = []
+ reference = '\0' * self.blocksize
+
+ for i in range(self.total_blocks):
+ d = self.data[i*self.blocksize : (i+1)*self.blocksize]
+ if d == reference:
+ zero_blocks.append(i)
+ zero_blocks.append(i+1)
+ else:
+ nonzero_blocks.append(i)
+ nonzero_blocks.append(i+1)
+
+ self.file_map = {"__ZERO": RangeSet(zero_blocks),
+ "__NONZERO": RangeSet(nonzero_blocks)}
+
+ def ReadRangeSet(self, ranges):
+ return [self.data[s*self.blocksize:e*self.blocksize] for (s, e) in ranges]
+
+ def TotalSha1(self):
+ if not hasattr(self, "sha1"):
+ self.sha1 = sha1(self.data).hexdigest()
+ return self.sha1
+
+
+class Transfer(object):
+ def __init__(self, tgt_name, src_name, tgt_ranges, src_ranges, style, by_id):
+ self.tgt_name = tgt_name
+ self.src_name = src_name
+ self.tgt_ranges = tgt_ranges
+ self.src_ranges = src_ranges
+ self.style = style
+ self.intact = (getattr(tgt_ranges, "monotonic", False) and
+ getattr(src_ranges, "monotonic", False))
+ self.goes_before = {}
+ self.goes_after = {}
+
+ self.stash_before = []
+ self.use_stash = []
+
+ self.id = len(by_id)
+ by_id.append(self)
+
+ def NetStashChange(self):
+ return (sum(sr.size() for (_, sr) in self.stash_before) -
+ sum(sr.size() for (_, sr) in self.use_stash))
+
+ def __str__(self):
+ return (str(self.id) + ": <" + str(self.src_ranges) + " " + self.style +
+ " to " + str(self.tgt_ranges) + ">")
+
+
+# BlockImageDiff works on two image objects. An image object is
+# anything that provides the following attributes:
+#
+# blocksize: the size in bytes of a block, currently must be 4096.
+#
+# total_blocks: the total size of the partition/image, in blocks.
+#
+# care_map: a RangeSet containing which blocks (in the range [0,
+# total_blocks) we actually care about; i.e. which blocks contain
+# data.
+#
+# file_map: a dict that partitions the blocks contained in care_map
+# into smaller domains that are useful for doing diffs on.
+# (Typically a domain is a file, and the key in file_map is the
+# pathname.)
+#
+# ReadRangeSet(): a function that takes a RangeSet and returns the
+# data contained in the image blocks of that RangeSet. The data
+# is returned as a list or tuple of strings; concatenating the
+# elements together should produce the requested data.
+# Implementations are free to break up the data into list/tuple
+# elements in any way that is convenient.
+#
+# TotalSha1(): a function that returns (as a hex string) the SHA-1
+# hash of all the data in the image (ie, all the blocks in the
+# care_map)
+#
+# When creating a BlockImageDiff, the src image may be None, in which
+# case the list of transfers produced will never read from the
+# original image.
+
+class BlockImageDiff(object):
+ def __init__(self, tgt, src=None, threads=None, version=2):
+ if threads is None:
+ threads = multiprocessing.cpu_count() // 2
+ if threads == 0: threads = 1
+ self.threads = threads
+ self.version = version
+
+ assert version in (1, 2)
+
+ self.tgt = tgt
+ if src is None:
+ src = EmptyImage()
+ self.src = src
+
+ # The updater code that installs the patch always uses 4k blocks.
+ assert tgt.blocksize == 4096
+ assert src.blocksize == 4096
+
+ # The range sets in each filemap should comprise a partition of
+ # the care map.
+ self.AssertPartition(src.care_map, src.file_map.values())
+ self.AssertPartition(tgt.care_map, tgt.file_map.values())
+
+ def Compute(self, prefix):
+ # When looking for a source file to use as the diff input for a
+ # target file, we try:
+ # 1) an exact path match if available, otherwise
+ # 2) a exact basename match if available, otherwise
+ # 3) a basename match after all runs of digits are replaced by
+ # "#" if available, otherwise
+ # 4) we have no source for this target.
+ self.AbbreviateSourceNames()
+ self.FindTransfers()
+
+ # Find the ordering dependencies among transfers (this is O(n^2)
+ # in the number of transfers).
+ self.GenerateDigraph()
+ # Find a sequence of transfers that satisfies as many ordering
+ # dependencies as possible (heuristically).
+ self.FindVertexSequence()
+ # Fix up the ordering dependencies that the sequence didn't
+ # satisfy.
+ if self.version == 1:
+ self.RemoveBackwardEdges()
+ else:
+ self.ReverseBackwardEdges()
+ self.ImproveVertexSequence()
+
+ # Double-check our work.
+ self.AssertSequenceGood()
+
+ self.ComputePatches(prefix)
+ self.WriteTransfers(prefix)
+
+ def WriteTransfers(self, prefix):
+ out = []
+
+ total = 0
+ performs_read = False
+
+ stashes = {}
+ stashed_blocks = 0
+ max_stashed_blocks = 0
+
+ free_stash_ids = []
+ next_stash_id = 0
+
+ for xf in self.transfers:
+
+ if self.version < 2:
+ assert not xf.stash_before
+ assert not xf.use_stash
+
+ for s, sr in xf.stash_before:
+ assert s not in stashes
+ if free_stash_ids:
+ sid = heapq.heappop(free_stash_ids)
+ else:
+ sid = next_stash_id
+ next_stash_id += 1
+ stashes[s] = sid
+ stashed_blocks += sr.size()
+ out.append("stash %d %s\n" % (sid, sr.to_string_raw()))
+
+ if stashed_blocks > max_stashed_blocks:
+ max_stashed_blocks = stashed_blocks
+
+ if self.version == 1:
+ src_string = xf.src_ranges.to_string_raw()
+ elif self.version == 2:
+
+ # <# blocks>
+ # OR
+ # <# blocks>
+ # OR
+ # <# blocks> -
+
+ size = xf.src_ranges.size()
+ src_string = [str(size)]
+
+ unstashed_src_ranges = xf.src_ranges
+ mapped_stashes = []
+ for s, sr in xf.use_stash:
+ sid = stashes.pop(s)
+ stashed_blocks -= sr.size()
+ unstashed_src_ranges = unstashed_src_ranges.subtract(sr)
+ sr = xf.src_ranges.map_within(sr)
+ mapped_stashes.append(sr)
+ src_string.append("%d:%s" % (sid, sr.to_string_raw()))
+ heapq.heappush(free_stash_ids, sid)
+
+ if unstashed_src_ranges:
+ src_string.insert(1, unstashed_src_ranges.to_string_raw())
+ if xf.use_stash:
+ mapped_unstashed = xf.src_ranges.map_within(unstashed_src_ranges)
+ src_string.insert(2, mapped_unstashed.to_string_raw())
+ mapped_stashes.append(mapped_unstashed)
+ self.AssertPartition(RangeSet(data=(0, size)), mapped_stashes)
+ else:
+ src_string.insert(1, "-")
+ self.AssertPartition(RangeSet(data=(0, size)), mapped_stashes)
+
+ src_string = " ".join(src_string)
+
+ # both versions:
+ # zero
+ # new
+ # erase
+ #
+ # version 1:
+ # bsdiff patchstart patchlen
+ # imgdiff patchstart patchlen
+ # move
+ #
+ # version 2:
+ # bsdiff patchstart patchlen
+ # imgdiff patchstart patchlen
+ # move
+
+ tgt_size = xf.tgt_ranges.size()
+
+ if xf.style == "new":
+ assert xf.tgt_ranges
+ out.append("%s %s\n" % (xf.style, xf.tgt_ranges.to_string_raw()))
+ total += tgt_size
+ elif xf.style == "move":
+ performs_read = True
+ assert xf.tgt_ranges
+ assert xf.src_ranges.size() == tgt_size
+ if xf.src_ranges != xf.tgt_ranges:
+ if self.version == 1:
+ out.append("%s %s %s\n" % (
+ xf.style,
+ xf.src_ranges.to_string_raw(), xf.tgt_ranges.to_string_raw()))
+ elif self.version == 2:
+ out.append("%s %s %s\n" % (
+ xf.style,
+ xf.tgt_ranges.to_string_raw(), src_string))
+ total += tgt_size
+ elif xf.style in ("bsdiff", "imgdiff"):
+ performs_read = True
+ assert xf.tgt_ranges
+ assert xf.src_ranges
+ if self.version == 1:
+ out.append("%s %d %d %s %s\n" % (
+ xf.style, xf.patch_start, xf.patch_len,
+ xf.src_ranges.to_string_raw(), xf.tgt_ranges.to_string_raw()))
+ elif self.version == 2:
+ out.append("%s %d %d %s %s\n" % (
+ xf.style, xf.patch_start, xf.patch_len,
+ xf.tgt_ranges.to_string_raw(), src_string))
+ total += tgt_size
+ elif xf.style == "zero":
+ assert xf.tgt_ranges
+ to_zero = xf.tgt_ranges.subtract(xf.src_ranges)
+ if to_zero:
+ out.append("%s %s\n" % (xf.style, to_zero.to_string_raw()))
+ total += to_zero.size()
+ else:
+ raise ValueError, "unknown transfer style '%s'\n" % (xf.style,)
+
+
+ # sanity check: abort if we're going to need more than 512 MB if
+ # stash space
+ assert max_stashed_blocks * self.tgt.blocksize < (512 << 20)
+
+ all_tgt = RangeSet(data=(0, self.tgt.total_blocks))
+ if performs_read:
+ # if some of the original data is used, then at the end we'll
+ # erase all the blocks on the partition that don't contain data
+ # in the new image.
+ new_dontcare = all_tgt.subtract(self.tgt.care_map)
+ if new_dontcare:
+ out.append("erase %s\n" % (new_dontcare.to_string_raw(),))
+ else:
+ # if nothing is read (ie, this is a full OTA), then we can start
+ # by erasing the entire partition.
+ out.insert(0, "erase %s\n" % (all_tgt.to_string_raw(),))
+
+ out.insert(0, "%d\n" % (self.version,)) # format version number
+ out.insert(1, str(total) + "\n")
+ if self.version >= 2:
+ # version 2 only: after the total block count, we give the number
+ # of stash slots needed, and the maximum size needed (in blocks)
+ out.insert(2, str(next_stash_id) + "\n")
+ out.insert(3, str(max_stashed_blocks) + "\n")
+
+ with open(prefix + ".transfer.list", "wb") as f:
+ for i in out:
+ f.write(i)
+
+ if self.version >= 2:
+ print("max stashed blocks: %d (%d bytes)\n" % (
+ max_stashed_blocks, max_stashed_blocks * self.tgt.blocksize))
+
+ def ComputePatches(self, prefix):
+ print("Reticulating splines...")
+ diff_q = []
+ patch_num = 0
+ with open(prefix + ".new.dat", "wb") as new_f:
+ for xf in self.transfers:
+ if xf.style == "zero":
+ pass
+ elif xf.style == "new":
+ for piece in self.tgt.ReadRangeSet(xf.tgt_ranges):
+ new_f.write(piece)
+ elif xf.style == "diff":
+ src = self.src.ReadRangeSet(xf.src_ranges)
+ tgt = self.tgt.ReadRangeSet(xf.tgt_ranges)
+
+ # We can't compare src and tgt directly because they may have
+ # the same content but be broken up into blocks differently, eg:
+ #
+ # ["he", "llo"] vs ["h", "ello"]
+ #
+ # We want those to compare equal, ideally without having to
+ # actually concatenate the strings (these may be tens of
+ # megabytes).
+
+ src_sha1 = sha1()
+ for p in src:
+ src_sha1.update(p)
+ tgt_sha1 = sha1()
+ tgt_size = 0
+ for p in tgt:
+ tgt_sha1.update(p)
+ tgt_size += len(p)
+
+ if src_sha1.digest() == tgt_sha1.digest():
+ # These are identical; we don't need to generate a patch,
+ # just issue copy commands on the device.
+ xf.style = "move"
+ else:
+ # For files in zip format (eg, APKs, JARs, etc.) we would
+ # like to use imgdiff -z if possible (because it usually
+ # produces significantly smaller patches than bsdiff).
+ # This is permissible if:
+ #
+ # - the source and target files are monotonic (ie, the
+ # data is stored with blocks in increasing order), and
+ # - we haven't removed any blocks from the source set.
+ #
+ # If these conditions are satisfied then appending all the
+ # blocks in the set together in order will produce a valid
+ # zip file (plus possibly extra zeros in the last block),
+ # which is what imgdiff needs to operate. (imgdiff is
+ # fine with extra zeros at the end of the file.)
+ imgdiff = (xf.intact and
+ xf.tgt_name.split(".")[-1].lower()
+ in ("apk", "jar", "zip"))
+ xf.style = "imgdiff" if imgdiff else "bsdiff"
+ diff_q.append((tgt_size, src, tgt, xf, patch_num))
+ patch_num += 1
+
+ else:
+ assert False, "unknown style " + xf.style
+
+ if diff_q:
+ if self.threads > 1:
+ print("Computing patches (using %d threads)..." % (self.threads,))
+ else:
+ print("Computing patches...")
+ diff_q.sort()
+
+ patches = [None] * patch_num
+
+ lock = threading.Lock()
+ def diff_worker():
+ while True:
+ with lock:
+ if not diff_q: return
+ tgt_size, src, tgt, xf, patchnum = diff_q.pop()
+ patch = compute_patch(src, tgt, imgdiff=(xf.style == "imgdiff"))
+ size = len(patch)
+ with lock:
+ patches[patchnum] = (patch, xf)
+ print("%10d %10d (%6.2f%%) %7s %s" % (
+ size, tgt_size, size * 100.0 / tgt_size, xf.style,
+ xf.tgt_name if xf.tgt_name == xf.src_name else (
+ xf.tgt_name + " (from " + xf.src_name + ")")))
+
+ threads = [threading.Thread(target=diff_worker)
+ for i in range(self.threads)]
+ for th in threads:
+ th.start()
+ while threads:
+ threads.pop().join()
+ else:
+ patches = []
+
+ p = 0
+ with open(prefix + ".patch.dat", "wb") as patch_f:
+ for patch, xf in patches:
+ xf.patch_start = p
+ xf.patch_len = len(patch)
+ patch_f.write(patch)
+ p += len(patch)
+
+ def AssertSequenceGood(self):
+ # Simulate the sequences of transfers we will output, and check that:
+ # - we never read a block after writing it, and
+ # - we write every block we care about exactly once.
+
+ # Start with no blocks having been touched yet.
+ touched = RangeSet()
+
+ # Imagine processing the transfers in order.
+ for xf in self.transfers:
+ # Check that the input blocks for this transfer haven't yet been touched.
+
+ x = xf.src_ranges
+ if self.version >= 2:
+ for _, sr in xf.use_stash:
+ x = x.subtract(sr)
+
+ assert not touched.overlaps(x)
+ # Check that the output blocks for this transfer haven't yet been touched.
+ assert not touched.overlaps(xf.tgt_ranges)
+ # Touch all the blocks written by this transfer.
+ touched = touched.union(xf.tgt_ranges)
+
+ # Check that we've written every target block.
+ assert touched == self.tgt.care_map
+
+ def ImproveVertexSequence(self):
+ print("Improving vertex order...")
+
+ # At this point our digraph is acyclic; we reversed any edges that
+ # were backwards in the heuristically-generated sequence. The
+ # previously-generated order is still acceptable, but we hope to
+ # find a better order that needs less memory for stashed data.
+ # Now we do a topological sort to generate a new vertex order,
+ # using a greedy algorithm to choose which vertex goes next
+ # whenever we have a choice.
+
+ # Make a copy of the edge set; this copy will get destroyed by the
+ # algorithm.
+ for xf in self.transfers:
+ xf.incoming = xf.goes_after.copy()
+ xf.outgoing = xf.goes_before.copy()
+
+ L = [] # the new vertex order
+
+ # S is the set of sources in the remaining graph; we always choose
+ # the one that leaves the least amount of stashed data after it's
+ # executed.
+ S = [(u.NetStashChange(), u.order, u) for u in self.transfers
+ if not u.incoming]
+ heapq.heapify(S)
+
+ while S:
+ _, _, xf = heapq.heappop(S)
+ L.append(xf)
+ for u in xf.outgoing:
+ del u.incoming[xf]
+ if not u.incoming:
+ heapq.heappush(S, (u.NetStashChange(), u.order, u))
+
+ # if this fails then our graph had a cycle.
+ assert len(L) == len(self.transfers)
+
+ self.transfers = L
+ for i, xf in enumerate(L):
+ xf.order = i
+
+ def RemoveBackwardEdges(self):
+ print("Removing backward edges...")
+ in_order = 0
+ out_of_order = 0
+ lost_source = 0
+
+ for xf in self.transfers:
+ lost = 0
+ size = xf.src_ranges.size()
+ for u in xf.goes_before:
+ # xf should go before u
+ if xf.order < u.order:
+ # it does, hurray!
+ in_order += 1
+ else:
+ # it doesn't, boo. trim the blocks that u writes from xf's
+ # source, so that xf can go after u.
+ out_of_order += 1
+ assert xf.src_ranges.overlaps(u.tgt_ranges)
+ xf.src_ranges = xf.src_ranges.subtract(u.tgt_ranges)
+ xf.intact = False
+
+ if xf.style == "diff" and not xf.src_ranges:
+ # nothing left to diff from; treat as new data
+ xf.style = "new"
+
+ lost = size - xf.src_ranges.size()
+ lost_source += lost
+
+ print((" %d/%d dependencies (%.2f%%) were violated; "
+ "%d source blocks removed.") %
+ (out_of_order, in_order + out_of_order,
+ (out_of_order * 100.0 / (in_order + out_of_order))
+ if (in_order + out_of_order) else 0.0,
+ lost_source))
+
+ def ReverseBackwardEdges(self):
+ print("Reversing backward edges...")
+ in_order = 0
+ out_of_order = 0
+ stashes = 0
+ stash_size = 0
+
+ for xf in self.transfers:
+ lost = 0
+ size = xf.src_ranges.size()
+ for u in xf.goes_before.copy():
+ # xf should go before u
+ if xf.order < u.order:
+ # it does, hurray!
+ in_order += 1
+ else:
+ # it doesn't, boo. modify u to stash the blocks that it
+ # writes that xf wants to read, and then require u to go
+ # before xf.
+ out_of_order += 1
+
+ overlap = xf.src_ranges.intersect(u.tgt_ranges)
+ assert overlap
+
+ u.stash_before.append((stashes, overlap))
+ xf.use_stash.append((stashes, overlap))
+ stashes += 1
+ stash_size += overlap.size()
+
+ # reverse the edge direction; now xf must go after u
+ del xf.goes_before[u]
+ del u.goes_after[xf]
+ xf.goes_after[u] = None # value doesn't matter
+ u.goes_before[xf] = None
+
+ print((" %d/%d dependencies (%.2f%%) were violated; "
+ "%d source blocks stashed.") %
+ (out_of_order, in_order + out_of_order,
+ (out_of_order * 100.0 / (in_order + out_of_order))
+ if (in_order + out_of_order) else 0.0,
+ stash_size))
+
+ def FindVertexSequence(self):
+ print("Finding vertex sequence...")
+
+ # This is based on "A Fast & Effective Heuristic for the Feedback
+ # Arc Set Problem" by P. Eades, X. Lin, and W.F. Smyth. Think of
+ # it as starting with the digraph G and moving all the vertices to
+ # be on a horizontal line in some order, trying to minimize the
+ # number of edges that end up pointing to the left. Left-pointing
+ # edges will get removed to turn the digraph into a DAG. In this
+ # case each edge has a weight which is the number of source blocks
+ # we'll lose if that edge is removed; we try to minimize the total
+ # weight rather than just the number of edges.
+
+ # Make a copy of the edge set; this copy will get destroyed by the
+ # algorithm.
+ for xf in self.transfers:
+ xf.incoming = xf.goes_after.copy()
+ xf.outgoing = xf.goes_before.copy()
+
+ # We use an OrderedDict instead of just a set so that the output
+ # is repeatable; otherwise it would depend on the hash values of
+ # the transfer objects.
+ G = OrderedDict()
+ for xf in self.transfers:
+ G[xf] = None
+ s1 = deque() # the left side of the sequence, built from left to right
+ s2 = deque() # the right side of the sequence, built from right to left
+
+ while G:
+
+ # Put all sinks at the end of the sequence.
+ while True:
+ sinks = [u for u in G if not u.outgoing]
+ if not sinks: break
+ for u in sinks:
+ s2.appendleft(u)
+ del G[u]
+ for iu in u.incoming:
+ del iu.outgoing[u]
+
+ # Put all the sources at the beginning of the sequence.
+ while True:
+ sources = [u for u in G if not u.incoming]
+ if not sources: break
+ for u in sources:
+ s1.append(u)
+ del G[u]
+ for iu in u.outgoing:
+ del iu.incoming[u]
+
+ if not G: break
+
+ # Find the "best" vertex to put next. "Best" is the one that
+ # maximizes the net difference in source blocks saved we get by
+ # pretending it's a source rather than a sink.
+
+ max_d = None
+ best_u = None
+ for u in G:
+ d = sum(u.outgoing.values()) - sum(u.incoming.values())
+ if best_u is None or d > max_d:
+ max_d = d
+ best_u = u
+
+ u = best_u
+ s1.append(u)
+ del G[u]
+ for iu in u.outgoing:
+ del iu.incoming[u]
+ for iu in u.incoming:
+ del iu.outgoing[u]
+
+ # Now record the sequence in the 'order' field of each transfer,
+ # and by rearranging self.transfers to be in the chosen sequence.
+
+ new_transfers = []
+ for x in itertools.chain(s1, s2):
+ x.order = len(new_transfers)
+ new_transfers.append(x)
+ del x.incoming
+ del x.outgoing
+
+ self.transfers = new_transfers
+
+ def GenerateDigraph(self):
+ print("Generating digraph...")
+ for a in self.transfers:
+ for b in self.transfers:
+ if a is b: continue
+
+ # If the blocks written by A are read by B, then B needs to go before A.
+ i = a.tgt_ranges.intersect(b.src_ranges)
+ if i:
+ if b.src_name == "__ZERO":
+ # the cost of removing source blocks for the __ZERO domain
+ # is (nearly) zero.
+ size = 0
+ else:
+ size = i.size()
+ b.goes_before[a] = size
+ a.goes_after[b] = size
+
+ def FindTransfers(self):
+ self.transfers = []
+ empty = RangeSet()
+ for tgt_fn, tgt_ranges in self.tgt.file_map.items():
+ if tgt_fn == "__ZERO":
+ # the special "__ZERO" domain is all the blocks not contained
+ # in any file and that are filled with zeros. We have a
+ # special transfer style for zero blocks.
+ src_ranges = self.src.file_map.get("__ZERO", empty)
+ Transfer(tgt_fn, "__ZERO", tgt_ranges, src_ranges,
+ "zero", self.transfers)
+ continue
+
+ elif tgt_fn in self.src.file_map:
+ # Look for an exact pathname match in the source.
+ Transfer(tgt_fn, tgt_fn, tgt_ranges, self.src.file_map[tgt_fn],
+ "diff", self.transfers)
+ continue
+
+ b = os.path.basename(tgt_fn)
+ if b in self.src_basenames:
+ # Look for an exact basename match in the source.
+ src_fn = self.src_basenames[b]
+ Transfer(tgt_fn, src_fn, tgt_ranges, self.src.file_map[src_fn],
+ "diff", self.transfers)
+ continue
+
+ b = re.sub("[0-9]+", "#", b)
+ if b in self.src_numpatterns:
+ # Look for a 'number pattern' match (a basename match after
+ # all runs of digits are replaced by "#"). (This is useful
+ # for .so files that contain version numbers in the filename
+ # that get bumped.)
+ src_fn = self.src_numpatterns[b]
+ Transfer(tgt_fn, src_fn, tgt_ranges, self.src.file_map[src_fn],
+ "diff", self.transfers)
+ continue
+
+ Transfer(tgt_fn, None, tgt_ranges, empty, "new", self.transfers)
+
+ def AbbreviateSourceNames(self):
+ self.src_basenames = {}
+ self.src_numpatterns = {}
+
+ for k in self.src.file_map.keys():
+ b = os.path.basename(k)
+ self.src_basenames[b] = k
+ b = re.sub("[0-9]+", "#", b)
+ self.src_numpatterns[b] = k
+
+ @staticmethod
+ def AssertPartition(total, seq):
+ """Assert that all the RangeSets in 'seq' form a partition of the
+ 'total' RangeSet (ie, they are nonintersecting and their union
+ equals 'total')."""
+ so_far = RangeSet()
+ for i in seq:
+ assert not so_far.overlaps(i)
+ so_far = so_far.union(i)
+ assert so_far == total
diff --git a/reverses/de-dat/dedat.sh b/reverses/de-dat/dedat.sh
new file mode 100755
index 0000000..925ced3
--- /dev/null
+++ b/reverses/de-dat/dedat.sh
@@ -0,0 +1,91 @@
+#!/bin/bash
+#
+# Copyright 2015 Coron
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+ROOT=$1
+SYS_TRANS_LIST=$ROOT/system.transfer.list
+SYS_NEW_DAT=$ROOT/system.new.dat
+SYS_EXT4_IMG=$ROOT/system.img
+SYS_DIR=$ROOT/system
+
+function usage()
+{
+ echo "Usage: $0 ROOT_DIRECTORY_OF_UNZIPED_OTA_PACKAGE"
+}
+
+
+function convert_dat_to_img()
+{
+
+ [ ! -e $SYS_TRANS_LIST ] && return
+ [ ! -e $SYS_NEW_DAT ] && return
+
+ $SDAT2IMG $SYS_TRANS_LIST $SYS_NEW_DAT $SYS_EXT4_IMG
+}
+
+
+function unpack_sys_ext4_img()
+{
+ [ ! -e $SYS_EXT4_IMG ] && return
+
+ local name=`whoami`
+ local outdir=`mktemp -d /tmp/dedat.mount.XXXXX`
+ sudo mount -t ext4 -o loop $SYS_EXT4_IMG $outdir
+ sudo cp -r $outdir $SYS_DIR
+ sudo chown -R $name:$name $SYS_DIR
+ sudo umount $outdir
+ rm -rf $outdir
+
+ echo "DEDAT ===> $SYS_DIR"
+}
+
+prog="$0"
+while [ -h "${prog}" ]; do
+ newProg=`/bin/ls -ld "${prog}"`
+
+ newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
+ if expr "x${newProg}" : 'x/' >/dev/null; then
+ prog="${newProg}"
+ else
+ progdir=`dirname "${prog}"`
+ prog="${progdir}/${newProg}"
+ fi
+done
+oldwd=`pwd`
+progdir=`dirname "${prog}"`
+cd "${progdir}"
+progdir=`pwd`
+prog="${progdir}"/`basename "${prog}"`
+cd "${oldwd}"
+
+SDAT2IMG=sdat2img.py
+if [ ! -r "$progdir/$SDAT2IMG" ]
+then
+ echo `basename "$prog"`": can't find $SDAT2IMG"
+ exit 1
+fi
+
+if [ "$OSTYPE" = "cygwin" ] ; then
+ SDAT2IMG=`cygpath -w "$progdir/$SDAT2IMG"`
+else
+ SDAT2IMG="$progdir/$SDAT2IMG"
+fi
+
+[ ! -d "$ROOT" ] && usage && exit 1
+
+convert_dat_to_img
+unpack_sys_ext4_img
+
diff --git a/reverses/de-dat/img2sdat.py b/reverses/de-dat/img2sdat.py
new file mode 100755
index 0000000..471f853
--- /dev/null
+++ b/reverses/de-dat/img2sdat.py
@@ -0,0 +1,40 @@
+#!/usr/bin/env python
+#encoding:utf8
+#====================================================
+# FILE: img2sdat.py
+# AUTHORS: xpirt - luxi78 - howellzhu
+# DATE: 2015-10-11 14:37:22 CST
+#====================================================
+
+import sys, blockimgdiff, sparse_img, os
+
+def main(sysimg, outdir):
+ tgt = sparse_img.SparseImage(sysimg)
+ bif = blockimgdiff.BlockImageDiff(tgt, None)
+ bif.Compute(outdir)
+ return
+
+if __name__ == '__main__':
+ sysimg = 'system.img'
+ outdir = './'
+ if len(sys.argv) == 1:
+ print ("\nimg2sdat - usage is: \n\n img2sdat [system.img] [outdir]\n\n")
+ print ("Visit xda thread for more information.\n")
+ try:
+ input = raw_input
+ except NameError: pass
+ input ("Press ENTER to exit...\n")
+ sys.exit(1)
+ if len(sys.argv) >= 2:
+ sysimg = sys.argv[1]
+ if len(sys.argv) >= 3:
+ outdir = sys.argv[2] + '/'
+ if len(sys.argv) >= 4:
+ print ("\nimg2sdat - usage is: \n\n img2sdat [system.img] [outdir]" % sys.argv[0])
+ print ("Visit xda thread for more information.\n")
+ try:
+ input = raw_input
+ except NameError: pass
+ input ("Press ENTER to exit...\n")
+ sys.exit(1)
+ main(sysimg, outdir)
diff --git a/reverses/de-dat/make_ext4fs b/reverses/de-dat/make_ext4fs
new file mode 100755
index 0000000..cd88dd0
Binary files /dev/null and b/reverses/de-dat/make_ext4fs differ
diff --git a/reverses/de-dat/rangelib.py b/reverses/de-dat/rangelib.py
new file mode 100755
index 0000000..7279c60
--- /dev/null
+++ b/reverses/de-dat/rangelib.py
@@ -0,0 +1,243 @@
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from __future__ import print_function
+import heapq
+import itertools
+
+__all__ = ["RangeSet"]
+
+class RangeSet(object):
+ """A RangeSet represents a set of nonoverlapping ranges on the
+ integers (ie, a set of integers, but efficient when the set contains
+ lots of runs."""
+
+ def __init__(self, data=None):
+ if isinstance(data, str):
+ self._parse_internal(data)
+ elif data:
+ self.data = tuple(self._remove_pairs(data))
+ else:
+ self.data = ()
+
+ def __iter__(self):
+ for i in range(0, len(self.data), 2):
+ yield self.data[i:i+2]
+
+ def __eq__(self, other):
+ return self.data == other.data
+ def __ne__(self, other):
+ return self.data != other.data
+ def __nonzero__(self):
+ return bool(self.data)
+
+ def __str__(self):
+ if not self.data:
+ return "empty"
+ else:
+ return self.to_string()
+
+ def __repr__(self):
+ return ''
+
+ @classmethod
+ def parse(cls, text):
+ """Parse a text string consisting of a space-separated list of
+ blocks and ranges, eg "10-20 30 35-40". Ranges are interpreted to
+ include both their ends (so the above example represents 18
+ individual blocks. Returns a RangeSet object.
+
+ If the input has all its blocks in increasing order, then returned
+ RangeSet will have an extra attribute 'monotonic' that is set to
+ True. For example the input "10-20 30" is monotonic, but the input
+ "15-20 30 10-14" is not, even though they represent the same set
+ of blocks (and the two RangeSets will compare equal with ==).
+ """
+ return cls(text)
+
+ def _parse_internal(self, text):
+ data = []
+ last = -1
+ monotonic = True
+ for p in text.split():
+ if "-" in p:
+ s, e = p.split("-")
+ data.append(int(s))
+ data.append(int(e)+1)
+ if last <= s <= e:
+ last = e
+ else:
+ monotonic = False
+ else:
+ s = int(p)
+ data.append(s)
+ data.append(s+1)
+ if last <= s:
+ last = s+1
+ else:
+ monotonic = True
+ data.sort()
+ self.data = tuple(self._remove_pairs(data))
+ self.monotonic = monotonic
+
+ @staticmethod
+ def _remove_pairs(source):
+ last = None
+ for i in source:
+ if i == last:
+ last = None
+ else:
+ if last is not None:
+ yield last
+ last = i
+ if last is not None:
+ yield last
+
+ def to_string(self):
+ out = []
+ for i in range(0, len(self.data), 2):
+ s, e = self.data[i:i+2]
+ if e == s+1:
+ out.append(str(s))
+ else:
+ out.append(str(s) + "-" + str(e-1))
+ return " ".join(out)
+
+ def to_string_raw(self):
+ return str(len(self.data)) + "," + ",".join(str(i) for i in self.data)
+
+ def union(self, other):
+ """Return a new RangeSet representing the union of this RangeSet
+ with the argument.
+
+ >>> RangeSet("10-19 30-34").union(RangeSet("18-29"))
+
+ >>> RangeSet("10-19 30-34").union(RangeSet("22 32"))
+
+ """
+ out = []
+ z = 0
+ for p, d in heapq.merge(zip(self.data, itertools.cycle((+1, -1))),
+ zip(other.data, itertools.cycle((+1, -1)))):
+ if (z == 0 and d == 1) or (z == 1 and d == -1):
+ out.append(p)
+ z += d
+ return RangeSet(data=out)
+
+ def intersect(self, other):
+ """Return a new RangeSet representing the intersection of this
+ RangeSet with the argument.
+
+ >>> RangeSet("10-19 30-34").intersect(RangeSet("18-32"))
+
+ >>> RangeSet("10-19 30-34").intersect(RangeSet("22-28"))
+
+ """
+ out = []
+ z = 0
+ for p, d in heapq.merge(zip(self.data, itertools.cycle((+1, -1))),
+ zip(other.data, itertools.cycle((+1, -1)))):
+ if (z == 1 and d == 1) or (z == 2 and d == -1):
+ out.append(p)
+ z += d
+ return RangeSet(data=out)
+
+ def subtract(self, other):
+ """Return a new RangeSet representing subtracting the argument
+ from this RangeSet.
+
+ >>> RangeSet("10-19 30-34").subtract(RangeSet("18-32"))
+
+ >>> RangeSet("10-19 30-34").subtract(RangeSet("22-28"))
+
+ """
+
+ out = []
+ z = 0
+ for p, d in heapq.merge(zip(self.data, itertools.cycle((+1, -1))),
+ zip(other.data, itertools.cycle((-1, +1)))):
+ if (z == 0 and d == 1) or (z == 1 and d == -1):
+ out.append(p)
+ z += d
+ return RangeSet(data=out)
+
+ def overlaps(self, other):
+ """Returns true if the argument has a nonempty overlap with this
+ RangeSet.
+
+ >>> RangeSet("10-19 30-34").overlaps(RangeSet("18-32"))
+ True
+ >>> RangeSet("10-19 30-34").overlaps(RangeSet("22-28"))
+ False
+ """
+
+ # This is like intersect, but we can stop as soon as we discover the
+ # output is going to be nonempty.
+ z = 0
+ for p, d in heapq.merge(zip(self.data, itertools.cycle((+1, -1))),
+ zip(other.data, itertools.cycle((+1, -1)))):
+ if (z == 1 and d == 1) or (z == 2 and d == -1):
+ return True
+ z += d
+ return False
+
+ def size(self):
+ """Returns the total size of the RangeSet (ie, how many integers
+ are in the set).
+
+ >>> RangeSet("10-19 30-34").size()
+ 15
+ """
+
+ total = 0
+ for i, p in enumerate(self.data):
+ if i % 2:
+ total += p
+ else:
+ total -= p
+ return total
+
+ def map_within(self, other):
+ """'other' should be a subset of 'self'. Returns a RangeSet
+ representing what 'other' would get translated to if the integers
+ of 'self' were translated down to be contiguous starting at zero.
+
+ >>> RangeSet("0-9").map_within(RangeSet("3-4"))
+
+ >>> RangeSet("10-19").map_within(RangeSet("13-14"))
+
+ >>> RangeSet("10-19 30-39").map_within(RangeSet("17-19 30-32"))
+
+ >>> RangeSet("10-19 30-39").map_within(RangeSet("12-13 17-19 30-32"))
+
+ """
+
+ out = []
+ offset = 0
+ start = None
+ for p, d in heapq.merge(zip(self.data, itertools.cycle((-5, +5))),
+ zip(other.data, itertools.cycle((-1, +1)))):
+ if d == -5:
+ start = p
+ elif d == +5:
+ offset += p-start
+ start = None
+ else:
+ out.append(offset + p - start)
+ return RangeSet(data=out)
+
+
+if __name__ == "__main__":
+ import doctest
+ doctest.testmod()
diff --git a/reverses/de-dat/rimg2sdat b/reverses/de-dat/rimg2sdat
new file mode 100755
index 0000000..e86f17d
Binary files /dev/null and b/reverses/de-dat/rimg2sdat differ
diff --git a/reverses/de-dat/sdat2img.py b/reverses/de-dat/sdat2img.py
new file mode 100755
index 0000000..d44ee34
--- /dev/null
+++ b/reverses/de-dat/sdat2img.py
@@ -0,0 +1,97 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#====================================================
+# FILE: sdat2img.py
+# AUTHORS: xpirt - luxi78 - howellzhu
+# DATE: 2015-10-11 16:33:32 CST
+#====================================================
+
+import sys, os
+
+try:
+ TRANSFER_LIST_FILE = str(sys.argv[1])
+ NEW_DATA_FILE = str(sys.argv[2])
+ OUTPUT_IMAGE_FILE = str(sys.argv[3])
+except IndexError:
+ print ("\nsdat2img - usage is: \n\n sdat2img \n\n")
+ print ("Visit xda thread for more information.\n")
+ try:
+ input = raw_input
+ except NameError: pass
+ input ("Press ENTER to exit...\n")
+ sys.exit()
+
+BLOCK_SIZE = 4096
+
+def rangeset(src):
+ src_set = src.split(',')
+ num_set = [int(item) for item in src_set]
+ if len(num_set) != num_set[0]+1:
+ print ('Error on parsing following data to rangeset:\n%s' % src)
+ sys.exit(1)
+
+ return tuple ([ (num_set[i], num_set[i+1]) for i in range(1, len(num_set), 2) ])
+
+def parse_transfer_list_file(path):
+ trans_list = open(TRANSFER_LIST_FILE, 'r')
+ version = int(trans_list.readline()) # 1st line = transfer list version
+ new_blocks = int(trans_list.readline()) # 2nd line = total number of blocks
+
+ # system.transfer.list:
+ # - version 1: android-5.0.0_r1
+ # - version 2: android-5.1.0_r1
+ # - version 3: android-6.0.0_r1
+
+ # skip next 2 lines. we don't need this stuff now
+ if version >= 2:
+ trans_list.readline() # 3rd line = stash entries needed simultaneously
+ trans_list.readline() # 4th line = number of blocks that will be stashed
+
+ commands = []
+ for line in trans_list:
+ line = line.split(' ') # 5th & next lines should be only commands
+ cmd = line[0]
+ if cmd in ['erase', 'new', 'zero']:
+ commands.append([cmd, rangeset(line[1])])
+ else:
+ # skip lines starting with numbers, they're not commands anyway.
+ if not cmd[0].isdigit():
+ print ('No valid command: %s.' % cmd)
+ trans_list.close()
+ sys.exit(1)
+
+ trans_list.close()
+ return version, new_blocks, commands
+
+def init_output_file_size(output_file_obj, commands):
+ all_block_sets = [i for command in commands for i in command[1]]
+ max_block_num = max(pair[1] for pair in all_block_sets)
+ output_file_obj.seek(max_block_num*BLOCK_SIZE - 1)
+ output_file_obj.write('\0'.encode('utf-8'))
+ output_file_obj.flush()
+
+def main(argv):
+ version, new_blocks, commands = parse_transfer_list_file(TRANSFER_LIST_FILE)
+ output_img = open(OUTPUT_IMAGE_FILE, 'wb')
+ init_output_file_size(output_img, commands)
+ new_data_file = open(NEW_DATA_FILE, 'rb')
+
+ for command in commands:
+ if command[0] == 'new':
+ for block in command[1]:
+ begin = block[0]
+ end = block[1]
+ block_count = end - begin
+ data = new_data_file.read(block_count*BLOCK_SIZE)
+ print('Copying {} blocks into position {}...'.format(block_count, begin))
+ output_img.seek(begin*BLOCK_SIZE)
+ output_img.write(data)
+ else:
+ print('Skipping command %s' % command[0])
+
+ output_img.close()
+ new_data_file.close()
+ print ('\nDone! Output image: %s' % os.path.realpath(output_img.name))
+
+if __name__ == "__main__":
+ main(sys.argv)
diff --git a/reverses/de-dat/sparse_img.py b/reverses/de-dat/sparse_img.py
new file mode 100755
index 0000000..7574747
--- /dev/null
+++ b/reverses/de-dat/sparse_img.py
@@ -0,0 +1,213 @@
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import bisect
+import os
+import sys
+import struct
+import pprint
+from hashlib import sha1
+
+from rangelib import *
+
+class SparseImage(object):
+ """Wraps a sparse image file (and optional file map) into an image
+ object suitable for passing to BlockImageDiff."""
+
+ def __init__(self, simg_fn, file_map_fn=None):
+ self.simg_f = f = open(simg_fn, "rb")
+
+ header_bin = f.read(28)
+ header = struct.unpack("> 2))
+ to_read -= this_read
+
+ while to_read > 0:
+ # continue with following chunks if this range spans multiple chunks.
+ idx += 1
+ chunk_start, chunk_len, filepos, fill_data = self.offset_map[idx]
+ this_read = min(chunk_len, to_read)
+ if filepos is not None:
+ f.seek(filepos, os.SEEK_SET)
+ yield f.read(this_read * self.blocksize)
+ else:
+ yield fill_data * (this_read * (self.blocksize >> 2))
+ to_read -= this_read
+
+ def LoadFileBlockMap(self, fn):
+ remaining = self.care_map
+ self.file_map = out = {}
+
+ with open(fn) as f:
+ for line in f:
+ fn, ranges = line.split(None, 1)
+ ranges = RangeSet.parse(ranges)
+ out[fn] = ranges
+ assert ranges.size() == ranges.intersect(remaining).size()
+ remaining = remaining.subtract(ranges)
+
+ # For all the remaining blocks in the care_map (ie, those that
+ # aren't part of the data for any file), divide them into blocks
+ # that are all zero and blocks that aren't. (Zero blocks are
+ # handled specially because (1) there are usually a lot of them
+ # and (2) bsdiff handles files with long sequences of repeated
+ # bytes especially poorly.)
+
+ zero_blocks = []
+ nonzero_blocks = []
+ reference = '\0' * self.blocksize
+
+ f = self.simg_f
+ for s, e in remaining:
+ for b in range(s, e):
+ idx = bisect.bisect_right(self.offset_index, b) - 1
+ chunk_start, chunk_len, filepos, fill_data = self.offset_map[idx]
+ if filepos is not None:
+ filepos += (b-chunk_start) * self.blocksize
+ f.seek(filepos, os.SEEK_SET)
+ data = f.read(self.blocksize)
+ else:
+ if fill_data == reference[:4]: # fill with all zeros
+ data = reference
+ else:
+ data = None
+
+ if data == reference:
+ zero_blocks.append(b)
+ zero_blocks.append(b+1)
+ else:
+ nonzero_blocks.append(b)
+ nonzero_blocks.append(b+1)
+
+ out["__ZERO"] = RangeSet(data=zero_blocks)
+ out["__NONZERO"] = RangeSet(data=nonzero_blocks)
+
+ def ResetFileMap(self):
+ """Throw away the file map and treat the entire image as
+ undifferentiated data."""
+ self.file_map = {"__DATA": self.care_map}
diff --git a/reverses/de-oat/README.md b/reverses/de-oat/README.md
new file mode 100644
index 0000000..441e95b
--- /dev/null
+++ b/reverses/de-oat/README.md
@@ -0,0 +1,53 @@
+This is for Android 5.0+.
+
+De-oat the ota package zip, of which apks and jars are oat format(Android ELF on ART).
+
+### Github
+
+
+### About
+This is forked from https://code.google.com/p/smali/
+The additional modification is to support convert oat file to dex.
+
+Function concept:
+boot.oat -> extract optimized boot class dex files -> deoptimize to dex files
+app.odex(oat) -> reference boot dex files to deoptimize
+
+Download latest version:
+https://github.com/testwhat/SmaliEx/blob/master/smaliex-bin/oat2dex.jar?raw=true
+
+Usage:
+Deoptimize boot classes (The output will be in "odex" and "dex" folders):
+ java -jar oat2dex.jar boot <boot.oat file>
+Deoptimize application:
+ java -jar oat2dex.jar <app.odex> <boot-class-folder output from above>
+Get odex from oat:
+ java -jar oat2dex.jar odex <oat file>
+Get odex smali (with optimized opcode) from oat/odex:
+ java -jar oat2dex.jar smali <oat/odex file>
+Deodex /system/framework/ from device (need to connect with adb):
+ java -jar oat2dex.jar devfw
+
+Used by:
+[JoelDroid](http://forum.xda-developers.com/android/software-hacking/script-app-joeldroid-lollipop-batch-t2980857)
+[SVADeodexerForArt](http://forum.xda-developers.com/galaxy-s5/general/tool-deodex-tool-android-l-t2972025)
+[PUMa - Patch Utility Manager](http://forum.xda-developers.com/showthread.php?t=1434946)
+
+Original Readme
+### About
+
+smali/baksmali is an assembler/disassembler for the dex format used by dalvik, Android's Java VM implementation. The syntax is loosely based on Jasmin's/dedexer's syntax, and supports the full functionality of the dex format (annotations, debug info, line info, etc.)
+
+Downloads are at https://bitbucket.org/JesusFreke/smali/downloads. If you are interested in submitting a patch, feel free to send me a pull request here.
+
+#### Support
+- [github Issue tracker](https://github.com/JesusFreke/smali/issues) - For any bugs/issues/feature requests
+- [#smali on freenode](http://webchat.freenode.net/?channels=smali) - Free free to drop by and ask a question. Don't expect an instant response, but if you hang around someone will respond.
+
+
+#### Some useful links for getting started with smali
+
+- [Official dex bytecode reference](https://source.android.com/devices/tech/dalvik/dalvik-bytecode.html)
+- [Registers wiki page](https://github.com/JesusFreke/smali/wiki/Registers)
+- [Types, Methods and Fields wiki page](https://github.com/JesusFreke/smali/wiki/TypesMethodsAndFields)
+- [Official dex format reference](https://source.android.com/devices/tech/dalvik/dex-format.html)
diff --git a/reverses/de-oat/oat2dex.jar b/reverses/de-oat/oat2dex.jar
new file mode 100644
index 0000000..447e08e
Binary files /dev/null and b/reverses/de-oat/oat2dex.jar differ
diff --git a/reverses/de-oat/oat2dex.sh b/reverses/de-oat/oat2dex.sh
new file mode 100755
index 0000000..36e10d7
--- /dev/null
+++ b/reverses/de-oat/oat2dex.sh
@@ -0,0 +1,72 @@
+#!/bin/bash
+#
+# Copyright 2015 Coron
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+prog="$0"
+while [ -h "${prog}" ]; do
+ newProg=`/bin/ls -ld "${prog}"`
+ #echo ${newProg}
+
+
+ newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
+ if expr "x${newProg}" : 'x/' >/dev/null; then
+ prog="${newProg}"
+ else
+ progdir=`dirname "${prog}"`
+ prog="${progdir}/${newProg}"
+ fi
+done
+oldwd=`pwd`
+progdir=`dirname "${prog}"`
+cd "${progdir}"
+progdir=`pwd`
+prog="${progdir}"/`basename "${prog}"`
+cd "${oldwd}"
+
+
+jarfile=oat2dex.jar
+libdir="$progdir"
+if [ ! -r "$libdir/$jarfile" ]
+then
+ echo `basename "$prog"`": can't find $jarfile"
+ exit 1
+fi
+
+javaOpts=""
+
+# If you want DX to have more memory when executing, uncomment the following
+# line and adjust the value accordingly. Use "java -X" for a list of options
+# you can pass here.
+#
+javaOpts="-Xmx512M"
+
+# Alternatively, this will extract any parameter "-Jxxx" from the command line
+# and pass them to Java (instead of to dx). This makes it possible for you to
+# add a command-line parameter such as "-JXmx256M" in your ant scripts, for
+# example.
+while expr "x$1" : 'x-J' >/dev/null; do
+ opt=`expr "$1" : '-J\(.*\)'`
+ javaOpts="${javaOpts} -${opt}"
+ shift
+done
+
+if [ "$OSTYPE" = "cygwin" ] ; then
+ jarpath=`cygpath -w "$libdir/$jarfile"`
+else
+ jarpath="$libdir/$jarfile"
+fi
+
+exec java $javaOpts -jar "$jarpath" "$@"
diff --git a/reverses/de-odex/README.md b/reverses/de-odex/README.md
new file mode 100644
index 0000000..e4aa5bd
--- /dev/null
+++ b/reverses/de-odex/README.md
@@ -0,0 +1,26 @@
+This is for Android 2.3~4.4.
+
+De-odex the ota package zip, of which apks and jars are odexed (Optimized dex on DALVIKVM).
+
+### Github
+
+
+### About
+
+smali/baksmali is an assembler/disassembler for the dex format used by dalvik, Android's Java VM implementation. The syntax is loosely based on Jasmin's/dedexer's syntax, and supports the full functionality of the dex format (annotations, debug info, line info, etc.)
+
+Downloads are at https://bitbucket.org/JesusFreke/smali/downloads. If you are interested in submitting a patch, feel free to send me a pull request here.
+
+See [the wiki](https://github.com/JesusFreke/smali/wiki) for more info/news/release notes/etc.
+
+#### Support
+- [github Issue tracker](https://github.com/JesusFreke/smali/issues) - For any bugs/issues/feature requests
+- [#smali on freenode](http://webchat.freenode.net/?channels=smali) - Free free to drop by and ask a question. Don't expect an instant response, but if you hang around someone will respond.
+
+
+#### Some useful links for getting started with smali
+
+- [Official dex bytecode reference](https://source.android.com/devices/tech/dalvik/dalvik-bytecode.html)
+- [Registers wiki page](https://github.com/JesusFreke/smali/wiki/Registers)
+- [Types, Methods and Fields wiki page](https://github.com/JesusFreke/smali/wiki/TypesMethodsAndFields)
+- [Official dex format reference](https://source.android.com/devices/tech/dalvik/dex-format.html)
diff --git a/reverses/de-odex/baksmali.jar b/reverses/de-odex/baksmali.jar
new file mode 100644
index 0000000..c453a9b
Binary files /dev/null and b/reverses/de-odex/baksmali.jar differ
diff --git a/reverses/de-odex/baksmali.sh b/reverses/de-odex/baksmali.sh
new file mode 100755
index 0000000..f8d0480
--- /dev/null
+++ b/reverses/de-odex/baksmali.sh
@@ -0,0 +1,77 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This script is a wrapper around baksmali.jar, so you can simply call
+# "baksmali", instead of java -jar baksmali.jar. It is heavily based on
+# the "dx" script from the Android SDK
+
+# Set up prog to be the path of this script, including following symlinks,
+# and set up progdir to be the fully-qualified pathname of its directory.
+prog="$0"
+while [ -h "${prog}" ]; do
+ newProg=`/bin/ls -ld "${prog}"`
+ #echo ${newProg}
+
+
+ newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
+ if expr "x${newProg}" : 'x/' >/dev/null; then
+ prog="${newProg}"
+ else
+ progdir=`dirname "${prog}"`
+ prog="${progdir}/${newProg}"
+ fi
+done
+oldwd=`pwd`
+progdir=`dirname "${prog}"`
+cd "${progdir}"
+progdir=`pwd`
+prog="${progdir}"/`basename "${prog}"`
+cd "${oldwd}"
+
+
+jarfile=baksmali.jar
+libdir="$progdir"
+if [ ! -r "$libdir/$jarfile" ]
+then
+ echo `basename "$prog"`": can't find $jarfile"
+ exit 1
+fi
+
+javaOpts=""
+
+# If you want DX to have more memory when executing, uncomment the following
+# line and adjust the value accordingly. Use "java -X" for a list of options
+# you can pass here.
+#
+javaOpts="-Xmx256M"
+
+# Alternatively, this will extract any parameter "-Jxxx" from the command line
+# and pass them to Java (instead of to dx). This makes it possible for you to
+# add a command-line parameter such as "-JXmx256M" in your ant scripts, for
+# example.
+while expr "x$1" : 'x-J' >/dev/null; do
+ opt=`expr "$1" : '-J\(.*\)'`
+ javaOpts="${javaOpts} -${opt}"
+ shift
+done
+
+if [ "$OSTYPE" = "cygwin" ] ; then
+ jarpath=`cygpath -w "$libdir/$jarfile"`
+else
+ jarpath="$libdir/$jarfile"
+fi
+
+exec java $javaOpts -jar "$jarpath" "$@"
diff --git a/reverses/de-odex/deodex.sh b/reverses/de-odex/deodex.sh
new file mode 100755
index 0000000..30d3999
--- /dev/null
+++ b/reverses/de-odex/deodex.sh
@@ -0,0 +1,228 @@
+#!/bin/bash
+
+
+CUR_DIR=$(dirname $0)
+SMALI=$CUR_DIR/smali.sh
+BAKSMALI=$CUR_DIR/baksmali.sh
+
+function deodex_one_file() {
+ local outdir=`mktemp -d /tmp/baksamli.out.XXXXX`
+ local class_dex='classes.dex'
+
+ if [ "$1" = '-a' ]
+ then
+ apilevel=$2
+ classpath=$3
+ file=$4
+ tofile=${file/odex/$5}
+ echo ">>> processing $tofile"
+ $BAKSMALI -a $apilevel -c $classpath -d framework -I -x $file -o $outdir/out || return 2
+ else
+ classpath=$1
+ file=$2
+ tofile=${file/odex/$3}
+ echo "<<< processing $tofile"
+ $BAKSMALI -c $classpath -d framework -I -x $file -o $outdir/out || return 2
+ fi
+
+ tofileFullPath=$PWD/$tofile
+ pre_dir=$PWD
+ cd $outdir
+ $SMALI out -o "$class_dex" || return 2
+
+ if [ -f $tofileFullPath ];then
+ jar uf $tofileFullPath "$class_dex" || return 2
+ else
+ jar cf $tofileFullPath "$class_dex" || return 2
+ fi
+
+ cd "$pre_dir"
+ rm -rf $outdir
+ rm $file
+ zipalign 4 $tofile $tofile.aligned || return 2
+ mv $tofile.aligned $tofile
+ echo ">>> deodex $tofile done"
+ return 0
+}
+
+
+function deodex_framework()
+{
+ local stop=`mktemp`
+ ls framework/*.odex > /dev/null
+ if [ $? -eq 0 ]
+ then
+ for file in framework/*.odex
+ do
+ if [ x`cat $stop` != "x" ]; then
+ # see help.xml for ERR_DEODEX_FAILED 163
+ exit 163
+ fi
+ read -u6
+ {
+ deodex_out=`mktemp /tmp/deodex_out.XXXXX`
+ if [ ! -z $apilevel ]
+ then
+ deodex_one_file -a $apilevel $classpath $file jar 2>&1 | tee $deodex_out
+ else
+ deodex_one_file $classpath $file jar 2>&1 | tee $deodex_out
+ fi
+ deodex_result="$?"
+ hasError=`egrep "Error|Exception" -i $deodex_out`
+ rm $deodex_out -rf
+ if [ $deodex_result != "0" -o "x$hasError" != "x" ];then
+ echo ">>> ERROR: deodex $file fail"
+ echo "1" > $stop
+ fi
+ echo >&6
+ } &
+ done
+ fi
+ if [ x`cat $stop` != "x" ]; then
+ # see help.xml for ERR_DEODEX_FAILED 163
+ exit 163
+ fi
+ echo ">>> wait for framework deodex done!"
+ wait
+ echo ">>> framework deodex done! $?"
+}
+
+
+function deodex_app()
+{
+ app_dir=$1
+ ls $app_dir/*.odex > /dev/null
+ if [ $? -eq 0 ]
+ then
+ for file in $app_dir/*.odex
+ do
+ read -u6
+ {
+ if [ ! -z $apilevel ]
+ then
+ deodex_one_file -a $apilevel $classpath $file apk || exit $?
+ else
+ deodex_one_file $classpath $file apk || exit $?
+ fi
+ echo >&6
+ } &
+ done
+ fi
+ echo ">>> wait for app deodex done!"
+ wait
+ echo ">>> app deodex done! $?"
+}
+
+
+#usage
+if [ "$1" = "--help" -o "$#" -lt "1" ];then
+ echo "usage: ./deodex.sh [-a APILevel] absolute_path_to_ota_zip_file -jn"
+ echo " -a specify APILevel, default Level is 15"
+ echo " -jn n is the thread num"
+ echo " -framework only deodex framework"
+ exit 0
+fi
+
+if [ ! -x $BAKSMALI -o ! -x $SMALI ];then
+ echo "Error: Can not find baksmali/smali"
+ exit -1
+fi
+
+if [ "$1" = "-a" ]
+then
+ apilevel=$2
+ stockzip=$3
+ threadnum=${4#-j}
+elif [ "$1" = "-framework" ]
+then
+ stockzip=$2
+ threadnum=${3#-j}
+else
+ stockzip=$1
+ threadnum=${2#-j}
+fi
+
+if [ "x$threadnum" = "x" ];then
+ threadnum=4
+fi
+
+for arg in $*
+do
+ [ "$arg" = "-framework" ] && onlyframework=true && break
+done
+
+zippath=$(cd "$(dirname "$stockzip")"; pwd)
+zipname=$(basename "$stockzip")
+deodexfile="$zippath/$zipname.deodex.zip"
+
+tempdir=`mktemp -d /tmp/tempdir.XXXXX`
+#echo "temp dir: $tempdir"
+echo "unzip $stockzip to $tempdir"
+unzip -q -o $stockzip -d $tempdir
+
+if [ -d $tempdir/system ]
+then
+ cd $tempdir/system
+elif [ -d $tempdir/SYSTEM ]
+then
+ cd $tempdir/SYSTEM
+else
+ echo "can't find system or SYSTEM dir in $tempdir"
+ exit -1
+fi
+
+if [ ! -f framework/core.odex ];then
+ cd -
+ rm -rf $tempdir
+ echo ">>> $stockzip isnot odex! "
+ cp $stockzip $deodexfile
+ exit 0
+fi
+
+tmp_fifofile="/tmp/$$.fifo"
+mkfifo "$tmp_fifofile"
+exec 6<>"$tmp_fifofile"
+rm $tmp_fifofile
+
+for ((i=0;i<$threadnum;i++));do
+ echo
+done >&6
+
+ls framework/core.odex > /dev/null
+if [ $? -eq 0 ]
+then
+ #classpath="core.jar:ext.jar:framework.jar:android.policy.jar:services.jar"
+ if [ $1 = '-a' ]
+ then
+ deodex_one_file -a $apilevel "" framework/core.odex jar
+ else
+ deodex_one_file "" framework/core.odex jar
+ fi
+fi
+
+for f in framework/*.jar
+do
+ # Ignore the jar not having odex
+ odexf=`echo $f | sed {s/\.jar$/\.odex/g}`
+ [ -e $odexf ] && classpath=$classpath:$f
+done
+
+#echo "classpath=$classpath"
+
+# Only deodex framework
+if [ $onlyframework ]
+then
+ deodex_framework
+else
+ deodex_framework
+ deodex_app app
+ deodex_app priv-app
+fi
+
+cd $tempdir
+#echo "zip tmp_target_files"
+zip -q -r -y "tmp_target_files" *
+mv "tmp_target_files.zip" $deodexfile
+cd - > /dev/null
+rm -rf $tempdir
+echo "deodex done. deodex zip: $stockzip"
diff --git a/reverses/de-odex/smali.jar b/reverses/de-odex/smali.jar
new file mode 100644
index 0000000..8b246a8
Binary files /dev/null and b/reverses/de-odex/smali.jar differ
diff --git a/reverses/de-odex/smali.sh b/reverses/de-odex/smali.sh
new file mode 100755
index 0000000..a6619f8
--- /dev/null
+++ b/reverses/de-odex/smali.sh
@@ -0,0 +1,77 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This script is a wrapper for smali.jar, so you can simply call "smali",
+# instead of java -jar smali.jar. It is heavily based on the "dx" script
+# from the Android SDK
+
+# Set up prog to be the path of this script, including following symlinks,
+# and set up progdir to be the fully-qualified pathname of its directory.
+prog="$0"
+while [ -h "${prog}" ]; do
+ newProg=`/bin/ls -ld "${prog}"`
+ #echo ${newProg}
+
+
+ newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
+ if expr "x${newProg}" : 'x/' >/dev/null; then
+ prog="${newProg}"
+ else
+ progdir=`dirname "${prog}"`
+ prog="${progdir}/${newProg}"
+ fi
+done
+oldwd=`pwd`
+progdir=`dirname "${prog}"`
+cd "${progdir}"
+progdir=`pwd`
+prog="${progdir}"/`basename "${prog}"`
+cd "${oldwd}"
+
+
+jarfile=smali.jar
+libdir="$progdir"
+if [ ! -r "$libdir/$jarfile" ]
+then
+ echo `basename "$prog"`": can't find $jarfile"
+ exit 1
+fi
+
+javaOpts=""
+
+# If you want DX to have more memory when executing, uncomment the following
+# line and adjust the value accordingly. Use "java -X" for a list of options
+# you can pass here.
+#
+javaOpts="-Xmx512M"
+
+# Alternatively, this will extract any parameter "-Jxxx" from the command line
+# and pass them to Java (instead of to dx). This makes it possible for you to
+# add a command-line parameter such as "-JXmx256M" in your ant scripts, for
+# example.
+while expr "x$1" : 'x-J' >/dev/null; do
+ opt=`expr "$1" : '-J\(.*\)'`
+ javaOpts="${javaOpts} -${opt}"
+ shift
+done
+
+if [ "$OSTYPE" = "cygwin" ] ; then
+ jarpath=`cygpath -w "$libdir/$jarfile"`
+else
+ jarpath="$libdir/$jarfile"
+fi
+
+exec java $javaOpts -jar "$jarpath" "$@"
diff --git a/reverses/decode_all.sh b/reverses/decode_all.sh
new file mode 100755
index 0000000..141aac0
--- /dev/null
+++ b/reverses/decode_all.sh
@@ -0,0 +1,131 @@
+#!/bin/bash
+
+#####################################################
+#
+# use to decode all of the apk and jar in the system
+# note: unzip the zip first
+#
+#####################################################
+
+CUR_DIR=$(dirname $0)
+APKTOOL=$CUR_DIR/apktool
+THREAD_NUM=4
+
+IN_DIR=""
+OUT_DIR=""
+
+# echo the usage of decode_all
+function usage
+{
+ echo "usage: decode_all [OPTIONS] SYSTEM_DIR [OUT_DIR]"
+ echo " OPTIONS: -j[0-9]* jobs number"
+ echo " SYSTEM_DIR: the apk/jar's directory which will be decoded"
+ echo " OUT_DIR: output directory for decoded, current directory is default."
+ echo "eg: decode_all system/framework ."
+}
+
+# setup the parameters
+function setParam
+{
+ if [ $# -eq 0 ]
+ then
+ usage
+ exit 1
+ fi
+
+ set -- `getopt "j:" "$@"`
+
+ while :
+ do
+ case "$1" in
+ -j) shift; THREAD_NUM=$1 ;;
+ --) break ;;
+ esac
+ shift
+ done
+ shift
+
+ IN_DIR=$1
+
+ if [ $# -eq 1 ]
+ then
+ OUT_DIR="."
+ else
+ OUT_DIR=$2
+ if [ ! -d $OUT_DIR ]
+ then
+ mkdir -p $OUT_DIR
+ fi
+ fi
+}
+
+# init multi build jobs through fifo
+function initMultiJobs
+{
+ tmp_fifofile="/tmp/$$.fifo"
+
+ mkfifo "$tmp_fifofile"
+ exec 6<>"$tmp_fifofile"
+ rm $tmp_fifofile
+
+ for ((i=0;i<$THREAD_NUM;i++));do
+ echo
+ done >&6
+}
+
+# decode all apks
+function decodeApks
+{
+ for line in `find $IN_DIR -name "*.apk"`
+ do
+ read -u6
+ {
+ echo ">>> begin decode $line"
+ out_file=${line:${#IN_DIR}:${#line}}
+ let "len=${#out_file}-4"
+ out_file=${out_file:0:$len}
+ $APKTOOL d $line -o $OUT_DIR"/"$out_file
+ echo "<<< decode $line done"
+ echo >&6
+ } &
+ done
+}
+
+# decode all jars
+function decodeJars
+{
+ for line in `find $IN_DIR -name "*.jar"`
+ do
+ read -u6
+ {
+ echo ">>> begin decode $line"
+ out_file=${line:${#IN_DIR}:${#line}}
+ let "len=${#out_file}"
+ #out_file=${line:${#file_path}:${#line}}
+ out_file=${out_file:0:$len}
+ $APKTOOL d $line -o $OUT_DIR"/"$out_file".out"
+ echo "<<< decode $line done"
+ echo >&6
+ } &
+ done
+}
+
+# wait for the end
+function waitToEnd
+{
+ # wait it for done
+ for ((i=0;i<$THREAD_NUM;i++));do
+ read -u6
+ done
+}
+
+function main
+{
+ setParam $@
+ initMultiJobs
+ decodeApks
+ decodeJars
+ waitToEnd
+}
+
+main $@
diff --git a/reverses/deoat.py b/reverses/deoat.py
new file mode 100755
index 0000000..5f20c83
--- /dev/null
+++ b/reverses/deoat.py
@@ -0,0 +1,496 @@
+#!/usr/bin/python
+
+# Copyright 2015 Coron
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+"""
+Convert the OAT format on ART to DEX format on DALVIKVM.
+ Usage: deoat.py [OPTIONS] []
+ OPTIONS:
+ --app, -a: only de-oat the apk in system.
+ --framework, -f: only de-oat the jar in system.
+"""
+
+# Refer to the SuperR's Kitchen for the deodex Lollipop ROMs
+
+__author__ = 'duanqz@gmail.com'
+
+import os
+import commands
+import re
+import shutil
+import threading
+
+from common import Utils, Log
+
+# Global
+TAG="reverse-deoat"
+OPTIONS = None
+
+class OatZip:
+ """ Model of OAT ZIP file
+ """
+
+ OAT2DEX = os.path.join(os.path.dirname(__file__), "de-oat", "oat2dex.sh")
+
+ def __init__(self, unzipRoot):
+ self.mRoot = unzipRoot
+
+ self.mFrwDir = os.path.join(self.mRoot, "system/framework")
+ self.mAppDir = os.path.join(self.mRoot, "system/app")
+ self.mPrivAppDir = os.path.join(self.mRoot, "system/priv-app")
+ self.mAllAppDirList = [self.mFrwDir, self.mAppDir, self.mPrivAppDir]
+ self.mSystemDir = os.path.join(self.mRoot, "system")
+
+ self.findArch()
+
+ self.mBootOAT = os.path.join(self.mFrwDir, self.arch, "boot.oat")
+ if os.path.exists(self.mBootOAT):
+ Log.i(TAG, "mBootOAT : " + self.mBootOAT)
+ else:
+ self.mBootOAT = None
+ Log.i(TAG, "boot.oat not found!")
+
+ @staticmethod
+ def testArch(frwDir, arch):
+ """ Test whether arch exists
+ """
+ bootOATPath = os.path.join(frwDir, arch, "boot.oat")
+ Log.i(TAG, "testArch : " + bootOATPath)
+ if os.path.exists(bootOATPath):
+ return True
+ return False
+
+
+ def findArch(self):
+ """ Find arch and arch2
+ """
+ self.arch = ""
+ self.arch2 = ""
+
+ if OatZip.testArch(self.mFrwDir, "arm64"):
+ self.arch = "arm64"
+ if OatZip.testArch(self.mFrwDir, "arm"):
+ self.arch2 = "arm"
+ elif OatZip.testArch(self.mFrwDir, "x86_64"):
+ self.arch = "x86_64"
+ if OatZip.testArch(self.mFrwDir, "x86"):
+ self.arch2="x86"
+ elif OatZip.testArch(self.mFrwDir, "arm"):
+ self.arch = "arm"
+ elif OatZip.testArch(self.mFrwDir, "x86"):
+ self.arch = "x86"
+ else:
+ Log.d(TAG, "unknow arch")
+
+
+ def findBootOAT(self):
+ """ Find the absolute path of boot.oat
+ In Android 5.0+, all the jars of BOOTCLASSPATH are packaged into boot.oat
+ """
+ bootOATPath = os.path.join(self.mFrwDir, "arm64/boot.oat")
+ if os.path.exists(bootOATPath):
+ return bootOATPath
+
+ bootOATPath = os.path.join(self.mFrwDir, "arm/boot.oat")
+ if os.path.exists(bootOATPath):
+ return bootOATPath
+
+ bootOATPath = os.path.join(self.mFrwDir, "x86_64/boot.oat")
+ if os.path.exists(bootOATPath):
+ return bootOATPath
+
+ bootOATPath = os.path.join(self.mFrwDir, "x86/boot.oat")
+ if os.path.exists(bootOATPath):
+ return bootOATPath
+
+ bootOATPath = None
+ cmd = "find %s -name boot.oat" % (commands.mkarg(self.mFrwDir))
+ (sts, text) = commands.getstatusoutput(cmd)
+ try:
+ if sts == 0:
+ text = text.split("\n")[0]
+ if len(text) > 0:
+ return text
+ except:
+ bootOATPath = None
+
+ return bootOATPath
+
+
+ def deoat(self):
+ """ De-oat the OTA package.
+ """
+
+ if self.mBootOAT == None:
+ Log.i(TAG, "deoat(): boot.oat not found in %s, nothing need deoat" % self.mRoot)
+ return self
+
+ # Phase 1: de-oat boot.oat
+ OatZip.deoatBootOAT(os.path.join(self.mFrwDir, self.arch, "boot.oat"))
+ if self.arch2.strip():
+ OatZip.deoatBootOAT(os.path.join(self.mFrwDir, self.arch2, "boot.oat"))
+
+ # Phase 2: de-oat all the other oat files, of which suffix is odex.
+ # [Android 5.0]: All the oat jars are located in the same folder with boot.oat
+
+ # Phase 3: de-oat app
+ # de-oat app
+ threadApp = threading.Thread(target = OatZip.deoatAppWithArch, args = (self.mAppDir, self.mFrwDir, self.arch, self.arch2))
+ threadApp.start()
+
+ threadPrivApp = threading.Thread(target = OatZip.deoatAppWithArch, args = (self.mPrivAppDir, self.mFrwDir, self.arch, self.arch2))
+ threadPrivApp.start()
+
+ threadApp.join()
+ threadPrivApp.join()
+
+ # Phase 4: de-oat framework
+ # de-oat framework
+ OatZip.deoatFrwWithArch(self.mFrwDir, self.arch)
+
+ # de-oat framework/oat/$arch
+ OatZip.deoatFrwOatWithArch(self.mFrwDir, self.arch)
+
+ return self
+
+
+ def rebuild(self):
+ """ Rebuild the deoated zip
+ """
+
+ if self.mBootOAT == None:
+ Log.i(TAG, "rebuild(): boot.oat not found, nothing need rebuild")
+ return
+
+ # repackage app
+ OatZip.repackageAppWithArch(self.mAppDir, self.arch)
+ if self.arch2.strip():
+ OatZip.repackageAppWithArch(self.mAppDir, self.arch2)
+
+ OatZip.repackageAppWithArch(self.mPrivAppDir, self.arch)
+ if self.arch2.strip():
+ OatZip.repackageAppWithArch(self.mPrivAppDir, self.arch2)
+
+ # repackage framework
+ #$framedir/$arch
+ OatZip.repackageFrwWithArch(self.mFrwDir, os.path.join(self.mFrwDir, self.arch))
+
+ #$framedir/$arch/dex
+ if os.path.exists(os.path.join(self.mFrwDir, self.arch, "dex")):
+ OatZip.repackageFrwWithArch(self.mFrwDir, os.path.join(self.mFrwDir, self.arch, "dex"))
+
+ #$framedir/oat/$arch
+ if os.path.exists(os.path.join(self.mFrwDir, "oat", self.arch)):
+ OatZip.repackageFrwWithArch(self.mFrwDir, os.path.join(self.mFrwDir, "oat", self.arch))
+
+ # deal with additional apks not in system/framework system/app system/priv-app
+ OatZip.dealWithAdditionalApks(self.mSystemDir, self.mFrwDir, self.arch, self.arch2, self.mAllAppDirList)
+
+ # Remove arch and arch2 dir
+ os.chdir(self.mRoot)
+ shutil.rmtree(os.path.join(self.mFrwDir, self.arch))
+ if self.arch2.strip():
+ shutil.rmtree(os.path.join(self.mFrwDir, self.arch2))
+ if os.path.exists(os.path.join(self.mFrwDir, "oat")) :
+ shutil.rmtree(os.path.join(self.mFrwDir, "oat"))
+
+
+ @staticmethod
+ def deoatBootOAT(bootOAT):
+ """ De-oat boot.oat
+ """
+ bootClassFolder = os.path.dirname(bootOAT)
+ bootClassFolderDex = os.path.join(bootClassFolder, "dex")
+ bootClassFolderOdex = os.path.join(bootClassFolder, "odex")
+
+ if os.path.exists(bootClassFolderDex):
+ Log.d(TAG, "Delete the already exists %s" %bootClassFolderDex)
+ shutil.rmtree(bootClassFolderDex)
+ if os.path.exists(bootClassFolderOdex):
+ Log.d(TAG, "Delete the already exists %s" %bootClassFolderOdex)
+ shutil.rmtree(bootClassFolderOdex)
+
+ Log.i(TAG, "De-oat %s" % bootOAT)
+ Utils.runWithOutput([OatZip.OAT2DEX, "boot", bootOAT])
+
+ @staticmethod
+ def packageDexToAppWithArch(apkFile, arch):
+
+ # Keep the old directory, we will change back after some operations.
+ oldDir = os.path.abspath(os.curdir)
+
+ apkPath = os.path.dirname(apkFile)
+ appName = os.path.basename(apkFile)
+ app = appName[0:-4]
+
+ archPath = os.path.join(apkPath, "oat", arch)
+
+ # chagnge to arch path
+ os.chdir(archPath)
+
+ Log.d(TAG, "Repackage %s" %(apkFile))
+
+ dexFile = os.path.join(archPath, app + ".dex")
+
+ # mv $appdir/$app/$arch/$app.dex $appdir/$app/$arch/classes.dex
+ if os.path.exists(dexFile):
+ shutil.move(dexFile, os.path.join(archPath, "classes.dex"))
+ Utils.runWithOutput(["jar", "uf", apkFile, "classes.dex"])
+ else:
+ Log.d(TAG, "Repackage ERROR %s" %(apkFile))
+
+ dexFile = os.path.join(archPath, app + "-classes2.dex")
+ # if [[ -f "$appdir/$app/$arch/$app-classes2.dex" ]]; then
+ # mv $appdir/$app/$arch/$app-classes2.dex $appdir/$app/$arch/classes2.dex
+ if os.path.exists(dexFile):
+ shutil.move(dexFile, os.path.join(archPath, "classes2.dex"))
+ Utils.runWithOutput(["jar", "uf", apkFile, "classes2.dex"])
+
+ dexFile = os.path.join(archPath, app + "-classes3.dex")
+ # if [[ -f "$appdir/$app/$arch/$app-classes3.dex" ]]; then
+ # mv $appdir/$app/$arch/$app-classes3.dex $appdir/$app/$arch/classes3.dex
+ if os.path.exists(dexFile):
+ shutil.move(dexFile, os.path.join(archPath, "classes3.dex"))
+ Utils.runWithOutput(["jar", "uf", apkFile, "classes3.dex"])
+
+ os.chdir(oldDir)
+
+ @staticmethod
+ def deoatFrwWithArch(frwDir, arch):
+ """ De-oat framework
+ """
+
+ if not OPTIONS.formatFrw: return
+
+ Log.i(TAG, "De-oat files of oat-format in %s" % frwDir)
+ archDir = os.path.join(frwDir, arch)
+ odexDir = os.path.join(archDir, "odex")
+ for item in os.listdir(archDir):
+ if item.endswith(".odex"):
+ jarFile = os.path.join(frwDir, item[0:-5] + ".jar")
+ if not OatZip.isDeodexed(jarFile):
+ odexFile = os.path.join(archDir, item)
+ Utils.runWithOutput([OatZip.OAT2DEX, odexFile, odexDir])
+
+ @staticmethod
+ def deoatFrwOatWithArch(frwDir, arch):
+ """ De-oat framework oat
+ """
+
+ if not OPTIONS.formatFrw: return
+
+ Log.i(TAG, "De-oat files of oat-format in %s/oat" % frwDir)
+ archDir = os.path.join(frwDir, arch)
+ odexDir = os.path.join(archDir, "odex")
+ oatDir = os.path.join(frwDir, "oat", arch)
+ for item in os.listdir(oatDir):
+ if item.endswith(".odex"):
+ jarFile = os.path.join(frwDir, item[0:-5] + ".jar")
+ if not OatZip.isDeodexed(jarFile):
+ odexFile = os.path.join(oatDir, item)
+ Utils.runWithOutput([OatZip.OAT2DEX, odexFile, odexDir])
+
+ @staticmethod
+ def isDeodexed(apkFile):
+ """ Wheather apk/jar is deodexed
+ """
+ cmd = "jar tf " + apkFile + "| grep classes.dex"
+ (sts, text) = commands.getstatusoutput(cmd)
+ if sts == 0 and text.find('classes.dex') != -1:
+ return True
+ return False
+
+
+ @staticmethod
+ def deoatAppWithArch(appsDir, frwDir, arch, arch2):
+ """ De-oat app
+ """
+
+ if OPTIONS.formatApp == False: return
+
+ Log.i(TAG, "De-oat files of oat-format in %s" %(appsDir))
+
+ bootClassFolderArch = os.path.join(frwDir, arch, "odex")
+ bootClassFolderArch2 = os.path.join(frwDir, arch2, "odex")
+
+ #for app in $( ls $appdir ); do
+ for app in os.listdir(appsDir):
+ appPath = os.path.join(appsDir, app)
+ apkFile = os.path.join(appPath, app + ".apk")
+
+ archPath = os.path.join(appPath, "oat", arch)
+ #if [[ -d "$appdir/$app/$arch" ]];
+ if os.path.exists(archPath):
+ odexFile = os.path.join(archPath, app + ".odex")
+
+ #java -Xmx512m -jar $oat2dex $appdir/$app/$arch/$app.odex $framedir/$arch/odex
+ Utils.runWithOutput([OatZip.OAT2DEX, odexFile, bootClassFolderArch])
+ else:
+ # if exists arch2
+ if arch2.strip():
+ arch2Path = os.path.join(appPath, "oat", arch2)
+ if os.path.exists(arch2Path):
+ odexFile2 = os.path.join(arch2Path, app + ".odex")
+ Utils.runWithOutput([OatZip.OAT2DEX, odexFile2, bootClassFolderArch2])
+
+
+ @staticmethod
+ def repackageFrwWithArch(frwDir, dexFolder):
+ """ Repackage the classes.dex into jar of frwDir.
+ """
+
+ if OPTIONS.formatFrw == False : return
+
+ # Keep the old directory, we will change back after some operations.
+ oldDir = os.path.abspath(os.curdir)
+
+ Log.i(TAG, "Repackage JARs of %s - %s" %(frwDir,dexFolder))
+
+ os.chdir(dexFolder)
+ for dexFile in os.listdir(dexFolder):
+ if dexFile.endswith(".dex") and dexFile.find("classes") == -1:
+ appName = dexFile[0:-4]
+ jarFile = os.path.join(frwDir, appName + ".apk")
+ if not os.path.exists(jarFile):
+ jarFile = jarFile[0:-4] + ".jar"
+
+ if not os.path.exists(jarFile):
+ dexName = "classes.dex"
+ shutil.move(os.path.join(dexFolder, dexFile), os.path.join(dexFolder, dexName))
+ Utils.runWithOutput(["jar", "cf", jarFile, dexName])
+ os.remove(os.path.join(dexFolder, dexName))
+ continue
+
+ Log.d(TAG, "Repackage %s" %(jarFile))
+ if not OatZip.isDeodexed(jarFile):
+ # Put the dex and framework's jar in the same folder, and jar into the jarFile
+ dexName = "classes.dex"
+ shutil.move(os.path.join(dexFolder, dexFile), os.path.join(dexFolder, dexName))
+ Utils.runWithOutput(["jar", "uf", jarFile, dexName])
+ os.remove(os.path.join(dexFolder, dexName))
+
+ dexName = "classes2.dex"
+ dexFile = appName + "-" + dexName
+ if os.path.exists(os.path.join(dexFolder, dexFile)):
+ shutil.move(os.path.join(dexFolder, dexFile), os.path.join(dexFolder, dexName))
+ Utils.runWithOutput(["jar", "uf", jarFile, dexName])
+ os.remove(os.path.join(dexFolder, dexName))
+
+ dexName = "classes3.dex"
+ dexFile = appName + "-" + dexName
+ if os.path.exists(os.path.join(dexFolder, dexFile)):
+ shutil.move(os.path.join(dexFolder, dexFile), os.path.join(dexFolder, dexName))
+ Utils.runWithOutput(["jar", "uf", jarFile, dexName])
+ os.remove(os.path.join(dexFolder, dexName))
+
+ os.chdir(oldDir)
+
+
+ @staticmethod
+ def repackageAppWithArch(appDir, arch):
+ """ Repackage the classes.dex into apk of appDir
+ """
+
+ if OPTIONS.formatApp == False: return
+
+ # Keep the old directory, we will change back after some operations.
+ oldDir = os.path.abspath(os.curdir)
+
+ Log.i(TAG, "Repackage APKs of %s" %(appDir))
+ for app in os.listdir(appDir):
+ apkPath = os.path.join(appDir, app)
+ apkFile = os.path.join(apkPath, app + ".apk")
+ archPath = os.path.join(apkPath, "oat", arch)
+ dexFile = os.path.join(archPath, app + ".dex")
+ if os.path.exists(archPath):
+ if not OatZip.isDeodexed(apkFile):
+ OatZip.packageDexToAppWithArch(apkFile, arch)
+
+ #rm -rf $appdir/$app/$arch
+ shutil.rmtree(archPath)
+
+ os.chdir(oldDir)
+
+ @staticmethod
+ def check_validate(apkFile, arch, arch2):
+ '''check whether is validate apk'''
+ return True
+
+ @staticmethod
+ def dealWithAdditionalApks(systemDir, frwDir, arch, arch2, allAppDirs):
+ ''' deal with additional apks '''
+
+ if OPTIONS.formatApp == False: return
+
+ # Keep the old directory, we will change back after some operations.
+ oldDir = os.path.abspath(os.curdir)
+
+ bootClassFolderArch = os.path.join(frwDir, arch, "odex")
+ bootClassFolderArch2 = os.path.join(frwDir, arch2, "odex")
+
+ for (dirpath, dirnames, filenames) in os.walk(systemDir):
+
+ # Exclude scanned directories
+ if dirpath in allAppDirs:
+ continue
+
+ dirnames = dirnames # no use, to avoid warning
+
+ for filename in filenames:
+ if filename.endswith(".apk") or filename.endswith(".jar"):
+ apkFile = os.path.join(dirpath, filename)
+ if not OatZip.check_validate(apkFile, arch, arch2):
+ continue
+
+ archDir = os.path.join(dirpath, "oat", arch)
+ #app name
+ app = filename[0:-4]
+ if os.path.exists(archDir):
+ if not OatZip.isDeodexed(apkFile):
+ odexFile = os.path.join(archDir, app + ".odex")
+ if os.path.exists(odexFile):
+ Utils.runWithOutput([OatZip.OAT2DEX, odexFile, bootClassFolderArch])
+
+ OatZip.packageDexToAppWithArch(apkFile, arch)
+ #rm -rf $appdir/$app/$arch
+ shutil.rmtree(archDir)
+
+ arch2Dir = os.path.join(dirpath, "oat", arch2)
+ if os.path.exists(arch2Dir):
+ if not OatZip.isDeodexed(apkFile):
+ odexFile = os.path.join(arch2Dir, app + ".odex")
+ if os.path.exists(odexFile):
+ Utils.runWithOutput([OatZip.OAT2DEX, odexFile, bootClassFolderArch2])
+
+ OatZip.packageDexToAppWithArch(apkFile, arch2)
+ #rm -rf $appdir/$app/$arch
+ shutil.rmtree(arch2Dir)
+
+ if os.path.exists(os.path.join(dirpath, "oat")) :
+ shutil.rmtree(os.path.join(dirpath, "oat"))
+
+
+def debug():
+
+ Log.DEBUG = True
+ root = "root directory the unziped files"
+
+ OatZip(root).deoat()
+ OatZip(root).rebuild()
+
+if __name__ == "__main__":
+
+ debug()
diff --git a/reverses/deodex.py b/reverses/deodex.py
new file mode 100755
index 0000000..23734a8
--- /dev/null
+++ b/reverses/deodex.py
@@ -0,0 +1,159 @@
+#!/usr/bin/python
+
+# Copyright 2015 Coron
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+"""
+Deodex
+TODO
+"""
+
+__author__ = 'duanqz@gmail.com'
+
+
+import os
+import tempfile
+import commands
+import shutil
+
+from common import Options, Log
+
+# Global
+TAG="reverse-deodex"
+OPTIONS = Options()
+
+class OdexZip:
+
+ CLASSPATH="core.jar:ext.jar:framework.jar:android.policy.jar:services.jar"
+
+ def __init__(self, unzipRoot):
+ self.mRoot = unzipRoot
+
+ self.mFrwDir = os.path.join(self.mRoot, "system/framework")
+ self.mAppDir = os.path.join(self.mRoot, "system/app")
+ self.mPrivAppDir = os.path.join(self.mRoot, "system/priv-app")
+
+ if OPTIONS.classpath == None:
+ OPTIONS.classpath = OdexZip.CLASSPATH
+
+
+ def deodex(self):
+ OdexZip.deodexFrw(self.mFrwDir)
+
+ #OdexZip.deodexApp(self.mAppDir)
+ #OdexZip.deodexApp(self.mPrivAppDir)
+
+
+ @staticmethod
+ def deodexFrw(odexJarDir):
+ """ De-odex framework
+ """
+
+ if OPTIONS.formatFrw == False: return
+
+ coreOdex = os.path.join(odexJarDir, "core.odex")
+ if os.path.exists(coreOdex):
+ Log.i(TAG, "De-odex core.odex")
+ deodexFile = os.path.join(odexJarDir, "core.jar")
+ OdexZip.deodexOneFile(coreOdex, deodexFile)
+
+ Log.i(TAG, "De-odex files of odex-format in %s" % odexJarDir)
+ for item in os.listdir(odexJarDir):
+ if item.endswith(".odex"):
+ odexJar = os.path.join(odexJarDir, item)
+ deodexJar = odexJar[0:-4] + ".jar"
+ OdexZip.deodexOneFile(odexJar, deodexJar)
+ break
+
+
+ @staticmethod
+ def deodexApp(odexApkDir):
+ """ De-oat app
+ """
+
+ if OPTIONS.formatApp == False: return
+
+ for item in os.listdir(odexApkDir):
+ if item.endswith(".odex"):
+ odexApk = os.path.join(odexApkDir, item)
+ deodexApk = odexApk[0:-4] + ".apk"
+ OdexZip.deodexOneFile(odexApk, deodexApk)
+
+
+ @staticmethod
+ def deodexOneFile(odexFile, deodexFile):
+ """ De-odex one file.
+ """
+
+ if not odexFile.endswith(".odex"): return
+
+ temp = tempfile.mktemp()
+
+ # Phase 1: Baksmali the odex file
+ cmd = ["baksmali", "-x", odexFile, "-d", "framework", "-I", "-o", os.path.join(temp, "out")]
+ if OPTIONS.apiLevel != None: cmd.extend(["-a", OPTIONS.apiLevel])
+ if OPTIONS.classpath != None: cmd.extend(["-c", OPTIONS.classpath])
+
+ cmd = " ".join(cmd)
+ Log.d(TAG, cmd)
+ Log.d(TAG, commands.getoutput(cmd))
+
+ # Phase 2: Smali the files into dex
+ oldDir = os.path.abspath(os.curdir)
+ os.chdir(temp)
+
+ cmd = "smali out/ -o classes.dex"
+ Log.d(TAG, commands.getoutput(cmd))
+ #Utils.runWithOutput(["smali", "out", "-o", "classes.dex"])
+
+ # Phase 3: Package
+ if os.path.exists(deodexFile):
+ #cmd = ["jar", "uf", deodexFile, "classes.dex"]
+ cmd = "jar uf %s classes.dex" % deodexFile
+ else:
+ #cmd = ["jar", "cf", deodexFile, "classes.dex"]
+ cmd = "jar cf %s classes.dex" % deodexFile
+
+ Log.d(TAG, commands.getoutput(cmd))
+ #Utils.runWithOutput(cmd)
+ os.chdir(oldDir)
+
+ if os.path.exists(odexFile): os.remove(odexFile)
+
+ Log.i(TAG, "Delete %s" %temp)
+ shutil.rmtree(temp)
+
+ # Phase 4: zipalign
+ #Utils.runWithOutput(["zipalign", "4", deodexFile, deodexFile+".aligned"])
+ #Utils.runWithOutput(["mv", deodexFile+".aligned", deodexFile])
+
+ cmd = "zipalign 4 %s %s" %(deodexFile, deodexFile+".aligned")
+ Log.d(TAG, commands.getoutput(cmd))
+
+ cmd = "mv %s %s" %(deodexFile+".aligned", deodexFile)
+ Log.d(TAG, cmd)
+
+
+def debug():
+
+ Log.DEBUG = True
+ root = "root directory the unziped files"
+ OdexZip(root).deodex()
+
+
+if __name__ == "__main__":
+
+ debug()
+
diff --git a/reverses/otanormalize.py b/reverses/otanormalize.py
new file mode 100755
index 0000000..386dd7d
--- /dev/null
+++ b/reverses/otanormalize.py
@@ -0,0 +1,39 @@
+#!/usr/bin/python
+
+# Copyright 2015 Coron
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+__author__ = 'duanqz@gmail.com'
+
+
+from os import sys
+
+from common import Options, Log
+from zipformatter import ZipFormatter
+
+
+# Global
+TAG="reverse-oatnormalize"
+OPTIONS = Options()
+
+
+if __name__ == "__main__":
+
+ Log.DEBUG = True
+
+ OPTIONS.handle(sys.argv)
+
+ ZipFormatter.create(OPTIONS).format()
+
diff --git a/reverses/zipformatter.py b/reverses/zipformatter.py
new file mode 100644
index 0000000..9fb6934
--- /dev/null
+++ b/reverses/zipformatter.py
@@ -0,0 +1,253 @@
+#!/usr/bin/python
+
+# Copyright 2015 Coron
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+__author__ = 'duanqz@gmail.com'
+
+
+import os
+import shutil, commands
+import tempfile
+import re
+
+import deodex, deoat
+from common import Log, Options
+
+
+# Global
+TAG="reverse-zipformatter"
+
+
+class ZipFormatter:
+ """ Abstract Model of ZIP Formatter
+ """
+
+ def __init__(self, zipModel):
+ self.mZipModel = zipModel
+
+
+ def format(self, zipBack=True):
+ self.mZipModel.unzip()
+
+ if not self.mZipModel.isFormatted():
+ self.doFormat()
+
+ if zipBack:
+ self.mZipModel.zip()
+
+
+ def doFormat(self):
+ Log.e(TAG, "No implementation of ZipFormatter.doFormat() if found.")
+ raise Exception("Should be implemented by sub-class!")
+
+
+ def getFilesRoot(self):
+ """ Get the root directory of unziped files
+ """
+
+ return self.mZipModel.getRoot();
+
+
+ @staticmethod
+ def genOptions(inZip):
+ """ Generate options.
+ Only format framework.
+ """
+
+ options = Options()
+ options.inZip = inZip
+ options.outZip = inZip + ".std.zip"
+ options.formatFrw = True
+
+ return options
+
+
+ @staticmethod
+ def create(options):
+ """ Create a zip formatter for the incoming zip file
+ """
+
+ zipModel = ZipModel(options.inZip, options.outZip)
+ zipType = zipModel.getZipType()
+
+ Log.i(TAG, "process(): Creating %s ZipFormatter..." %zipType)
+ if zipType == ZipModel.ART:
+ deoat.OPTIONS = options
+ return Deoat(zipModel)
+
+ elif zipType == ZipModel.DVM:
+ deodex.OPTIONS = options
+ return Deodex(zipModel)
+
+ else:
+ raise Exception("Unknown OTA package zip. Is it an ART or DALVIKVM package?")
+
+
+class Deodex(ZipFormatter):
+ """ De-odex formatter
+ """
+
+ def __init__(self, zipModel):
+ ZipFormatter.__init__(self, zipModel)
+
+
+ def doFormat(self):
+ deodex.OdexZip(self.getFilesRoot()).deodex()
+
+
+class Deoat(ZipFormatter):
+ """ De-oat formatter
+ """
+
+ def __init__(self, zipModel):
+ ZipFormatter.__init__(self, zipModel)
+
+
+ def doFormat(self):
+ deoat.OatZip(self.getFilesRoot()).deoat().rebuild()
+
+
+
+class ZipModel:
+ """ Model of an OTA package zip
+ """
+
+ ART = "ART"
+ DVM = "DVM"
+
+ DAT2IMG = os.path.join(os.path.dirname(__file__), "de-dat", "dedat.sh")
+
+ def __init__(self, inZip, outZip):
+ self.mInZip = inZip
+ self.mOutZip = outZip
+ self.mRoot = None
+
+
+ def unzip(self):
+ # Already unziped
+ if self.mRoot is not None: return
+
+ self.mRoot = tempfile.mkdtemp()
+
+ Log.i(TAG, "unzip %s to %s" % (self.mInZip, self.mRoot))
+ cmd = "unzip -q -o %s -d %s" %(self.mInZip, self.mRoot)
+ Log.d(TAG, commands.getoutput(cmd))
+
+ self.dedatIfNeeded()
+
+ # Format path
+ if os.path.exists(os.path.join(self.mRoot, "SYSTEM")):
+ shutil.move(os.path.join(self.mRoot, "SYSTEM"), os.path.join(self.mRoot, "system"))
+
+ return self
+
+
+ def zip(self):
+ if self.mRoot is None: return
+
+ origDir = os.path.abspath(os.curdir)
+
+ Log.i(TAG, "zip from %s to %s" % (self.mRoot, self.mOutZip))
+
+ os.chdir(self.mRoot)
+ cmd = "zip -r -y -q tmp *; mv tmp.zip %s" % self.mOutZip
+ Log.d(TAG, commands.getoutput(cmd))
+ os.chdir(origDir)
+
+ Log.i(TAG, "Deleting %s" % self.mRoot)
+ shutil.rmtree(self.mRoot)
+
+ Log.i(TAG, "===> %s" % self.mOutZip)
+
+
+ def getRoot(self):
+ """ Note: This method is not thread-safe.
+ """
+
+ if self.mRoot is None: self.unzip()
+ return self.mRoot
+
+
+ def isFormatted(self):
+ return False
+
+
+ def dedatIfNeeded(self):
+ """ Android 5.0 zip structure:
+ * META-INF (folder containing scripts)
+ * system.new.dat (compressed /system partition)
+ * system.patch.dat
+ * system.transfer.list (see explanation below)
+ """
+
+ if not os.path.exists(os.path.join(self.mRoot, "system.new.dat")):
+ return
+
+ if not os.path.exists(os.path.join(self.mRoot, "system.transfer.list")):
+ return
+
+ if os.geteuid() != 0:
+ raise Exception("DEDAT should be executed as root.")
+
+ cmd = "%s %s" % (commands.mkarg(ZipModel.DAT2IMG), commands.mkarg(self.mRoot))
+ Log.d(TAG, commands.getoutput(cmd))
+
+
+ def getZipType(self):
+ """ Retrieve the OTA package type
+ The property defines the VM type.
+ If libart.so is used, it is an ART package;
+ If libdvm.so is used, it is an DVM package.
+ """
+
+ if self.mRoot is None: self.unzip()
+
+ buildProp = os.path.join(self.mRoot, "system/build.prop")
+
+ # Retrieve the in build.prop
+ zipType = None
+ if os.path.exists(buildProp):
+ fileHandle = open(buildProp, "r")
+ content = fileHandle.read()
+ vmlib = re.compile("\n.*sys.dalvik.vm.lib.*=\s*(?P.*)\n")
+ match = vmlib.search(content)
+ if match is not None:
+ libType = match.group("lib")
+ Log.d(TAG, "sys.dalvik.vm.lib=%s" % libType)
+
+ fileHandle.close()
+ else:
+ raise Exception("Could not find %s, unknown ota type" %buildProp)
+
+ if libType.find("art") >= 0:
+ zipType = ZipModel.ART
+ elif libType.find("dvm") >= 0:
+ zipType = ZipModel.DVM
+
+ return zipType
+
+
+
+def debug():
+ inZip = "/home/duanqizhi/tmp/n5-dat/n5-dat.zip"
+ outZip = "/home/duanqizhi/tmp/n5-dat/n5-dat.std.zip"
+ zipModel = ZipModel(inZip, outZip)
+ #zipModel.mRoot = "/home/duanqizhi/tmp/n5-dat"
+ print "Zip Type: %s" % zipModel.getZipType()
+
+if __name__ == "__main__":
+
+ debug()
diff --git a/rmline b/rmline
new file mode 120000
index 0000000..6468dcc
--- /dev/null
+++ b/rmline
@@ -0,0 +1 @@
+formatters/rmline.sh
\ No newline at end of file
diff --git a/smali b/smali
new file mode 120000
index 0000000..0e7a942
--- /dev/null
+++ b/smali
@@ -0,0 +1 @@
+reverses/de-odex/smali.sh
\ No newline at end of file
diff --git a/smaliparser/Content.py b/smaliparser/Content.py
new file mode 100644
index 0000000..1bcb982
--- /dev/null
+++ b/smaliparser/Content.py
@@ -0,0 +1,53 @@
+'''
+Created on Feb 26, 2014
+
+@author: tangliuxiang
+'''
+from SmaliLine import SmaliLine
+
+class Content(object):
+ def __init__(self, contentStr = None):
+ self.ContentStr = contentStr
+
+ def append(self, contentStr):
+ if self.ContentStr is not None:
+ self.ContentStr = "%s\n%s" %(self.ContentStr, contentStr)
+ else:
+ self.ContentStr = contentStr
+
+ def clone(self):
+ return Content(self.getContentStr())
+
+ def getContentStr(self):
+ return self.ContentStr
+
+ def setContentStr(self, contentStr):
+ self.ContentStr = contentStr
+
+ def isMultiLine(self):
+ firstLine = True
+ for line in self.ContentStr.split('\n'):
+ if firstLine is False:
+ if not SmaliLine(line).isBlank():
+ return True
+ else:
+ firstLine = True
+
+ return False
+
+ def getFirstLine(self):
+ if self.ContentStr is not None:
+ return self.ContentStr.split('\n')[0]
+ else:
+ return None
+
+ def getPostContent(self):
+ postContent = Content()
+ firstLine = True
+ for line in self.ContentStr.split('\n'):
+ if firstLine is False:
+ postContent.append(line)
+ else:
+ firstLine = False
+
+ return postContent
\ No newline at end of file
diff --git a/smaliparser/EntryUsed.py b/smaliparser/EntryUsed.py
new file mode 100644
index 0000000..24b9b1e
--- /dev/null
+++ b/smaliparser/EntryUsed.py
@@ -0,0 +1,72 @@
+'''
+Created on Jun 25, 2014
+
+@author: tangliuxiang
+'''
+
+import utils
+import SmaliEntry
+import Smali
+
+MAX_CHECK_INVOKE_DEEP = 50
+
+class Used(object):
+ '''
+ classdocs
+ '''
+
+ def __init__(self, sLib, aLib):
+ '''
+ Constructor
+ '''
+ self.mSLib = sLib
+ self.mALib = aLib
+ self.mMethodParser = MethodUsedParser(sLib, aLib)
+ self.mFieldParser = FieldUsedParser(sLib, aLib)
+
+ def isUsed(self, entry):
+ if entry.getType() == SmaliEntry.FIELD:
+ return self.mFieldParser.isUsed(entry)
+ elif entry.getType() == SmaliEntry.METHOD:
+ return self.mMethodParser.isUsed(entry)
+ else:
+ return False
+
+ def checkIsUsed(self, entry, deep = 0):
+ if entry is not None:
+ clsName = entry.getClassName()
+ entryName = entry.getName()
+ aSmali = self.mALib.getSmali(entry.getClassName())
+ if aSmali is not None and aSmali.getEntry(SmaliEntry.METHOD, entryName) is not None:
+ return True
+
+ sSmali = self.mSLib.getSmali(clsName)
+ usedMethodsList = self.getUsedMethods(sSmali, [entry])
+
+ if not usedMethodsList.has_key(entryName):
+ for childClsName in sSmali.getChildren():
+ cSmali = self.mSLib.getSmali(childClsName)
+ if cSmali is not None and self.checkIsUsed(cSmali.getEntry(SmaliEntry.METHOD, entryName)):
+ return True
+ else:
+ if len(usedMethodsList) < Smali.MAX_INVOKE_LEN and deep < MAX_CHECK_INVOKE_DEEP:
+ isUsed = False
+ for invokeItem in usedMethodsList[entryName]:
+ cSmali = self.mSLib.getSmali(invokeItem.belongCls)
+ if cSmali is not None and self.checkIsUsed(cSmali.getEntry(SmaliEntry.METHOD, invokeItem.belongMethod), deep + 1):
+ isUsed = True
+ break
+ return isUsed
+ else:
+ return True
+ return False
+
+class MethodUsedParser(Used):
+ def checkIsUsed(self, entry):
+ return False
+
+
+class FieldUsedParser(Used):
+ def checkIsUsed(self, entry):
+ print ""
+
\ No newline at end of file
diff --git a/smaliparser/FormatSmaliLib.py b/smaliparser/FormatSmaliLib.py
new file mode 100644
index 0000000..56a3758
--- /dev/null
+++ b/smaliparser/FormatSmaliLib.py
@@ -0,0 +1,50 @@
+'''
+Created on Jul 22, 2014
+
+@author: tangliuxiang
+'''
+
+import os, sys
+from os import path
+import Smali
+from SmaliLib import SmaliLib
+
+sys.path.append(path.dirname(path.dirname(path.abspath(__file__))))
+from formatters.format import Format
+
+
+class FormatSmaliLib(SmaliLib):
+ '''
+ classdocs
+ '''
+
+ mFormatList = {}
+ def __init__(self, libPath, smaliDirMaxDepth=0):
+ '''
+ Constructor
+ '''
+ self.mFieldFormatMap = {}
+ SmaliLib.__init__(self, libPath, smaliDirMaxDepth)
+
+ def __format(self, root, sPath):
+ sPath = os.path.abspath(sPath)
+ if not FormatSmaliLib.mFormatList.has_key(sPath):
+ sFormat = Format(root, sPath)
+ sFormat.do(Format.ACCESS_TO_NAME | Format.RESID_TO_NAME | Format.REMOVE_LINE)
+ FormatSmaliLib.mFormatList[sPath] = sFormat
+ return True
+ return False
+
+ def getFormatSmali(self, clsName):
+ oldSmali = self.getSmali(clsName)
+ if oldSmali is None:
+ print "Can not get class: %s in %s" %(clsName, self.getPath())
+ return None
+ if self.__format(self.getPath(), oldSmali.getPath()):
+ self.setSmali(clsName, Smali.Smali(oldSmali.getPath()))
+ return self.getSmali(clsName)
+
+ @staticmethod
+ def undoFormat():
+ for fKey in FormatSmaliLib.mFormatList.keys():
+ FormatSmaliLib.mFormatList[fKey].undo()
\ No newline at end of file
diff --git a/smaliparser/LibUtils.py b/smaliparser/LibUtils.py
new file mode 100644
index 0000000..3c65742
--- /dev/null
+++ b/smaliparser/LibUtils.py
@@ -0,0 +1,44 @@
+'''
+Created on Jul 22, 2014
+
+@author: tangliuxiang
+'''
+
+from FormatSmaliLib import FormatSmaliLib
+from SmaliLib import SmaliLib
+import os
+
+mSmaliLibDict = {}
+
+LIBTYPE_FORMAT = 1
+LIBTYPE_SMALILIB = 0
+
+def getSmaliLib(libPath, smaliDirMaxDepth = 0, libType = LIBTYPE_FORMAT):
+ absRoot = os.path.abspath(libPath)
+ if not mSmaliLibDict.has_key(absRoot) or mSmaliLibDict[absRoot].mSmaliDirMaxDepth != smaliDirMaxDepth:
+ if libType == LIBTYPE_FORMAT:
+ mSmaliLibDict[absRoot] = FormatSmaliLib(libPath, smaliDirMaxDepth)
+ elif libType == LIBTYPE_SMALILIB:
+ mSmaliLibDict[absRoot] = SmaliLib(libPath, smaliDirMaxDepth)
+ return mSmaliLibDict[absRoot]
+
+def getLibPath(path):
+ if os.path.isdir(path):
+ children = os.listdir(path)
+ for child in children:
+ if os.path.isdir("%s/smali" % child):
+ return path
+ while not os.path.isdir("%s/smali" % path):
+ path = os.path.dirname(path)
+ if path == "/":
+ return None
+ return os.path.dirname(path)
+
+def getOwnLib(smaliFile):
+ libPath = getLibPath(smaliFile)
+ if libPath is not None:
+ return getSmaliLib(libPath, 1)
+ return None
+
+def undoFormat():
+ FormatSmaliLib.undoFormat()
\ No newline at end of file
diff --git a/smaliparser/Replace.py b/smaliparser/Replace.py
new file mode 100644
index 0000000..2b53fee
--- /dev/null
+++ b/smaliparser/Replace.py
@@ -0,0 +1,203 @@
+'''
+Created on Jun 4, 2014
+
+@author: tangliuxiang
+'''
+from xml.dom import minidom
+import Content
+import LibUtils
+import Smali
+import SmaliEntry
+import SmaliEntryFactory
+import SmaliFileReplace
+import SmaliMethod
+import os
+import re
+import string
+import utils
+import sys
+
+
+class Replace(object):
+ '''
+ classdocs
+ '''
+ BLANK_CONTENT_XML = r'%s/autocomplete.xml' %(os.path.dirname(os.path.abspath(__file__)))
+ BLANK_ENTRY = None
+
+ def __init__(self, vendorDir, aospDir, bospDir, mergedDir):
+ '''
+ Constructor
+ '''
+ self.mVSLib = LibUtils.getSmaliLib(vendorDir)
+ self.mASLib = LibUtils.getSmaliLib(aospDir)
+ self.mBSLib = LibUtils.getSmaliLib(bospDir)
+ self.mMSLib = LibUtils.getSmaliLib(mergedDir)
+
+ def preReplaceCheck(self, smali):
+ unImplementMethods = self.getUnImplementMethods(smali)
+ canReplaceEntryList = []
+ canNotReplaceEntryList = []
+ if unImplementMethods is not None and len(unImplementMethods) > 0:
+ vSmali = self.mVSLib.getSmali(smali.getClassName())
+ if vSmali is None:
+ utils.SLog.d("Can't get smali %s from vendor: %s" %(smali.getClassName(), string.join(unImplementMethods)))
+ return (canReplaceEntryList, canNotReplaceEntryList)
+
+ unImplMethodEntryList = vSmali.getEntryListByNameList(SmaliEntry.METHOD, unImplementMethods)
+ (canReplaceEntryList, canNotReplaceEntryList) = self.mMSLib.getCanReplaceEntry(self.mVSLib, smali.getClassName(), unImplMethodEntryList)
+
+ for entry in canReplaceEntryList:
+ utils.SLog.d(" Can Replace: %s" %(entry.getSimpleString()))
+
+ for entry in canNotReplaceEntryList:
+ utils.SLog.d(" Can not Replace: %s" %(entry.getSimpleString()))
+
+ return (canReplaceEntryList, canNotReplaceEntryList)
+
+ @staticmethod
+ def replaceEntryInFile(entry, outFilePath):
+ utils.SLog.d(" REPLACE %s" % (entry.getSimpleString()))
+
+ dirName = os.path.dirname(outFilePath)
+ if not os.path.isdir(dirName):
+ os.makedirs(os.path.dirname(outFilePath))
+
+ outSmali = Smali.Smali(outFilePath)
+ outSmali.replaceEntry(entry)
+ outSmali.out()
+
+ print " ADD %s %s --> %s" % (entry.getType(), entry.getName(), os.path.basename(outFilePath))
+
+ @staticmethod
+ def replaceEntry(src, dst, type, name, withCheck = True):
+ srcSmali = Smali.Smali(src)
+ dstSmali = Smali.Smali(dst)
+
+ if srcSmali is None:
+ utils.SLog.d("%s doesn't exist or is not smali file!" %src)
+ return False
+
+ if dstSmali is None:
+ utils.SLog.d("%s doesn't exist or is not smali file!" %dst)
+ return False
+
+ name = name.split()[-1]
+ srcEntry = srcSmali.getEntry(type, name)
+ returnValue = False
+ if srcEntry is not None:
+ srcLib = LibUtils.getOwnLib(src)
+ dstLib = LibUtils.getOwnLib(dst)
+ if withCheck:
+ (canReplaceEntry, canNotReplaceEntry) = dstLib.getCanReplaceEntry(srcLib, dstSmali.getClassName(), [srcEntry], False)
+
+ if len(canNotReplaceEntry) > 0:
+ utils.SLog.fail("Failed to replace %s %s from %s to %s" % (type, name, src, dst))
+ for entry in canNotReplaceEntry:
+ if entry != srcEntry:
+ utils.SLog.fail(" Can not replace %s %s in %s" %(entry.getType(), entry.getName(), entry.getClassName()))
+ returnValue = False
+ else:
+ for entry in canReplaceEntry:
+ dstLib.replaceEntry(srcLib, entry)
+ returnValue = True
+ else:
+ dstLib.replaceEntry(srcLib, srcEntry)
+ returnValue = True
+ else:
+ utils.SLog.d("Can not get %s:%s from %s" %(type, name, src))
+ returnValue = False
+ return returnValue
+
+ @staticmethod
+ def parseAutoComXml():
+ assert os.path.isfile(Replace.BLANK_CONTENT_XML), "%s doesn't exist! Are you remove it?" %(Replace.BLANK_CONTENT_XML)
+ root = minidom.parse(Replace.BLANK_CONTENT_XML).documentElement
+
+ Replace.BLANK_ENTRY = {}
+
+ for item in root.childNodes:
+ if item.nodeType == minidom.Node.ELEMENT_NODE:
+ if not Replace.BLANK_ENTRY.has_key(item.nodeName):
+ Replace.BLANK_ENTRY[item.nodeName] = {}
+
+ if item.nodeName == SmaliEntry.METHOD:
+ returnType = item.getAttribute("return")
+ contentStr = item.getAttribute("content")
+ outStr = re.sub(r'^ ', '', contentStr, 0, re.M)
+ Replace.BLANK_ENTRY[item.nodeName][returnType] = outStr
+ else:
+ utils.SLog.d("Doesn't support %s in %s" %(item.nodeName, Replace.BLANK_CONTENT_XML))
+
+ @staticmethod
+ def __getBlankContentStr__(entry):
+ returnType = entry.getReturnType()
+ if Replace.BLANK_ENTRY is None:
+ Replace.parseAutoComXml()
+ if Replace.BLANK_ENTRY[entry.getType()].has_key(returnType):
+ return Replace.BLANK_ENTRY[entry.getType()][returnType]
+ else:
+ for key in Replace.BLANK_ENTRY[entry.getType()].keys():
+ if re.match(key, returnType) is not None:
+ return Replace.BLANK_ENTRY[entry.getType()][key]
+ return None
+
+ @staticmethod
+ def getBlankContent(entry):
+ content = Content.Content()
+ if entry.getType() == SmaliEntry.METHOD:
+ contentStr = Replace.__getBlankContentStr__(entry)
+
+ if contentStr is not None:
+ content.append(entry.getFirstLine())
+ content.append(contentStr)
+
+ return content
+
+ @staticmethod
+ def getBlankEntry(entry):
+ return SmaliEntryFactory.newSmaliEntry(entry.getType(), Replace.getBlankContent(entry), entry.getClassName(), entry.getPreContent())
+
+ @staticmethod
+ def appendBlankEntry(entry, outFilePath):
+ utils.SLog.d(" ADD BLANK %s" % (entry.getSimpleString()))
+
+ if Replace.BLANK_ENTRY is None:
+ Replace.parseAutoComXml()
+
+ if not Replace.BLANK_ENTRY.has_key(entry.getType()) or entry.getType() != SmaliEntry.METHOD:
+ utils.SLog.d("Doesn't support add blank %s in autocomplete")
+ return
+
+ dirName = os.path.dirname(outFilePath)
+ if not os.path.isdir(dirName):
+ os.makedirs(os.path.dirname(outFilePath))
+
+ partSmali = Smali.Smali(outFilePath)
+ nEntry = Replace.getBlankEntry(entry)
+ partSmali.replaceEntry(nEntry)
+ partSmali.out()
+
+ print " ADD %s %s --> %s" % (entry.getType(), entry.getName(), os.path.basename(outFilePath))
+
+ def getUnImplementMethods(self, smali):
+ return self.mMSLib.getUnImplementMethods(self.mASLib, smali)
+
+ def getCanReplaceToBoardMethods(self, smali, methodEntryList):
+ return self.mMSLib.getCanReplaceEntry(self.mBSLib, smali.getClassName(), methodEntryList)
+
+def replaceMethod(src, dst, methodName, withCheck = False):
+ ret = Replace.replaceEntry(src, dst, SmaliEntry.METHOD, methodName, withCheck)
+ if ret:
+ utils.SLog.i("\n>>>> SUCCESS: replaced method %s from %s to %s" %(methodName, src, dst))
+ else:
+ utils.SLog.i("\n>>>> FAILED: Can not replace method %s from %s to %s" %(methodName, src, dst))
+ LibUtils.undoFormat()
+ return ret
+
+def methodtobosp(smaliFile, methodName, withCheck = True):
+ utils.annotation.setAction("methodtosmali")
+ src = utils.getMatchFile(smaliFile, utils.BOSP)
+ dst = utils.getMatchFile(smaliFile, utils.TARGET)
+ if replaceMethod(src, dst, methodName, withCheck) is False:
+ raise
diff --git a/smaliparser/SAutoCom.py b/smaliparser/SAutoCom.py
new file mode 100644
index 0000000..4ca429e
--- /dev/null
+++ b/smaliparser/SAutoCom.py
@@ -0,0 +1,54 @@
+'''
+Created on Apr 4, 2014
+
+@author: tangliuxiang
+'''
+import utils
+import Replace
+
+from formatters.log import Paint
+
+
+MAX_CHECK_INVOKE_DEEP = 50
+
+class SAutoCom(object):
+ '''
+ classdocs
+ '''
+
+ @staticmethod
+ def autocom(vendorDir, aospDir, bospDir, mergedDir, outdir, comModuleList):
+ sReplace = Replace.Replace(vendorDir, aospDir, bospDir, mergedDir)
+
+ for module in comModuleList:
+ print Paint.bold(" Complete missed method in %s") % module
+ needComleteDir = '%s/%s' % (mergedDir, module)
+ sDict = utils.getSmaliDict(needComleteDir)
+ for clsName in sDict.keys():
+ mSmali = sReplace.mMSLib.getSmali(clsName)
+ if mSmali is None:
+ utils.SLog.d("can not get class: %s" % clsName)
+ continue
+
+ (canReplaceEntryList, canNotReplaceEntryList) = sReplace.preReplaceCheck(mSmali)
+
+ for entry in canReplaceEntryList:
+ sReplace.replaceEntryInFile(entry, SAutoCom.getAutocomPartPath(sReplace, entry, outdir))
+
+ for entry in canNotReplaceEntryList:
+ sReplace.appendBlankEntry(entry, SAutoCom.getAutocomPartPath(sReplace, entry, outdir))
+
+ @staticmethod
+ def getAutocomPartPath(sCheck, entry, outdir):
+ cls = entry.getClassName()
+ cSmali = sCheck.mVSLib.getSmali(cls)
+
+ if cSmali is None:
+ utils.SLog.d("can not get class %s from: %s" % (cls, sCheck.mVSLib.getPath()))
+ return
+
+ jarName = cSmali.getJarName()
+ pkgName = cSmali.getPackageName()
+
+ return r'%s/%s/smali/%s/%s.smali.part' % (outdir, jarName, pkgName, cSmali.getClassBaseName())
+
diff --git a/smaliparser/SCheck b/smaliparser/SCheck
new file mode 100755
index 0000000..1b6c3f2
--- /dev/null
+++ b/smaliparser/SCheck
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+SCHECK=`dirname $0`/SCheck.py
+
+python $SCHECK $@
diff --git a/smaliparser/SCheck.py b/smaliparser/SCheck.py
new file mode 100644
index 0000000..80b5b49
--- /dev/null
+++ b/smaliparser/SCheck.py
@@ -0,0 +1,103 @@
+'''
+Created on Mar 12, 2014
+
+@author: tangliuxiang
+
+'''
+import SmaliEntry
+import Smali
+import string
+import SmaliMethod
+import sys
+import os
+import getopt
+import SAutoCom
+import SmaliFileReplace
+import tobosp
+import utils
+import Replace
+import LibUtils
+import traceback
+
+
+class Options(object): pass
+OPTIONS = Options()
+OPTIONS.autoComplete = False
+
+OPTIONS.replaceWithCheck = False
+OPTIONS.methodToBosp = False
+OPTIONS.smaliToBosp = False
+
+def formatSmali(smaliLib, smaliFileList = None):
+ utils.SLog.i(" begin format smali files, please wait....")
+ if smaliFileList is not None:
+ idx = 0
+ while idx < len(smaliFileList):
+ clsName = utils.getClassFromPath(smaliFileList[idx])
+ cSmali = smaliLib.getSmali(clsName)
+ smaliLib.formatUsingField(cSmali)
+ idx = idx + 1
+ else:
+ for clsName in smaliLib.mSDict.keys():
+ cSmali = smaliLib.getSmali(clsName)
+ smaliLib.formatUsingField(cSmali)
+ utils.SLog.i(" format done")
+
+def usage():
+ print __doc__
+
+def main(argv):
+ options,args = getopt.getopt(argv[1:], "hams", [ "help", "autocomplete", "methodtobosp", "smalitobosp"])
+ for name,value in options:
+ if name in ("-h", "--help"):
+ usage()
+ elif name in ("-a", "--autocomplete"):
+ OPTIONS.autoComplete = True
+ elif name in ("-m", "--methodtobosp"):
+ OPTIONS.replaceWithCheck = False
+ OPTIONS.methodToBosp = True
+ elif name in ("-s", "--smalitobosp"):
+ OPTIONS.smaliToBosp = True
+ else:
+ utils.SLog.w("Wrong parameters, see the usage....")
+ usage()
+
+ if OPTIONS.autoComplete:
+ if len(args) >= 6:
+ try:
+ SAutoCom.SAutoCom.autocom(args[0], args[1], args[2], args[3], args[4], args[5:])
+ except:
+ traceback.print_exc()
+ # see error info in help.xml for ERR_AUTOCOM_FAILED
+ sys.exit(158)
+ else:
+ # see error info in help.xml for ERR_WRONG_PARAMETERS
+ sys.exit(157)
+ elif OPTIONS.methodToBosp:
+ if len(args) >= 2:
+ try:
+ Replace.methodtobosp(args[0], args[1], OPTIONS.replaceWithCheck)
+ except:
+ traceback.print_exc()
+ # see error info in help.xml for ERR_METHODTOBOSP_FAILED
+ sys.exit(159)
+ else:
+ # see error info in help.xml for ERR_WRONG_PARAMETERS
+ sys.exit(157)
+ elif OPTIONS.smaliToBosp:
+ if len(args) >= 1:
+ try:
+ SmaliFileReplace.smalitobosp(args, False)
+ except:
+ traceback.print_exc()
+ # see error info in help.xml for ERR_SMALITOBOSP_FAILED
+ sys.exit(160)
+ else:
+ # see error info in help.xml for ERR_WRONG_PARAMETERS
+ sys.exit(157)
+
+if __name__ == "__main__":
+ if len(sys.argv) > 2:
+ main(sys.argv)
+ else:
+ usage()
diff --git a/smaliparser/Smali.py b/smaliparser/Smali.py
new file mode 100644
index 0000000..b3be224
--- /dev/null
+++ b/smaliparser/Smali.py
@@ -0,0 +1,477 @@
+'''
+Created on Feb 26, 2014
+
+@author: tangliuxiang
+'''
+
+import os
+import re
+import SmaliEntry
+import hashlib
+import SmaliParser
+import utils
+
+from Content import Content
+
+MAX_INVOKE_LEN = 5
+DEBUG = True
+
+DEFAULT_SUPER = r'Ljava/lang/Object;'
+
+class Smali(object):
+ '''
+ classdocs
+ '''
+
+ def __init__(self, smaliFilePath):
+ '''
+ Constructor
+ '''
+ self.mPath = smaliFilePath
+ self.mParser = SmaliParser.SmaliParser(smaliFilePath, False)
+ self.mInvokeMethods = None
+ self.mAllMethods = None
+ self.mImplementsClassList = None
+ self.mChildrenClsNameList = []
+ self.__mWasInvokedList = {}
+ self.mClassName = None
+ self.mSuperClassName = None
+ self.mSourceName = None
+ self.mPackageName = None
+ self.mUsedOutsideFields = None
+ self.mUsedFields = None
+ self.mMemberClasses = None
+ self.mIsPartSmali = utils.isPartSmaliFile(smaliFilePath)
+ self.mModifed = False
+ self.mDefaultOutPath = None
+ self.mPreOutPath = None
+ self.mFormatMapFile = "%s.fieldMap" %(self.mPath)
+
+ def useField(self, name):
+ pass
+
+ def wasInvoke(self, invokeItem, check = False):
+ if check and not self.checkInvokeType(invokeItem.method, invokeItem.type):
+ utils.SLog.e("wrong invoke in class %s, method: %s, invoke method: %s, invoke type: %s" %(invokeItem.cls, invokeItem.belongMethod, invokeItem.method, invokeItem.type))
+ return False
+
+ if not self.__mWasInvokedList.has_key(invokeItem.method):
+ self.__mWasInvokedList[invokeItem.method] = []
+ if len(self.__mWasInvokedList[invokeItem.method]) < MAX_INVOKE_LEN:
+ self.__mWasInvokedList[invokeItem.method].append(invokeItem)
+
+ def getWasInvokeList(self):
+ return self.__mWasInvokedList
+
+ def checkInvokeType(self, methodName, invokeType):
+ if invokeType is None:
+ return True
+ else:
+ # need write function to check invoke type
+ return True
+
+ def addChild(self, childClsName):
+ self.mChildrenClsNameList.append(childClsName)
+
+ def getChildren(self):
+ return self.mChildrenClsNameList
+
+ def hasChild(self, child):
+ for ch in self.mChildrenClsNameList:
+ if ch == child:
+ return True
+ return False
+
+ def getMemberSmaliList(self):
+ if self.mMemberClasses is None:
+ self.mMemberClasses = []
+ clsBaseName = self.getClassBaseName()
+ memberClassRe = re.compile(r'(?:^%s\$.*%s$)' % (clsBaseName, utils.SMALI_POST_SUFFIX))
+ dirName = os.path.dirname(self.getPath())
+
+ for root, dirs, files in os.walk(dirName):
+ for fn in files:
+ if bool(memberClassRe.match(fn)):
+ self.mMemberClasses.append(Smali('%s/%s' % (dirName, fn)))
+ return self.mMemberClasses
+
+ def getEntryList(self, type = None, filterInList = None, filterOutList = None, maxSize=0):
+ entryList = self.mParser.getEntryList()
+ if type is None:
+ return entryList
+
+ outEntryList = []
+ for entry in entryList:
+ if entry.getType() == type:
+ if filterInList is not None and not entry.hasKeyList(filterInList):
+ continue
+
+ if filterOutList is not None and entry.hasKeyList(filterOutList):
+ continue
+
+ outEntryList.append(entry)
+ if maxSize > 0 and len(outEntryList) >= maxSize:
+ break;
+
+ return outEntryList
+
+ def getEntry(self, type, name, filterInList = None, filterOutList = None):
+ for entry in self.getEntryList(type, filterInList, filterOutList):
+ if entry.getName() == name:
+ return entry
+ return None
+
+ def hasEntry(self, type, name, filterInList = None, filterOutList = None):
+ return self.getEntry(type, name, filterInList, filterInList) is not None;
+
+ def hasMethod(self, name, filterInList = None, filterOutList = None):
+ return self.hasEntry(SmaliEntry.METHOD, name, filterInList, filterOutList)
+
+ def hasField(self, name, filterInList = None, filterOutList = None):
+ return self.hasEntry(SmaliEntry.FIELD, name, filterInList, filterOutList)
+
+ def getEntryNameList(self, type = None, filterInList = None, filterOutList = None):
+ outEntryList = []
+ for entry in self.getEntryList(type, filterInList, filterOutList):
+ outEntryList.append(entry.getName())
+ return outEntryList
+
+ def getEntryListByNameList(self, type, nameList):
+ outList = []
+ for entry in self.getEntryList(type):
+ for name in nameList:
+ if name == entry.getName():
+ outList.append(entry)
+ break
+ return outList
+
+ def getMethodsNameList(self, filterInList = None, filterOutList = None):
+ return self.getEntryNameList(SmaliEntry.METHOD, filterInList, filterOutList)
+
+ def getAbstractMethodsNameList(self):
+ return self.getMethodsNameList([utils.KEY_ABSTRACT])
+
+ def isAbstractClass(self):
+ entryList = self.getEntryList(SmaliEntry.CLASS, [utils.KEY_ABSTRACT])
+ assert len(entryList) <= 1, "Error: should has only one class define"
+ return len(entryList) == 1
+
+ def isInterface(self):
+ entryList = self.getEntryList(SmaliEntry.CLASS, [utils.KEY_INTERFACE])
+ assert len(entryList) <= 1, "Error: should has only one class define"
+ return len(entryList) == 1
+
+ def getSuperClassName(self):
+ if self.mSuperClassName is None:
+ entryList = self.getEntryList(SmaliEntry.SUPER, None, None, 1)
+
+ if len(entryList) == 1:
+ self.mSuperClassName = entryList[0].getName()
+ else:
+ # java/lang/Object doesn't have super
+ if utils.getClassFromPath(self.mPath) != DEFAULT_SUPER:
+ if not self.mIsPartSmali:
+ utils.SLog.w("Wrong smali, should define the super! (%s)" % (self.mPath))
+ self.mSuperClassName = DEFAULT_SUPER
+ else:
+ self.mSuperClassName = None
+ return self.mSuperClassName
+
+ def getImplementClassList(self):
+ if self.mImplementsClassList is None:
+ self.mImplementsClassList = []
+ for entry in self.getEntryList(SmaliEntry.IMPLEMENTS):
+ self.mImplementsClassList.append(entry.getName())
+
+ return self.mImplementsClassList
+
+ def getSuperAndImplementsClassName(self):
+ clsNameList = []
+ superClsName = self.getSuperClassName()
+
+ if superClsName is not None:
+ clsNameList.append(superClsName)
+
+ implementsEntryList = self.getEntryList(SmaliEntry.IMPLEMENTS)
+ for entry in implementsEntryList:
+ clsNameList.append(entry.getName())
+
+ return clsNameList
+
+ def getPath(self):
+ return self.mPath
+
+ def removeEntryByName(self, type, name):
+ entry = self.getEntry(type, name)
+ return self.mParser.removeEntry(entry)
+
+ def removeEntry(self, entry):
+ return self.mParser.removeEntry(entry)
+
+ def addEntry(self, entry, idx = -1, preFlag = None):
+ nEntry = entry.clone()
+ if preFlag is not None:
+ nPreContent = nEntry.getPreContent()
+ if nPreContent is None:
+ nPreContent = Content(preFlag)
+ nEntry.setPreContent(nPreContent)
+ else:
+ nPreContent.append(preFlag)
+
+ result = self.mParser.addEntry(nEntry, idx)
+ self.mModifed = True
+ return result
+
+ def getIndex(self, entry):
+ return self.mParser.getIndex(entry)
+
+ def replaceEntry(self, entry, preFlag = None):
+ nEntry = entry.clone()
+ if preFlag is not None:
+ nPreContent = nEntry.getPreContent()
+ if nPreContent is None:
+ nPreContent = Content(preFlag)
+ nEntry.setPreContent(nPreContent)
+ else:
+ nPreContent.append(preFlag)
+
+ result = self.mParser.replaceEntry(nEntry)
+ self.mModifed = True
+ return result
+
+ def getClassName(self):
+ if self.mClassName is None:
+ entryList = self.getEntryList(SmaliEntry.CLASS, None, None, 1)
+
+ if len(entryList) != 1:
+ if not self.mIsPartSmali:
+ utils.SLog.w("should has only one class define! (%s)" %(self.mPath))
+ self.mClassName = utils.getClassFromPath(self.getPath())
+ else:
+ self.mClassName = entryList[0].getName()
+ return self.mClassName
+
+ def getClassBaseName(self):
+ return utils.getClassBaseNameFromPath(self.getPath())
+
+ def getSourceName(self):
+ if self.mSourceName is None:
+ entryList = self.getEntryList(SmaliEntry.SOURCE, None, None, 1)
+
+ assert len(entryList) == 1
+ self.mSourceName = entryList[0].getName()
+ return self.mSourceName
+
+ def getJarName(self):
+ return utils.getJarNameFromPath(self.getPath())
+
+ def getPackageName(self):
+ if self.mPackageName is None:
+ self.mPackageName = utils.getPackageFromClass(self.getClassName())
+
+ return self.mPackageName;
+
+ def __getInvokeMethods__(self):
+ invokeMethodsList = []
+ for entry in self.getEntryList(SmaliEntry.METHOD):
+ invokeMethodsList.extend(entry.getInvokeMethods())
+ return list(set(invokeMethodsList))
+
+ def getInvokeMethods(self, filterInList = None):
+ if self.mInvokeMethods is None:
+ self.mInvokeMethods = self.__getInvokeMethods__()
+
+ if filterInList is None:
+ return self.mInvokeMethods
+
+ outInvokeMethodsList = []
+ for invokeItem in self.mInvokeMethods:
+ for k in filterInList:
+ if k == invokeItem.type:
+ outInvokeMethodsList.append(invokeItem)
+ break;
+
+ return outInvokeMethodsList
+
+ def __getUsedFields__(self):
+ usedFieldsList = []
+ for entry in self.getEntryList(SmaliEntry.METHOD):
+ usedFieldsList.extend(entry.getUsedFields())
+ return usedFieldsList
+
+ def getUsedFields(self, filterInList = None):
+ if self.mUsedFields is None:
+ self.mUsedFields = self.__getUsedFields__()
+
+ if filterInList is None:
+ return self.mUsedFields
+
+ outUsedMethodsList = []
+ for usedFieldItem in self.mUsedFields:
+ for k in filterInList:
+ if k == usedFieldItem.type:
+ outUsedMethodsList.append(usedFieldItem)
+ break;
+
+ return outUsedMethodsList
+
+ def getUsedOutsideFields(self):
+ if self.mUsedOutsideFields is None:
+ usedFileds = self.getUsedFields()
+ usedOutsideFields = []
+ for usedFieldItem in usedFileds:
+ if not usedFieldItem.cls == self.getClassName() or not self.hasField(usedFieldItem.field):
+ usedOutsideFields.append(usedFieldItem)
+ self.mUsedOutsideFields = usedOutsideFields
+ return self.mUsedOutsideFields
+
+ def getAllMethods(self):
+ return self.mAllMethods;
+
+ def setAllMethods(self, allMethods):
+ self.mAllMethods = allMethods
+
+ def toString(self, entryList=None):
+ if entryList is None:
+ entryList = self.getEntryList()
+
+ outContent = Content()
+ for entry in self.getEntryList():
+ outContent.append(entry.toString())
+ if outContent.getContentStr() is not None:
+ outContent.append("")
+ return outContent.getContentStr()
+
+ def toStringByType(self, type):
+ return self.toString(self.getEntryList(type))
+
+ # not finish
+ def split(self, outdir):
+ """ Return the sorted partition list.
+ """
+
+ sName = os.path.basename(self.mPath)[:-6]
+
+ utils.SLog.d("begin split file: %s to %s" %(self.mPath, outdir))
+
+ partList = []
+
+ sHeadFile = file('%s/%s.head' % (outdir, sName), 'w+')
+ partList.append(sHeadFile.name)
+
+ for entry in self.getEntryList():
+ entryStr = entry.toString()
+ if entryStr is None:
+ continue
+
+ if entry.getType() == SmaliEntry.METHOD:
+ methodFilePath = getHashMethodPath(sName, entry, outdir)
+ partList.append(methodFilePath)
+
+ sMethodFile = file(methodFilePath, 'w+')
+ sMethodFile.write("%s\n" %entryStr)
+ sMethodFile.close()
+ else:
+ sHeadFile.write("%s\n" %entryStr)
+ sHeadFile.close()
+
+ return partList
+
+ def formatUsingField(self, formatFieldMap):
+ modified = False
+
+ for entry in self.getEntryList():
+ if entry.formatUsingField(formatFieldMap):
+ modified = True
+ #self.__saveFormatMap(formatFieldMap)
+ return modified
+
+ def __saveFormatMap(self, formatFieldMap):
+ mapFile = file(self.mFormatMapFile, "w+")
+
+ for key in formatFieldMap.keys():
+ mapFile.write("%s#%s\n" %(key, formatFieldMap[key]))
+
+ mapFile.close()
+
+ def __getReverseFormatMap(self):
+ formatFieldMap = {}
+ if not os.path.isfile(self.mFormatMapFile):
+ return formatFieldMap
+
+ mapFile = file(self.mFormatMapFile, "r")
+ for line in mapFile.readlines():
+ splitArray = line[:-1].split('#')
+ assert len(splitArray) >= 2, "Error: Wrong format map in %s" %(self.getPath())
+
+ formatFieldMap[splitArray[1]] = splitArray[0]
+
+ mapFile.close()
+ return formatFieldMap
+
+ def undoFormatUsingField(self):
+ formatFieldMap = self.__getReverseFormatMap()
+ modified = False
+ for entry in self.getEntryList():
+ if entry.formatUsingField(formatFieldMap):
+ modified = True
+
+ if os.path.isfile(self.mFormatMapFile):
+ os.remove(self.mFormatMapFile)
+ return modified
+
+ def isModifed(self):
+ return self.mModifed
+
+ def modify(self):
+ self.mModifed = True
+
+ def cleanModify(self):
+ self.mModifed = False
+
+ def setDefaultOutPath(self, outPath):
+ self.mDefaultOutPath = outPath
+ self.modify()
+
+ def getDefaultOutPath(self):
+ if self.mDefaultOutPath == None:
+ return self.getPath()
+ else:
+ return self.mDefaultOutPath
+
+ def out(self, outPath = None):
+ if outPath is None:
+ outPath = self.getDefaultOutPath()
+
+ if self.mPreOutPath == outPath and self.mModifed == False:
+ return
+
+ self.mPreOutPath = outPath
+ self.mModifed = False
+ utils.SLog.d("out to : %s" %outPath)
+
+ dirName = os.path.dirname(outPath)
+ if not os.path.isdir(dirName):
+ os.makedirs(os.path.dirname(outPath))
+
+ sFile = file(outPath, "w+")
+
+ outStr = self.toString()
+ if outStr is None:
+ return
+ sFile.write(outStr)
+ sFile.close()
+
+DEFAULT_HASH_LEN = 6
+def getHashCode(name, len = DEFAULT_HASH_LEN):
+ return hashlib.md5(name).hexdigest()[:len]
+
+def getHashMethodPath(sName, entry, outdir):
+ eName = entry.getName()
+ mName = eName.split(r'(')[0]
+ sMethodPath = '%s/%s.%s.%s' %(outdir, sName, mName, getHashCode(eName))
+ idx=1
+ while os.path.isfile(sMethodPath):
+ sMethodPath = '%s/%s.%s.%s.%s' %(outdir, sName, mName, getHashCode(eName), idx)
+ idx = idx + 1
+ return sMethodPath
diff --git a/smaliparser/SmaliClass.py b/smaliparser/SmaliClass.py
new file mode 100644
index 0000000..8188e6f
--- /dev/null
+++ b/smaliparser/SmaliClass.py
@@ -0,0 +1,15 @@
+'''
+Created on Apr 3, 2014
+
+@author: tangliuxiang
+'''
+
+from SmaliEntry import SmaliEntry
+
+class SmaliClass(SmaliEntry):
+ '''
+ classdocs
+ '''
+
+ def getSimpleString(self):
+ return "%s %s" %(self.getType(), self.getClassName())
diff --git a/smaliparser/SmaliEntry.py b/smaliparser/SmaliEntry.py
new file mode 100644
index 0000000..2367dc1
--- /dev/null
+++ b/smaliparser/SmaliEntry.py
@@ -0,0 +1,196 @@
+'''
+Created on Feb 26, 2014
+
+@author: tangliuxiang
+'''
+import string
+from Content import *
+
+CLASS = "class"
+SUPER = "super"
+SOURCE = "source"
+IMPLEMENTS = "implements"
+ANNOTATION = "annotation"
+FIELD = "field"
+METHOD = "method"
+
+class SmaliEntry(object):
+ '''
+ classdocs
+ '''
+
+ def __init__(self, type, content, clsName=None, preContent=None):
+ '''
+ Constructor
+ '''
+ self.mContent = content
+ self.mType = type
+ self.mPreContent = preContent
+
+ self.mFirstLine = None
+ self.mKeyList = None
+ self.mName = None
+ self.mClsName = clsName
+
+ self.mFlag = 0
+
+ def clone(self):
+ nPreContent = None
+ if self.getPreContent() is not None:
+ nPreContent = self.getPreContent().clone()
+ return SmaliEntry(self.getType(), self.getContent().clone(), self.getClassName(), nPreContent)
+
+ def addFlag(self, flag):
+ self.mFlag = self.mFlag | flag
+
+ def rmFlag(self, flag):
+ self.mFlag = self.mFlag & (~flag)
+
+ def setFlag(self, flag):
+ self.mFlag = flag
+
+ def getFlag(self):
+ return self.mFlag
+
+ def setClassName(self, clsName):
+ self.mClsName = clsName
+
+ def getClassName(self):
+ return self.mClsName
+
+ def getEntry(self):
+ return self.mEntry;
+
+ def setEntry(self, entry):
+ self.mEntry = entry;
+
+ def getType(self):
+ return self.mType;
+
+ def setType(self, type):
+ self.mType = type;
+
+ def getContent(self):
+ return self.mContent;
+
+ def getContentStr(self):
+ if self.mContent is not None:
+ return self.mContent.getContentStr()
+ else:
+ return None
+
+ def setContent(self, content):
+ self.mContent = content
+
+ def setContentStr(self, contentStr):
+ if self.mContent is not None:
+ self.mContent.setContentStr(contentStr)
+ else:
+ self.mContent = Content(contentStr)
+
+ def getPreContent(self):
+ return self.mPreContent;
+
+ def getPreContentStr(self):
+ if self.mPreContent is not None:
+ return self.mPreContent.getContentStr()
+ else:
+ return None
+
+ def setPreContent(self, preContent):
+ self.mPreContent = preContent
+
+ def setPreContentStr(self, preContentStr):
+ if self.mPreContent is not None:
+ self.mPreContent.setContentStr(preContentStr)
+ else:
+ self.mPreContent = Content(preContentStr)
+
+ def equals(self, sEntry):
+ if sEntry.mType is not None \
+ and sEntry.mType == self.mType \
+ and sEntry.getName() == self.getName():
+ return True
+ return False
+
+ def formatUsingField(self, formatMap):
+ return False
+
+ def undoFormatUsingField(self, formatMap):
+ return False
+
+ def getFirstLine(self):
+ if self.mFirstLine is None:
+ self.mFirstLine = self.getContent().getFirstLine()
+
+ return self.mFirstLine
+
+ def getName(self):
+ if self.mName is None:
+ if self.getFirstLine() is None:
+ return ""
+
+ splitArray = self.getFirstLine().split()
+ self.mName = splitArray[len(splitArray) - 1]
+ return self.mName
+
+ def getKeyList(self):
+ if self.mKeyList is None:
+ splitArray = self.getFirstLine().split()
+ if len(splitArray) >= 3:
+ self.mKeyList = splitArray[1:len(splitArray) - 1]
+ else:
+ self.mKeyList = []
+
+ # print "keyList: %s" %self.mKeyList
+ return self.mKeyList
+
+ def hasKey(self, key):
+ if key is None:
+ return False
+
+ for k in self.getKeyList():
+ if k == key:
+ return True
+
+ return False
+
+ def hasKeyList(self, keyList):
+ if keyList is None:
+ return False
+
+ for k in keyList:
+ if self.hasKey(k):
+ return True
+
+ return False
+
+ def getAttributeList(self):
+ firstLine = self.getContent().getFirstLine()
+ splitArray = firstLine.split()
+ return splitArray[1:(len(splitArray) - 1)]
+
+ def getAttributes(self):
+ return string.join(self.getAttributeList())
+
+ def getSimpleString(self):
+ return "%s %s->%s" % (self.getType(), self.getClassName(), self.getName())
+
+ def toString(self):
+ if self.getPreContentStr() is not None:
+ if self.getContentStr() is not None:
+ return "%s\n%s" % (self.getPreContentStr(), self.getContentStr())
+ else:
+ return self.getPreContentStr()
+ else:
+ if self.getContentStr() is not None:
+ return self.getContentStr()
+ else:
+ return None
+
+ def out(self, outdir, sName):
+ entryStr = self.toString()
+ if entryStr is not None:
+ sFile = file("%s/%s.%s" % (outdir, sName, self.getType()), 'a')
+ sFile.write("%s\n" % entryStr)
+ sFile.close()
diff --git a/smaliparser/SmaliEntryFactory.py b/smaliparser/SmaliEntryFactory.py
new file mode 100644
index 0000000..e7e2f09
--- /dev/null
+++ b/smaliparser/SmaliEntryFactory.py
@@ -0,0 +1,28 @@
+'''
+Created on Feb 27, 2014
+
+@author: tangliuxiang
+'''
+
+import SmaliEntry
+import SmaliMethod
+import SmaliField
+import SmaliClass
+
+def newSmaliEntry(type, content, clsName = None, preContent = None):
+ #return SmaliEntry.SmaliEntry(type, content, preContent)
+
+ if type is None:
+ return None
+ elif type == SmaliEntry.METHOD:
+ return SmaliMethod.SmaliMethod(type, content, clsName, preContent)
+ elif type == SmaliEntry.FIELD:
+ return SmaliField.SmaliField(type, content, clsName, preContent)
+# elif type is SmaliEntry.ANNOTATION:
+ elif type is SmaliEntry.CLASS:
+ return SmaliClass.SmaliClass(type, content, clsName, preContent)
+# elif type is SmaliEntry.SOURCE:
+# elif type is SmaliEntry.SUPER:
+# elif type is SmaliEntry.IMPLEMENTS:
+ else:
+ return SmaliEntry.SmaliEntry(type, content, clsName, preContent)
\ No newline at end of file
diff --git a/smaliparser/SmaliField.py b/smaliparser/SmaliField.py
new file mode 100644
index 0000000..2553619
--- /dev/null
+++ b/smaliparser/SmaliField.py
@@ -0,0 +1,19 @@
+'''
+Created on Feb 26, 2014
+
+@author: tangliuxiang
+'''
+import SmaliEntry
+
+class SmaliField(SmaliEntry.SmaliEntry):
+ '''
+ classdocs
+ '''
+
+ def getName(self):
+ if self.mName is None:
+ firstLine = self.getContent().getFirstLine()
+ splitArray = firstLine.split(r'=')[0].split()
+ #self.mName = splitArray[len(splitArray) - 1].split(r':')[0]
+ self.mName = splitArray[len(splitArray) - 1]
+ return self.mName
\ No newline at end of file
diff --git a/smaliparser/SmaliFileReplace.py b/smaliparser/SmaliFileReplace.py
new file mode 100644
index 0000000..c52f66a
--- /dev/null
+++ b/smaliparser/SmaliFileReplace.py
@@ -0,0 +1,275 @@
+'''
+Created on Jun 3, 2014
+
+@author: tangliuxiang
+'''
+
+import Smali
+import SmaliEntry
+import SAutoCom
+import os
+import utils
+import LibUtils
+import Content
+import FormatSmaliLib
+
+class FileReplace(utils.precheck):
+ mASLib = LibUtils.getSmaliLib(utils.AOSP)
+
+ def __init__(self, withCheck):
+ self.outMap = {}
+ self.curSuccess = True
+ self.curAction = ""
+ self.mWithCheck = withCheck
+ self.curClassName = ""
+ self.init()
+ self.curSrcLib = None
+ self.curDstLib = None
+
+
+ def fail(self, dstLib, entry, failEntryList, tobosp = False):
+ if self.curSuccess:
+ utils.SLog.fail(">>>> Failed on %s" %(self.curAction))
+ utils.SLog.fail("")
+ if tobosp:
+ utils.SLog.fail(" >> Failed to replace %s %s in %s to bosp...." %(entry.getType(), entry.getName(), entry.getClassName()))
+ else:
+ utils.SLog.fail(" >> Failed to keep the %s %s in %s which was added by vendor!" %(entry.getType(), entry.getName(), entry.getClassName()))
+ self.curSuccess = False
+ utils.SLog.fail("")
+ utils.SLog.fail(" >> can not replace %s %s in %s" %(entry.getType(), entry.getName(), dstLib.getSmali(entry.getClassName()).getPath()))
+ for fEntry in failEntryList:
+ utils.SLog.fail(" %s %s in %s" %(fEntry.getType(), fEntry.getName(), dstLib.getSmali(fEntry.getClassName()).getPath()))
+
+ def __replaceOneFile__(self, srcSmali, dstSmali):
+ utils.SLog.i("\n>>>> Try replace %s to %s" %(dstSmali.getPath(), srcSmali.getPath()))
+ #dstLib = LibUtils.getOwnLib(dstSmali.getPath())
+ #srcLib = LibUtils.getOwnLib(srcSmali.getPath())
+
+ clsName = srcSmali.getClassName()
+ self.curClassName = clsName
+ srcSmali = self.curSrcLib.getFormatSmali(clsName)
+ dstSmali = self.curDstLib.getFormatSmali(clsName)
+
+ for entry in srcSmali.getEntryList(SmaliEntry.FIELD):
+ if not dstSmali.hasField(entry.getName()):
+ dstSmali.addEntry(entry)
+
+ for entry in srcSmali.getEntryList(SmaliEntry.METHOD):
+ (canReplaceEntry, canNotReplaceEntry) = self.curDstLib.getCanReplaceEntry(self.curSrcLib, clsName, [entry], False)
+ if len(canNotReplaceEntry) > 0:
+ self.fail(self.curDstLib, entry, canNotReplaceEntry, True)
+ return False
+
+ utils.annotation.disable()
+ for entry in dstSmali.getEntryList(SmaliEntry.FIELD):
+ if not srcSmali.hasField(entry.getName()) and utils.precheck.canAddField(entry):
+ self.curSrcLib.replaceEntry(self.curDstLib, entry, True, False)
+
+ self.curDstLib.setSmali(clsName, srcSmali)
+ nDstLib = FormatSmaliLib.FormatSmaliLib(LibUtils.getLibPath(dstSmali.getPath()))
+ noError = True
+ for dEntry in dstSmali.getEntryList():
+ if not srcSmali.hasEntry(dEntry.getType(), dEntry.getName()):
+ if dEntry.getType() == SmaliEntry.METHOD:
+ if not self.mWithCheck:
+ self.curDstLib.replaceEntry(nDstLib, dEntry, True, False)
+ continue
+
+ (canReplaceEntry, canNotReplaceEntry) = self.curDstLib.getCanReplaceEntry(nDstLib, clsName, [dEntry], False)
+ if len(canNotReplaceEntry) > 0:
+ self.fail(self.curDstLib, dEntry, canNotReplaceEntry)
+ noError = False
+ else:
+ for entry in canReplaceEntry:
+ self.curDstLib.replaceEntry(nDstLib, entry, True, False)
+
+ utils.annotation.enable()
+
+ # add precontent
+ if noError:
+ nDstSmali = Smali.Smali(dstSmali.getPath())
+ for entry in Smali.Smali(srcSmali.getPath()).getEntryList():
+ if nDstSmali.hasEntry(entry.getType(), entry.getName()):
+ dEntry = dstSmali.getEntry(entry.getType(), entry.getName())
+ if dEntry.getContentStr() != entry.getContentStr():
+ self.__setPreContent__(srcSmali, entry, utils.annotation.getReplaceToBospPreContent(entry))
+ else:
+ self.__setPreContent__(srcSmali, entry, utils.annotation.getAddToBospPreContent(entry))
+ srcSmali.setDefaultOutPath(dstSmali.getPath())
+ self.outMap[srcSmali] = dstSmali
+
+ return noError
+
+ @staticmethod
+ def __setPreContent__(smali, entry, nPreContentStr):
+ nPreContent = Content.Content(entry.getPreContentStr())
+ nPreContent.append(nPreContentStr)
+
+ nEntry = smali.getEntry(entry.getType(), entry.getName())
+ nEntry.setPreContent(nPreContent)
+
+ smali.modify()
+
+ def replace(self, src, dst):
+ srcSmali = Smali.Smali(src)
+ dstSmali = Smali.Smali(dst)
+
+ if utils.precheck.shouldIgnore(srcSmali):
+ utils.SLog.fail(">>>> Failed to replace %s to %s" %(dst, src))
+ return False
+
+ self.curSrcLib = LibUtils.getOwnLib(src)
+ self.curDstLib = LibUtils.getOwnLib(dst)
+
+ self.curSrcLib.cleanModify()
+ self.curDstLib.cleanModify()
+
+ srcSmali = self.curSrcLib.getFormatSmali(srcSmali.getClassName())
+ dstSmali = self.curDstLib.getFormatSmali(dstSmali.getClassName())
+
+ srcMemberSmaliList = srcSmali.getMemberSmaliList()
+ dstMemberSmaliList = dstSmali.getMemberSmaliList()
+
+ success = True
+ self.outMap.clear()
+ self.curSuccess = True
+ self.curAction = "replace %s to %s" %(dst, src)
+
+ nDstLib = FormatSmaliLib.FormatSmaliLib(LibUtils.getLibPath(dstSmali.getPath()))
+
+ srcMemberSmaliList.append(srcSmali)
+ dstMemberSmaliList.append(dstSmali)
+
+ for sMem in srcMemberSmaliList:
+ dstPath = '%s/%s' %(os.path.dirname(dst), os.path.basename(sMem.getPath()))
+ utils.SLog.d("DSTPATH: %s" %dstPath)
+ self.curDstLib.setSmali(sMem.getClassName(), sMem)
+ sMem.setDefaultOutPath(dstPath)
+
+ utils.annotation.disable()
+ for sMem in srcMemberSmaliList:
+ for dMem in dstMemberSmaliList:
+ if dMem.getClassName() == sMem.getClassName():
+ for dEntry in dMem.getEntryList():
+ if not sMem.hasEntry(dEntry.getType(), dEntry.getName()):
+ if self.mWithCheck:
+ if dEntry.getType() == SmaliEntry.METHOD:
+ utils.SLog.d("Try to replace method %s in %s" %(dEntry.getName(), dEntry.getClassName()))
+ if not dEntry.hasKey(utils.KEY_PRIVATE) and self.curDstLib.isMethodUsed(FileReplace.mASLib, sMem, dEntry.getName()):
+ (canReplaceEntry, canNotReplaceEntry) = self.curDstLib.getCanReplaceEntry(nDstLib, dMem.getClassName(), [dEntry], False)
+ if len(canNotReplaceEntry) > 0:
+ self.fail(self.curDstLib, dEntry, canNotReplaceEntry)
+ return False
+ else:
+ for entry in canReplaceEntry:
+ self.curDstLib.replaceEntry(nDstLib, entry, True, False)
+ elif dEntry.getType() == SmaliEntry.FIELD:
+ if utils.precheck.canAddField(dEntry):
+ self.curDstLib.replaceEntry(nDstLib, dEntry, True, False)
+ elif dEntry.hasKey(utils.KEY_PUBLIC):
+ self.fail(self.curDstLib, dEntry, canNotReplaceEntry)
+ return False
+ else:
+ self.curDstLib.replaceEntry(nDstLib, dEntry, True, False)
+ else:
+ sEntry = sMem.getEntry(dEntry.getType(), dEntry.getName())
+ if self.mWithCheck and sEntry.getType() == SmaliEntry.METHOD:
+ (canReplaceEntry, canNotReplaceEntry) = self.curDstLib.getCanReplaceEntry(self.curDstLib, dMem.getClassName(), [sEntry], False)
+ if len(canNotReplaceEntry) > 0:
+ self.fail(self.curDstLib, sEntry, canNotReplaceEntry)
+ return False
+
+ utils.annotation.enable()
+ if success:
+ for sMem in srcMemberSmaliList:
+ for dMem in dstMemberSmaliList:
+ if dMem.getClassName() == sMem.getClassName():
+ nDstSmali = Smali.Smali(dMem.getPath())
+ for sEntry in Smali.Smali(sMem.getPath()).getEntryList():
+ if nDstSmali.hasEntry(sEntry.getType(), sEntry.getName()):
+ dEntry = nDstSmali.getEntry(sEntry.getType(), sEntry.getName())
+ if dEntry.getContentStr() != sEntry.getContentStr():
+ self.__setPreContent__(sMem, sMem.getEntry(sEntry.getType(), sEntry.getName()), utils.annotation.getReplaceToBospPreContent(sEntry))
+ else:
+ self.__setPreContent__(sMem, sMem.getEntry(sEntry.getType(), sEntry.getName()), utils.annotation.getAddToBospPreContent(sEntry))
+
+ self.curDstLib.out()
+ utils.SLog.ok(">>>> SUCCESS: replace %s to %s" %(dst, src))
+ else:
+ utils.SLog.fail(">>>> Failed to replace %s to %s" %(dst, src))
+ return success
+
+ def precheck(self, tSmali, bSmali, entry):
+ if tSmali is not None:
+ if tSmali.getClassName() == self.curClassName:
+ return True
+
+ if tSmali is not None \
+ and bSmali is not None \
+ and entry.getType() == SmaliEntry.METHOD:
+ if entry.isConstructor():
+ for field in tSmali.getEntryList(SmaliEntry.FIELD):
+ if not bSmali.hasEntry(SmaliEntry.FIELD, field.getName()):
+ return False
+ elif entry.getSimpleName() == "onTransact":
+ utils.SLog.e(">>> onTransact is an binder interface, it shouldn't be replace, if you want to replace, find the stub and proxy, replace them all!")
+ utils.SLog.e(" such as if you want replace method in IAlarmManager$Stub.smali")
+ utils.SLog.e(" you better use smalitobosp to replace the IAlarmManager$Stub.smali and IAlarmManager$Stub$Proxy.smali both!")
+ return False
+ return True
+
+ def init(self):
+ utils.precheck.setInstance(self)
+
+ def __exit__(self):
+ LibUtils.undoFormat()
+
+ @staticmethod
+ def stb_withoutcheck(src, dst):
+ srcSmali = Smali.Smali(src)
+ dstSmali = Smali.Smali(dst)
+
+ if utils.precheck.shouldIgnore(srcSmali):
+ utils.SLog.fail(">>>> Failed to replace %s to %s" % (dst, src))
+ return False
+
+ curSrcLib = LibUtils.getOwnLib(src)
+ curDstLib = LibUtils.getOwnLib(dst)
+
+ srcSmali = curSrcLib.getFormatSmali(srcSmali.getClassName())
+ dstSmali = curDstLib.getFormatSmali(dstSmali.getClassName())
+
+ srcMemberSmaliList = srcSmali.getMemberSmaliList()
+ dstMemberSmaliList = dstSmali.getMemberSmaliList()
+
+ srcMemberSmaliList.append(srcSmali)
+ dstMemberSmaliList.append(dstSmali)
+
+ for dMem in dstMemberSmaliList:
+ os.remove(dMem.getPath())
+
+ for sMem in srcMemberSmaliList:
+ sMem = curSrcLib.getFormatSmali(sMem.getClassName())
+ sMem.out(os.path.join(os.path.dirname(dst), os.path.basename(sMem.getPath())))
+
+SMALITOBOSP_ADVICE = "%s/help/smalitobosp_advice" %os.path.dirname(os.path.abspath(__file__))
+SMALITOBOSP_SUCCESS = "%s/help/smalitobosp_success" %os.path.dirname(os.path.abspath(__file__))
+
+def smalitobosp(args, withCheck = True):
+ fReplace = FileReplace(withCheck)
+
+ for smaliFile in args:
+ src = utils.getMatchFile(smaliFile, utils.BOSP)
+ dst = utils.getMatchFile(smaliFile, utils.TARGET)
+
+ if withCheck:
+ if fReplace.replace(src, dst) is False:
+ raise
+ else:
+ fReplace.stb_withoutcheck(src, dst)
+ utils.SLog.setAdviceStr(file(SMALITOBOSP_ADVICE).read())
+ utils.SLog.setSuccessStr(file(SMALITOBOSP_SUCCESS).read())
+ #utils.SLog.conclude()
+ fReplace.__exit__()
+
diff --git a/smaliparser/SmaliLib.py b/smaliparser/SmaliLib.py
new file mode 100644
index 0000000..17a7b39
--- /dev/null
+++ b/smaliparser/SmaliLib.py
@@ -0,0 +1,553 @@
+'''
+Created on Feb 28, 2014
+
+@author: tangliuxiang
+'''
+
+import Smali
+import SmaliEntry
+import string
+import os
+import utils
+import sys
+import SmaliMethod
+import traceback
+
+MAX_CHECK_INVOKE_DEEP = 50
+
+class SmaliLib(object):
+ '''
+ classdocs
+ '''
+ def __init__(self, libPath, smaliDirMaxDepth=0):
+ '''
+ Constructor
+ '''
+ self.mSDict = utils.getSmaliDict(libPath, smaliDirMaxDepth)
+ self.mCalledMethods = None
+ self.mSmaliRoot = None
+ self.mAlreadyParsedInvoke = False
+ self.mPath = libPath
+ self.mFieldFormatMap = {}
+ self.mSmaliFileList = None
+ self.mSmaliDirMaxDepth = smaliDirMaxDepth
+
+ def cleanModify(self):
+ for cls in self.mSDict.keys():
+ self.mSDict[cls].cleanModify()
+
+ def replaceEntry(self, srcLib, entry, printOut=True, out=True, noAdd=False):
+ clsName = entry.getClassName()
+ type = entry.getType()
+ name = entry.getName()
+
+ srcSmali = srcLib.getFormatSmali(clsName)
+ dstSmali = self.getFormatSmali(clsName)
+
+ srcEntry = srcSmali.getEntry(type, name)
+ if dstSmali.hasEntry(type, name):
+ dstEntry = dstSmali.getEntry(type, name)
+ if not dstEntry.getContentStr() == srcEntry.getContentStr():
+ if (srcEntry.getName() == 'needNewResources(II)Z'):
+ print srcEntry.getPreContentStr()
+ dstSmali.replaceEntry(srcEntry, utils.annotation.getReplaceToBospPreContent(entry))
+ if (srcEntry.getName() == 'needNewResources(II)Z'):
+ print srcEntry.getPreContentStr()
+ #if printOut:
+ if True:
+ utils.SLog.ok(" Replace %s %s from %s to %s" % (type, name, srcSmali.getPath(), dstSmali.getPath()))
+ elif not noAdd:
+ idx = srcSmali.getIndex(srcEntry) - 1
+ bospEntryList = srcSmali.getEntryList()
+ while idx >= 0 and not dstSmali.hasEntry(bospEntryList[idx].getType(), bospEntryList[idx].getName()) :
+ idx = idx - 1
+
+ if idx >= 0:
+ idx = dstSmali.getIndex(dstSmali.getEntry(bospEntryList[idx].getType(), bospEntryList[idx].getName())) + 1
+
+ if printOut:
+ utils.SLog.ok(" Add %s %s from %s to %s" % (type, name, srcSmali.getPath(), dstSmali.getPath()))
+ dstSmali.addEntry(srcEntry, idx, utils.annotation.getAddToBospPreContent(entry))
+ if out:
+ dstSmali.out()
+ return dstSmali
+
+ def out(self):
+ for cls in self.mSDict.keys():
+ smali = self.mSDict.get(cls)
+ if smali.isModifed():
+ smali.out()
+
+ def getSmaliFileList(self):
+ if self.mSmaliFileList is None:
+ self.mSmaliFileList = []
+ for clsName in self.mSDict.keys():
+ self.mSmaliFileList.append(self.getSmali(clsName).getPath())
+ return self.mSmaliFileList
+
+ def getPath(self):
+ return self.mPath
+
+ def getSmali(self, className):
+ if self.mSDict.has_key(className):
+ return self.mSDict[className]
+
+ return None
+
+ def setSmali(self, className, smali):
+ self.mSDict[className] = smali
+
+ def getSuperSmali(self, child):
+ return self.getSmali(child.getSuperClassName())
+
+ def generateSmaliTree(self):
+ if self.mSmaliRoot is None:
+ self.mSmaliRoot = self.getSmali("Ljava/lang/Object;")
+ for clsName in self.mSDict.keys():
+ smali = self.getSmali(clsName)
+ superClsName = smali.getSuperClassName()
+ superSmali = self.getSmali(superClsName)
+ if superSmali is not None:
+ superSmali.addChild(smali.getClassName())
+
+ def getInheritMethods(self, child, filterInList=None, filterOutList=None):
+ methodsList = []
+
+ for clsName in child.getSuperAndImplementsClassName():
+ superSmali = self.getSmali(clsName)
+ if superSmali is None:
+ utils.SLog.d("can not get %s's super class: %s" % (child.getClassName(), clsName))
+ continue
+
+ methodsList.extend(self.getInheritMethods(superSmali, filterInList, filterOutList))
+ methodsList.extend(superSmali.getEntryList(SmaliEntry.METHOD, filterInList, filterOutList))
+
+ return methodsList
+
+ def getAllMethods(self, smali):
+ superFilterOutList = [utils.KEY_PRIVATE]
+ if not smali.isInterface() and not smali.isAbstractClass():
+ superFilterOutList.append(utils.KEY_ABSTRACT)
+ methodsList = self.getInheritMethods(smali, None, superFilterOutList)
+ methodsList.extend(smali.getEntryList(SmaliEntry.METHOD))
+
+ return methodsList
+
+ def getAllFields(self, smali):
+ fieldsList = smali.getEntryList(SmaliEntry.FIELD)
+ for clsName in self.getAllFathers(smali):
+ cSmali = self.getSmali(clsName)
+ if cSmali is not None and not cSmali.isInterface():
+ fieldsList.extend(cSmali.getEntryList(SmaliEntry.FIELD, None, [utils.KEY_PRIVATE]))
+ return fieldsList
+
+ def getNeedOverrideMethods(self, smali):
+ overrideMethodsList = []
+
+ superClsNameList = smali.getEntryNameList(SmaliEntry.IMPLEMENTS)
+ superClsName = smali.getSuperClassName()
+
+ if superClsName is not None:
+ superClsNameList.append(superClsName)
+
+ for clsName in superClsNameList:
+ superSmali = self.getSmali(clsName)
+ assert superSmali is not None, "Error: can not find class: %s" % (clsName)
+
+ overrideMethodsList.extend(self.getNeedOverrideMethods(superSmali))
+ overrideMethodsList.extend(superSmali.getAbstractMethodsNameList())
+
+ return overrideMethodsList
+
+ def __parseAllInvoke(self):
+ if self.mAlreadyParsedInvoke:
+ return
+
+ self.generateSmaliTree()
+ for clsName in self.mSDict.keys():
+ smali = self.mSDict[clsName]
+ for invokeItem in smali.getInvokeMethods():
+ cSmali = self.getSmali(invokeItem.cls)
+ if cSmali is not None:
+ cSmali.wasInvoke(invokeItem)
+ self.mAlreadyParsedInvoke = True
+
+ def getCalledMethod(self, smali):
+ self.__parseAllInvoke()
+ cSmali = self.getSmali(smali.getClassName())
+ if cSmali is not None:
+ return cSmali.getWasInvokeList()
+ else:
+ utils.SLog.d("can not get class %s from smali lib" % (smali.getClassName()))
+ return []
+
+ def getAllFathers(self, smali, allowDuplicate = False):
+ if smali is None:
+ return []
+ allFathersList = []
+ for clsName in smali.getSuperAndImplementsClassName():
+ cSmali = self.getSmali(clsName)
+ if cSmali is not None:
+ allFathersList.append(clsName)
+
+ for clsName in smali.getSuperAndImplementsClassName():
+ cSmali = self.getSmali(clsName)
+ if cSmali is not None:
+ allFathersList.extend(self.getAllFathers(cSmali))
+
+ if allowDuplicate:
+ return allFathersList
+ else:
+ return list(set(allFathersList))
+
+ def getAbstractMethodsNameList(self, smali):
+ absMethodsNameList = smali.getAbstractMethodsNameList()
+ for clsName in self.getAllFathers(smali):
+ cSmali = self.getSmali(clsName)
+ absMethodsNameList.append(cSmali.getAbstractMethodsNameList())
+ return list(set(absMethodsNameList))
+
+ def __getSuperCalledMethods__new(self, smali):
+ superCalledMethods = {}
+ for clsName in self.getAllFathers(smali):
+ cSmali = self.getSmali(clsName)
+ if cSmali.isInterface():
+ if superCalledMethods.has_key(clsName):
+ superCalledMethods[clsName] = dict(superCalledMethods[clsName], **self.getCalledMethod(cSmali))
+ else:
+ superCalledMethods[clsName] = self.getCalledMethod(cSmali)
+ elif cSmali.isAbstractClass():
+ absMethodsNameList = cSmali.getAbstractMethodsNameList()
+ sCalledMethods = self.getCalledMethod(cSmali)
+
+ for method in sCalledMethods.keys():
+ for absMethodName in absMethodsNameList:
+ if method == absMethodName:
+ if not superCalledMethods.has_key(cSmali):
+ superCalledMethods[cSmali] = {}
+ superCalledMethods[cSmali][method] = sCalledMethods[method]
+ break
+ return superCalledMethods
+
+ def __getSuperCalledMethods__(self, smali):
+ superCalledMethods = {}
+
+ superClsName = smali.getSuperClassName()
+ if superClsName is not None and self.getSmali(superClsName) is not None:
+ superSmali = self.getSmali(superClsName)
+ sCalledMethods = self.getCalledMethod(superSmali)
+ absMethodsNameList = superSmali.getAbstractMethodsNameList()
+ superCalledMethods = self.__getSuperCalledMethods__(superSmali)
+
+ for method in sCalledMethods.keys():
+ for absMethodName in absMethodsNameList:
+ if method == absMethodName:
+ if not superCalledMethods.has_key(superClsName):
+ superCalledMethods[superClsName] = {}
+ superCalledMethods[superClsName][method] = sCalledMethods[method]
+ break
+
+ for clsName in smali.getImplementClassList():
+ superSmali = self.getSmali(clsName)
+ if superSmali is not None:
+ assert superSmali.isAbstractClass() and superSmali.isInterface(), "Error: class %s was implement by %s, it should be interface!" % (clsName, smali.getClassName())
+ if superCalledMethods.has_key(clsName):
+ superCalledMethods[clsName] = dict(superCalledMethods[clsName], **self.getCalledMethod(superSmali))
+ else:
+ superCalledMethods[clsName] = self.getCalledMethod(superSmali)
+ return superCalledMethods
+
+ def __dict(self, dict1, dict2):
+ newDict = dict1.copy()
+ for key in dict2.keys():
+ if newDict.has_key(key):
+ newDict[key].extend(dict2[key])
+ else:
+ newDict[key] = dict2[key]
+ return newDict
+
+ def __getSelfAndSuperCalledMethods__(self, smali):
+ allCalledMethods = {}
+ allCalledMethods[smali.getClassName()] = self.getCalledMethod(smali)
+ if not smali.isInterface() and not smali.isAbstractClass():
+ allCalledMethods = self.__dict(allCalledMethods, self.__getSuperCalledMethods__new(smali))
+ return allCalledMethods
+
+ def __getUnImplementMethods__(self, smali, selfMethods):
+ usedMethodsList = {}
+ allCalledMethodDict = self.__getSelfAndSuperCalledMethods__(smali)
+
+ for clsName in allCalledMethodDict.keys():
+ for method in allCalledMethodDict[clsName]:
+ hasMethod = False
+ for sMethod in selfMethods:
+ if method == sMethod.getName():
+ hasMethod = True
+ break;
+ if hasMethod is False:
+ if not usedMethodsList.has_key(method):
+ usedMethodsList[method] = []
+ usedMethodsList[method].extend(allCalledMethodDict[clsName][method])
+
+ return usedMethodsList
+
+ def getUsedMethods(self, smali, selfMethods=None):
+ if selfMethods is None:
+ selfMethods = self.getAllMethods(smali)
+ usedMethodsList = {}
+ allCalledMethodDict = self.__getSelfAndSuperCalledMethods__(smali)
+ for method in selfMethods:
+ for clsName in allCalledMethodDict.keys():
+ for methodName in allCalledMethodDict[clsName]:
+ if methodName == method.getName():
+ if not usedMethodsList.has_key(methodName):
+ usedMethodsList[methodName] = []
+ usedMethodsList[methodName].extend(allCalledMethodDict[clsName][methodName])
+ break
+
+ if usedMethodsList.has_key(method.getName()) and \
+ len(usedMethodsList[method.getName()]) >= Smali.MAX_INVOKE_LEN:
+ break
+
+ return usedMethodsList
+
+ def getBelongClass(self, smali, type, name):
+ if type != SmaliEntry.FIELD:
+ utils.SLog.e("not support getBelongClass for type: %s" % type)
+ return None
+
+ if smali.hasEntry(type, name):
+ return smali.getClassName()
+
+ allClsName = self.getAllFathers(smali, True)
+ for clsName in allClsName:
+ cSmali = self.getSmali(clsName)
+ assert cSmali is not None, "Error: can not get class from: %s" % (self.getPath())
+ if cSmali.hasEntry(type, name):
+ return cSmali.getClassName()
+ return None
+
+ def checkMethods(self, smali):
+# if smali.isAbstractClass() or smali.isInterface():
+# return
+
+ allCalledMethods = self.getCalledMethod(smali)
+ if not smali.isInterface() and not smali.isAbstractClass():
+ mergedDict = self.__dict(allCalledMethods, self.__getSuperCalledMethods__(smali))
+ allCalledMethods = mergedDict
+
+ for key in allCalledMethods.keys():
+ splitArray = key.split(':')
+ assert len(splitArray) == 2, "Life is hard...."
+ type = splitArray[0]
+ method = splitArray[1]
+
+ hasMethod = False
+ for sMethod in self.getAllMethods(smali):
+ if method == sMethod.getName():
+ hasMethod = True
+ break;
+ if hasMethod is False:
+ utils.SLog.e("%s doesn't has method %s, was called by: %s" % (smali.getClassName(), method, string.join(allCalledMethods[key])))
+
+ def formatUsingField(self, smali):
+ if smali is None:
+ return
+
+ usedOutsideFields = smali.getUsedOutsideFields()
+
+ if usedOutsideFields is not None and len(usedOutsideFields) > 0:
+ for usedFieldsItem in usedOutsideFields:
+ key = r'%s->%s' % (usedFieldsItem.cls, usedFieldsItem.field)
+ uSmali = self.getSmali(usedFieldsItem.cls)
+ if uSmali is not None and not self.mFieldFormatMap.has_key(key):
+ belongCls = self.getBelongClass(uSmali, SmaliEntry.FIELD, usedFieldsItem.field)
+ if belongCls is not None and belongCls != uSmali.getClassName():
+ targetKey = r'%s->%s' % (belongCls, usedFieldsItem.field)
+ utils.SLog.d("change %s to %s" % (key, targetKey))
+ self.mFieldFormatMap[key] = targetKey
+ if smali.formatUsingField(self.mFieldFormatMap):
+ smali.out()
+
+ def undoFormatUsingField(self, smali):
+ if smali is None:
+ return
+
+ if smali.undoFormatUsingField():
+ smali.out()
+
+ def getEntryList(self, oriEntryList):
+ outEntryList = []
+ for entry in oriEntryList:
+ clsName = entry.getClassName()
+ smali = self.getSmali(clsName)
+
+ if smali is not None:
+ nEntry = smali.getEntry(entry.getType(), entry.getName())
+ if nEntry is not None:
+ outEntryList.append(nEntry)
+ return outEntryList
+
+ def getMissedMethods(self, invokeMethods):
+ missedMethods = []
+ for invokeItem in invokeMethods:
+ cSmali = self.getSmali(invokeItem.cls)
+ assert cSmali is not None, "Error: doesn't have class: %s" % invokeItem.cls
+
+ hasMethod = False
+ for mEntry in self.getAllMethods(cSmali):
+ if invokeItem.method == mEntry.getName():
+ hasMethod = True
+ break
+ if not hasMethod:
+ utils.SLog.d("class: %s doesn't have method: %s" % (invokeItem.cls, invokeItem.method))
+ missedMethods.append(invokeItem)
+ return missedMethods
+
+ def getMissedFields(self, usedFields):
+ missedFields = []
+ for usedFieldItem in usedFields:
+ cSmali = self.getSmali(usedFieldItem.cls)
+ assert cSmali is not None, "Error: doesn't have class: %s" % usedFieldItem.cls
+
+ hasField = False
+ for mEntry in self.getAllFields(cSmali):
+ if usedFieldItem.field == mEntry.getName():
+ hasField = True
+ break
+ if not hasField:
+ utils.SLog.d("class: %s doesn't have field: %s" % (usedFieldItem.cls, usedFieldItem.field))
+ missedFields.append(usedFieldItem)
+ return missedFields
+
+ def isMethodUsed(self, asLib, smali, methodName, deep = 0):
+ sMethodEntry = smali.getEntry(SmaliEntry.METHOD, methodName)
+ if sMethodEntry is not None:
+ aSmali = asLib.getSmali(smali.getClassName())
+ if aSmali is not None and aSmali.getEntry(SmaliEntry.METHOD, methodName) is not None:
+ return True
+
+ usedMethodsList = self.getUsedMethods(smali, [sMethodEntry])
+
+ if not usedMethodsList.has_key(methodName):
+ for childClsName in smali.getChildren():
+ cSmali = self.getSmali(childClsName)
+ if cSmali is not None and self.isMethodUsed(asLib, cSmali, methodName):
+ return True
+ else:
+ if len(usedMethodsList) < Smali.MAX_INVOKE_LEN and deep < MAX_CHECK_INVOKE_DEEP:
+ isUsed = False
+ for invokeItem in usedMethodsList[methodName]:
+ cSmali = self.getSmali(invokeItem.belongCls)
+ if cSmali is not None and self.isMethodUsed(asLib, cSmali, invokeItem.belongMethod, deep + 1):
+ isUsed = True
+ break
+ return isUsed
+ else:
+ return True
+ return False
+
+ def getUnImplementMethods(self, asLib, smali):
+ unImplementMethods = []
+ methodsList = self.__getUnImplementMethods__(smali, self.getAllMethods(smali))
+ for key in methodsList.keys():
+ if len(methodsList[key]) >= Smali.MAX_INVOKE_LEN:
+ unImplementMethods.append(key)
+ continue
+
+ for invokeItem in methodsList[key]:
+ cSmali = self.getSmali(invokeItem.belongCls)
+ if cSmali is None:
+ continue
+
+ if self.isMethodUsed(asLib, cSmali, invokeItem.belongMethod):
+ unImplementMethods.append(key)
+ break
+ return unImplementMethods
+
+ # targetLib is which you already merged
+ # sourceLib is which you want get the replace method
+ def getCanReplaceEntry(self, sourceLib, clsName, methodEntryList, allowBlank = True):
+ canReplaceEntryList = []
+ canNotReplaceEntryList = []
+ for mEntry in methodEntryList:
+ methodName = mEntry.getName()
+ utils.SLog.d("Check if can replace method: %s in class: %s" %(methodName, clsName))
+
+ if mEntry is None:
+ utils.SLog.e("Baidu doesn't have this Method: %s" %clsName)
+ continue
+
+ tSmali = self.getSmali(mEntry.getClassName())
+ sSmali = sourceLib.getSmali(mEntry.getClassName())
+ if not utils.preCheck(tSmali, sSmali, mEntry):
+ canNotReplaceEntryList.append(mEntry)
+ continue
+
+ try:
+ missedFields = self.getMissedFields(mEntry.getUsedFields())
+ canIgnore = True
+ outMissedFieldsList = []
+ for usedField in missedFields:
+ cSmali = sourceLib.getSmali(usedField.cls)
+ if cSmali is None:
+ canIgnore = False
+ break
+
+ fieldEntry = cSmali.getEntry(SmaliEntry.FIELD, usedField.field)
+ if fieldEntry is None or (not SmaliMethod.isPutUseField(usedField) and not utils.precheck.canAddField(fieldEntry)):
+ canIgnore = False
+ break
+ else:
+ outMissedFieldsList.append(fieldEntry)
+ if canIgnore:
+ canReplaceEntryList.extend(outMissedFieldsList)
+ else:
+ canNotReplaceEntryList.append(mEntry)
+ continue
+ except Exception as e:
+ utils.SLog.d(e)
+ canNotReplaceEntryList.append(mEntry)
+ utils.SLog.d("can not replace method: %s in class: %s" %(methodName, clsName))
+ continue
+
+ try:
+ missedMethods = self.getMissedMethods(mEntry.getInvokeMethods())
+ missedMethodDict = {}
+ for invokeItem in missedMethods:
+ if not missedMethodDict.has_key(invokeItem.cls):
+ missedMethodDict[invokeItem.cls] = []
+ missedMethodDict[invokeItem.cls].append(invokeItem.method)
+
+ missedClass = False
+ for cls in missedMethodDict.keys():
+ cSmali = sourceLib.getSmali(cls)
+ if cSmali is None:
+ canIgnore = False
+ missedClass = True
+ canNotReplaceEntryList.append(mEntry)
+ break
+
+ entryList = cSmali.getEntryListByNameList(SmaliEntry.METHOD, missedMethodDict[cls])
+ (cEntryList, nEntryList) = self.getCanReplaceEntry(sourceLib, cls, entryList, allowBlank)
+ if len(nEntryList) > 0:
+ canIgnore = False
+ canReplaceEntryList.extend(cEntryList)
+ canNotReplaceEntryList.extend(nEntryList)
+ if not missedClass and (canIgnore or allowBlank):
+ canReplaceEntryList.append(mEntry)
+ else:
+ canNotReplaceEntryList.append(mEntry)
+ missedMethodDict.clear()
+ except Exception as e:
+ utils.SLog.d(e)
+ canNotReplaceEntryList.append(mEntry)
+ utils.SLog.d("can not replace method: %s in class: %s" %(methodName, clsName))
+ continue
+
+ return list(set(canReplaceEntryList)), list(set(canNotReplaceEntryList))
+def __getSplitItem__(string, sep, idx=0):
+ splitArray = string.split(sep)
+ assert len(splitArray) > idx, "Life is hard...."
+ return splitArray[idx]
+
diff --git a/smaliparser/SmaliLine.py b/smaliparser/SmaliLine.py
new file mode 100644
index 0000000..960764d
--- /dev/null
+++ b/smaliparser/SmaliLine.py
@@ -0,0 +1,62 @@
+'''
+Created on Feb 26, 2014
+
+@author: tangliuxiang
+'''
+import re
+
+blankLineRule = re.compile(r'^ *#.*$|^ *$')
+entryStartRule = re.compile(r'^\..*$')
+
+def getLineType(line):
+ lineType = SmaliLine.TYPE_NORMAL_LINE
+ if blankLineRule.match(line) is not None:
+ lineType = SmaliLine.TYPE_BLANK_LINE
+ elif entryStartRule.match(line) is not None:
+ lineType = SmaliLine.TYPE_DOT_LINE
+
+ return lineType
+
+class SmaliLine(object):
+ '''
+ classdocs
+ '''
+
+ TYPE_NORMAL_LINE = 0
+ TYPE_BLANK_LINE = 1
+ TYPE_DOT_LINE = 2
+
+ def __init__(self, line):
+ '''
+ Constructor
+ '''
+ self.setLine(line)
+
+ def setLine(self, line):
+ self.mLineType = getLineType(line)
+ self.mLine = line
+
+ def getLine(self):
+ return self.mLine
+
+ def getType(self):
+ return self.mLineType
+
+ def isBlank(self):
+ return self.mLineType is SmaliLine.TYPE_BLANK_LINE
+
+ def getDotType(self):
+ if self.mLine is None and self.mLineType is not SmaliLine.TYPE_DOT_LINE:
+ return None
+ lstr = re.sub(r'^\.end *', '', self.mLine)
+ lstr = re.sub(r'^\.', '', lstr)
+ arr = lstr.split()
+ return arr[0]
+
+ def isDotEnd(self):
+ if self.mLine is None and self.mLineType is not SmaliLine.TYPE_DOT_LINE:
+ return False
+ if re.compile(r'^\.end.*$').match(self.mLine) is None:
+ return False
+ else:
+ return True
diff --git a/smaliparser/SmaliMethod.py b/smaliparser/SmaliMethod.py
new file mode 100644
index 0000000..e5baf78
--- /dev/null
+++ b/smaliparser/SmaliMethod.py
@@ -0,0 +1,123 @@
+#!/usr/bin/env python
+
+'''
+Created on 2012-12-11
+
+@author: tangliuxiang
+'''
+
+import re
+import sys
+import os
+import utils
+
+from SmaliEntry import SmaliEntry
+
+INVOKE_DIRECT = "invoke-direct"
+INVOKE_INTERFACE = "invoke-interface"
+INVOKE_STATIC = "invoke-static"
+INVOKE_SUPER = "invoke-super"
+INVOKE_VIRTUAL = "invoke-virtual"
+
+INVOKE_RE = re.compile("^ *invoke-.*$", re.M)
+USE_FIELD_RE = re.compile("^ *iget.*$|^ *iput.*$|^ *sget.*$|^ *sput.*$", re.M)
+PUT_FIELD_RE = re.compile("iput.*|sput.*")
+
+class Invoke(object): pass
+class UsedField(object): pass
+
+def isPutUseField(usedField):
+ if PUT_FIELD_RE.match(usedField.type) is not None:
+ return True
+ return False
+
+class SmaliMethod(SmaliEntry):
+ '''
+ classdocs
+ '''
+ def __init__(self, type, content, clsName, preContent=None):
+ super(SmaliMethod, self).__init__(type, content, clsName, preContent)
+ self.mInvokeMethods = None
+ self.mUsedFields = None
+
+ def __getInvokeMethods__(self):
+ invokeMethodsList = []
+ for line in self.getContentStr().split('\n'):
+ if INVOKE_RE.match(line) is not None:
+ splitArray = line.split()
+ invokeItem = Invoke()
+ assert len(splitArray) >= 2, "Wrong invoke: %s" %line
+ invokeItem.type = splitArray[0].split(r'/')[0]
+
+ splitArrayNew = splitArray[len(splitArray) - 1].split('->')
+ assert len(splitArrayNew) == 2, "Wrong invoke: %s" %line
+
+ if splitArrayNew[0][0] == r'[':
+ #print ">>> ignore cls: %s" % splitArrayNew[0]
+ continue
+
+ invokeItem.cls = splitArrayNew[0]
+ invokeItem.method = splitArrayNew[1]
+ invokeItem.belongMethod = self.getName()
+ invokeItem.belongCls = self.mClsName
+
+ invokeMethodsList.append(invokeItem)
+ return list(set(invokeMethodsList))
+
+ def getInvokeMethods(self):
+ if self.mInvokeMethods is None:
+ self.mInvokeMethods = self.__getInvokeMethods__()
+
+ return self.mInvokeMethods
+
+ def __getUsedFields__(self):
+ usedFieldsList = []
+ for line in self.getContentStr().split('\n'):
+ if USE_FIELD_RE.match(line) is not None:
+ splitArray = line.split()
+ usedField = UsedField()
+ assert len(splitArray) >= 2, "Wrong field get or put: %s" %line
+ usedField.type = splitArray[0].split(r'/')[0]
+
+ splitArrayNew = splitArray[len(splitArray) - 1].split('->')
+ assert len(splitArrayNew) == 2, "Wrong field get or put: %s" %line
+ usedField.cls = splitArrayNew[0]
+ usedField.field = splitArrayNew[1]
+
+ usedFieldsList.append(usedField)
+ return list(set(usedFieldsList))
+
+ def getUsedFields(self):
+ if self.mUsedFields is None:
+ self.mUsedFields = self.__getUsedFields__()
+
+ return self.mUsedFields
+
+ def formatUsingField(self, formatMap):
+ entryStr = self.getContentStr()
+ modifed = False
+ for usedFieldItem in self.getUsedFields():
+ key = r'%s->%s' % (usedFieldItem.cls, usedFieldItem.field)
+ if formatMap.has_key(key):
+ modifed = True
+ entryStr = entryStr.replace(key, formatMap[key])
+ if modifed:
+ self.setContentStr(entryStr)
+ return modifed
+
+ def getReturnType(self):
+ return getReturnType(self.getName())
+
+ def isConstructor(self):
+ return self.hasKey(utils.KEY_CONSTRUCTOR)
+
+ def getSimpleName(self):
+ return self.getName().split('(')[0]
+
+def getReturnType(methodName):
+ splitArray = methodName.split(r')')
+ if len(splitArray) < 2:
+ utils.SLog.w("method %s return nothing!" %methodName)
+ return ""
+ else:
+ return splitArray[-1]
diff --git a/smaliparser/SmaliParser.py b/smaliparser/SmaliParser.py
new file mode 100644
index 0000000..e74c886
--- /dev/null
+++ b/smaliparser/SmaliParser.py
@@ -0,0 +1,167 @@
+'''
+Created on Feb 26, 2014
+
+@author: tangliuxiang
+'''
+
+import os
+import SmaliEntryFactory
+import Smali
+import re
+import utils
+import sys
+
+from Content import Content
+from SmaliLine import SmaliLine
+
+DEBUG = False
+
+class SmaliParser(object):
+ '''
+ classdocs
+ '''
+ STATE_NULL = 0
+ STATE_WAIT_START = 1
+ STATE_WAIT_END = 2
+
+ def __init__(self, smaliFilePath, parseNow = True):
+ '''
+ Constructor
+ '''
+ self.mSmaliFilePath = smaliFilePath
+ self.mParsed = False
+ self.state = SmaliParser.STATE_NULL
+ self.mEntryList = []
+
+ if parseNow:
+ self.parse()
+
+ def parse(self):
+ clsName = utils.getClassFromPath(self.mSmaliFilePath)
+ state = SmaliParser.STATE_WAIT_START;
+ curEntryType = None
+ curEntryContent = Content()
+ curPreContent = Content()
+ entryList = []
+
+ if not os.path.isfile(self.mSmaliFilePath):
+ utils.SLog.w("%s doesn't exist!" %(self.mSmaliFilePath))
+ self.mParsed = True
+ return
+
+ sFile = file(self.mSmaliFilePath)
+ fileLinesList = sFile.readlines()
+ sFile.close()
+ idx = 0
+ while idx < len(fileLinesList):
+ if fileLinesList[idx][-1] == "\n":
+ sLine = SmaliLine(fileLinesList[idx][0:-1])
+ else:
+ sLine = SmaliLine(fileLinesList[idx])
+
+ if sLine.getType() is SmaliLine.TYPE_DOT_LINE:
+ if sLine.isDotEnd():
+ assert state == SmaliParser.STATE_WAIT_END, "wrong end in line: (%s:%s)" % (self.mSmaliFilePath, idx)
+ curEntryContent.append(sLine.getLine())
+ entryList.append(SmaliEntryFactory.newSmaliEntry(curEntryType, curEntryContent, clsName, curPreContent))
+
+ curPreContent = Content()
+ curEntryContent = Content()
+ state = SmaliParser.STATE_WAIT_START
+ else:
+ if state is SmaliParser.STATE_WAIT_START:
+ curEntryType = sLine.getDotType()
+ assert curEntryType is not None, "Life is hard...."
+
+ curEntryContent.setContentStr(sLine.getLine())
+ state = SmaliParser.STATE_WAIT_END
+ else:
+ assert state is SmaliParser.STATE_WAIT_END, "wrong state, Life is hard...."
+ assert not curEntryContent.isMultiLine(), "wrong entry start, expect .end %s (%s:%s)" % (curEntryType, self.mSmaliFilePath, idx)
+
+ postStr = curEntryContent.getPostContent().getContentStr()
+ curEntryContent.setContentStr(curEntryContent.getContentStr().split('\n')[0])
+ entryList.append(SmaliEntryFactory.newSmaliEntry(curEntryType, curEntryContent, clsName, curPreContent))
+
+ curEntryType = sLine.getDotType()
+ assert curEntryType is not None, "Life is hard...."
+
+ curPreContent = Content(postStr)
+ curEntryContent = Content(sLine.getLine())
+
+ else:
+ if state is SmaliParser.STATE_WAIT_START:
+ curPreContent.append(sLine.getLine())
+ else:
+ assert state is SmaliParser.STATE_WAIT_END, "wrong state, Life is hard...."
+ curEntryContent.append(sLine.getLine())
+ idx = idx + 1
+
+ if state is SmaliParser.STATE_WAIT_END \
+ and curEntryType is not None \
+ and curEntryContent.getContentStr() is not None:
+ curEntryContent.setContentStr(curEntryContent.getContentStr().split('\n')[0])
+ entryList.append(SmaliEntryFactory.newSmaliEntry(curEntryType, curEntryContent, clsName, curPreContent))
+
+ self.mEntryList = entryList
+ self.mParsed = True
+
+ def getEntryList(self):
+ if self.mParsed is False:
+ self.parse()
+ return self.mEntryList;
+
+ def removeEntry(self, entry):
+ if self.mParsed is False:
+ self.parse()
+
+ if entry is None:
+ return False
+
+ idx = 0
+ while idx < len(self.mEntryList):
+ if self.mEntryList[idx] == entry:
+ del self.mEntryList[idx]
+ return True
+ idx = idx + 1
+ return False
+
+ def addEntry(self, entry, idx = -1):
+ if self.mParsed is False:
+ self.parse()
+
+ if entry is None:
+ return False
+
+ if idx < 0 or idx >= len(self.mEntryList):
+ self.mEntryList.append(entry)
+ else:
+ self.mEntryList.append(self.mEntryList[len(self.mEntryList) - 1])
+ i = len(self.mEntryList) - 1
+ while i > idx:
+ self.mEntryList[i] = self.mEntryList[i - 1]
+ i = i - 1
+ self.mEntryList[idx] = entry
+ return True
+
+ def getIndex(self, entry):
+ return self.mEntryList.index(entry)
+
+ def replaceEntry(self, entry):
+ if self.mParsed is False:
+ self.parse()
+
+ if entry is None:
+ return False
+
+ idx = 0
+ while idx < len(self.mEntryList):
+ if self.mEntryList[idx].equals(entry):
+ self.mEntryList[idx] = entry
+ return True
+ idx = idx + 1
+
+ self.addEntry(entry)
+ return True
+
+
diff --git a/smaliparser/SmaliSubClass.py b/smaliparser/SmaliSubClass.py
new file mode 100644
index 0000000..528e4d8
--- /dev/null
+++ b/smaliparser/SmaliSubClass.py
@@ -0,0 +1,16 @@
+'''
+Created on 2012-12-11
+
+@author: jock
+'''
+
+class SmaliSubClass(object):
+ '''
+ classdocs
+ '''
+
+
+ def __init__(self):
+ '''
+ Constructor
+ '''
diff --git a/smaliparser/SmaliTest.py b/smaliparser/SmaliTest.py
new file mode 100644
index 0000000..5be9c5e
--- /dev/null
+++ b/smaliparser/SmaliTest.py
@@ -0,0 +1,229 @@
+#!/usr/bin/env python
+'''
+Created on 2012-12-11
+
+@author: jock
+'''
+import SmaliParser
+import os
+import re
+import shutil
+import Smali
+import SmaliLib
+import string
+import SCheck
+import SmaliEntry
+import utils
+
+class SmaliTest(object):
+ '''
+ classdocs
+ '''
+
+ def __init__(self, smaliDir):
+ '''
+ Constructor
+ '''
+ if os.path.isdir("/tmp/what-smali"):
+ shutil.rmtree("/tmp/what-smali")
+ #smaliLib = SmaliLib.SmaliLib(smaliDir)
+ #self.testSmaliLibGetUnImplementMethods("/home/tangliuxiang/work/smali/smali-4.0/devices/t328t/temp/test3/app/Phone/smali/com/android/phone/PhoneInterfaceManager.smali", smaliLib)
+ for smaliFilePath in self.getSmaliFiles(smaliDir, "/tmp/what-smali/"):
+ #self.testSmaliGetSuperClassName(smaliFilePath, "/tmp/what-smali/%s" %(smaliFilePath.replace(smaliDir, '',1)))
+
+ #self.testSmaliLibGetOverrideMethods(smaliFilePath, smaliLib)
+ #self.testSmaliGetInvokeMethods(smaliFilePath, smaliLib)
+ #self.testSamliGetCalledMethod(smaliFilePath, smaliLib)
+ #self.testSmaliLibGetUnImplementMethods(smaliFilePath, smaliLib)
+ self.testSmaliSplit(smaliFilePath, "/tmp/what-smali/%s" %(os.path.dirname(smaliFilePath.replace(smaliDir, '',1))))
+
+
+ def getSmaliFiles(self, inDir, outDir):
+ filelist = []
+ smaliRe = re.compile(r'(?:^.*\.smali$)')
+ os.mkdir(outDir)
+ for root, dirs, files in os.walk(inDir):
+ for fn in files:
+ if bool(smaliRe.match(fn)) is True:
+ filelist.append("%s/%s" % (root, fn))
+ for dir in dirs:
+ absDir = "%s/%s" %(root, dir)
+ odir = "%s/%s" %(outDir, absDir.replace(inDir, '', 1))
+ if os.path.isdir(odir) is False:
+ os.mkdir(odir)
+
+ return filelist
+
+ def testEntryToString(self, smaliFilePath, outFilePath):
+ '''
+ new a smali class from smali file
+ '''
+ print "testEntryToString: %s" %(smaliFilePath)
+
+ entries = SmaliParser.SmaliParser(smaliFilePath).getEntryList()
+ outFile = file(outFilePath, 'w+')
+
+ for entry in entries:
+ #outFile.write("# entry start: type: %s\n%s\n"%(entry.getType(), entry.toString()))
+ outFile.write("%s\n"%(entry.toString()))
+ outFile.close()
+
+
+ def TestSmaliToString(self, smaliFilePath, outFilePath):
+ '''
+ new a smali class from smali file
+ '''
+ print "TestSmaliToString: %s" %(smaliFilePath)
+
+ outFile = file(outFilePath, 'w+')
+ smaliFile = Smali.Smali(smaliFilePath)
+ outFile.write(smaliFile.toString())
+ outFile.close()
+
+ def getAllEntryType(self, smaliFilePath, outFilePath):
+ '''
+ new a smali class from smali file
+ '''
+ print "getAllEntryType: %s" %(smaliFilePath)
+
+ outFile1 = file("/tmp/what-smali/smali-entry-type", 'a')
+ smaliFile = Smali.Smali(smaliFilePath)
+
+ for entry in smaliFile.getEntryList():
+ outFile1.write("%s\n" %entry.getType())
+ outFile1.close()
+
+ outFile = file(outFilePath, 'w+')
+ outFile.write(smaliFile.toString())
+ outFile.close()
+
+ def testEntryOut(self, smaliFilePath, outFilePath):
+ '''
+ new a smali class from smali file
+ '''
+ print "testEntryOut: %s" %(smaliFilePath)
+
+ smaliFile = Smali.Smali(smaliFilePath)
+
+ for entry in smaliFile.getEntryList():
+ entry.out("/tmp/what-smali/", "")
+
+ def testEntryGetName(self, smaliFilePath, outFilePath):
+ print "testSmaliGetName: %s" %(smaliFilePath)
+
+ smaliFile = Smali.Smali(smaliFilePath)
+ outFile = file(outFilePath, 'w+')
+
+ for entry in smaliFile.getEntryList():
+ outFile.write("# entry name: type: %s name:%s\n"%(entry.getType(), entry.getName()))
+ outFile.write("%s\n"%(entry.toString()))
+ outFile.close()
+
+ def testSmaliGetSuperClassName(self, smaliFilePath, outFilePath):
+ smaliFile = Smali.Smali(smaliFilePath)
+ print "testSmaliGetSuper: %s, super: %s" %(smaliFilePath, smaliFile.getSuperClassName())
+
+ def testSmaliLibGetOverrideMethods(self, smaliFilePath, smaliLib):
+ smali = Smali.Smali(smaliFilePath)
+
+ if smali.isAbstractClass() or smali.isInterface():
+ return
+ #print "Overrides in: %s" %smaliFilePath
+
+ sMethods = smaliLib.getAllMethods(smali)
+
+ for overMethod in smaliLib.getNeedOverrideMethods(smali):
+ #print "\t\tMethod: %s" %overMethod
+ hasMethod = False
+ for method in sMethods:
+ if overMethod == method:
+ hasMethod = True
+ break
+ if hasMethod == False:
+ print "%s doesn't implements method: %s" %(smaliFilePath, overMethod)
+
+ def testSmaliGetInvokeMethods(self, smaliFilePath, smaliLib):
+ smali = Smali.Smali(smaliFilePath)
+ for method in smali.getInvokeMethods():
+ print "Invoke methods: type: %s, cls: %s, method: %s" %(method.type, method.cls, method.method)
+
+ def testSamliGetCalledMethod(self, smaliFilePath, smaliLib):
+ smali = Smali.Smali(smaliFilePath)
+
+ print "Smali: %s" %smaliFilePath
+ for method in smaliLib.getCalledMethod(smali):
+ print "method was called: type: %s, cls: %s, method: %s" %(method.type, method.cls, method.method)
+
+ def testSmaliLibCheckMethod(self, smaliFilePath, smaliLib):
+ smali = Smali.Smali(smaliFilePath)
+ #print "Smali: %s" %smaliFilePath
+ smaliLib.checkMethods(smali)
+
+ def testSmaliLibGetUnImplementMethods(self, smaliFilePath, smaliLib):
+ smali = Smali.Smali(smaliFilePath)
+ methodsList = smaliLib.getUnImplementMethods(smali)
+ for method in methodsList:
+ print "%s: %s" %(smali.getClassName(), method)
+
+ def testSmaliSplit(self, smaliFilePath, outFilePath):
+ smali = Smali.Smali(smaliFilePath)
+ smali.split(outFilePath)
+
+def testSCheckGetCanReplaceMethods(vendorDir, aosp, bosp):
+ sCheck = SCheck.SCheck(vendorDir, aosp, bosp)
+ for key in sCheck.mBSLib.mSDict.keys():
+ smali = sCheck.mBSLib.getSmali(key)
+ methodsList = sCheck.getCanReplaceToBoardMethods(smali, smali.getEntryList(SmaliEntry.METHOD))
+ if methodsList is None:
+ continue
+ for method in methodsList:
+ print "%s: %s" %(smali.getClassName(), method.getName())
+
+def testSCheckAutoComplete(vendorDir, aosp, bosp, mergedDir):
+ sCheck = SCheck.SCheck(vendorDir, aosp, bosp, mergedDir)
+
+ for key in sCheck.mMSLib.mSDict.keys():
+ smali = sCheck.mMSLib.getSmali(key)
+ if smali.getPackageName() == r'Lcom/baidu/internal/telephony/sip':
+ continue
+
+# print "packageName: %s, clsName: %s" %(smali.getPackageName(), smali.getClassName())
+ methodsList = sCheck.autoComplete(smali)
+ for method in methodsList:
+ print "%s: %s" %(method.getClassName(), method.getName())
+
+def testSCheckGetUnImplementMethods(vendorDir, aosp, bosp, mergedDir):
+ sCheck = SCheck.SCheck(vendorDir, aosp, bosp, mergedDir)
+
+ for key in sCheck.mMSLib.mSDict.keys():
+ smali = sCheck.mMSLib.getSmali(key)
+ if smali.getPackageName() == r'Lcom/baidu/internal/telephony/sip':
+ continue
+
+# print "packageName: %s, clsName: %s" %(smali.getPackageName(), smali.getClassName())
+ methodsList = sCheck.getUnImplementMethods(smali)
+ for method in methodsList:
+ print "%s: %s" %(smali.getClassName(), method)
+
+# smali = Smali.Smali("/home/tangliuxiang/work/smali/smali-4.0/devices/t328t/temp/test3/app/Phone/smali/com/android/phone/PhoneInterfaceManager.smali")
+# methodsList = sCheck.getUnImplementMethods(smali)
+# for method in methodsList:
+# print "%s: %s" %(smali.getClassName(), method)
+
+
+def testSmaliGetSmaliPathList(smaliDir):
+ smaliDict = utils.getSmaliDict(smaliDir)
+ for key in smaliDict.keys():
+ print "key: %s, file: %s" %(key, smaliDict[key].mPath)
+
+if __name__ == "__main__":
+ print ""
+ smali = SmaliTest("/home/tangliuxiang/work/smali/smali-mtk-4.2/devices/h30_t00/framework.jar.out/")
+ #smali = SmaliTest("/home/tangliuxiang/work/smali/smali-4.0/devices/t328t/temp/test-telephony")
+ #testSmaliGetSmaliPathList("/home/tangliuxiang/work/smali/smali-mtk-4.2/devices/h30_t00/temp/ori/framework/")
+ # main()
+
+ #testSCheckGetCanReplaceMethods("/home/tangliuxiang/work/smali/smali-4.0/devices/t328t/temp/ori/framework", "/home/tangliuxiang/work/smali/smali-4.0/reference/aosp", "/home/tangliuxiang/work/smali/smali-4.0/devices/t328t/temp/baidu")
+ #testSCheckGetUnImplementMethods("/home/tangliuxiang/work/smali/smali-4.0/devices/t328t/temp/test-telephony/ori", "/home/tangliuxiang/work/smali/smali-4.0/reference/aosp", "/home/tangliuxiang/work/smali/smali-4.0/reference/bosp")
+ #testSCheckGetUnImplementMethods("/home/tangliuxiang/work/smali/smali-4.0/devices/t328t/temp/ori/framework", "/home/tangliuxiang/work/smali/smali-4.0/reference/aosp", "/home/tangliuxiang/work/smali/smali-4.0/reference/bosp", "/home/tangliuxiang/work/smali/smali-4.0/devices/t328t/temp/test3")
+ #testSCheckAutoComplete("/home/tangliuxiang/work/smali/smali-4.2/devices/p6/temp/ori", "/home/tangliuxiang/work/smali/smali-4.2/reference/aosp", "/home/tangliuxiang/work/smali/smali-4.2/devices/p6/temp/baidu", "/home/tangliuxiang/work/smali/smali-4.2/devices/p6/temp/test-1")
diff --git a/smaliparser/__init__.py b/smaliparser/__init__.py
new file mode 100755
index 0000000..e69de29
diff --git a/smaliparser/autocomplete.xml b/smaliparser/autocomplete.xml
new file mode 100644
index 0000000..75ff2b3
--- /dev/null
+++ b/smaliparser/autocomplete.xml
@@ -0,0 +1,176 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/smaliparser/classtobosp b/smaliparser/classtobosp
new file mode 100755
index 0000000..cc33c12
--- /dev/null
+++ b/smaliparser/classtobosp
@@ -0,0 +1,67 @@
+#!/bin/bash
+
+#**************************************************#
+#This shell used to call SCheck to replace smali
+#**************************************************#
+
+CUR_DIR=$(dirname $0)
+SCHECK=$CUR_DIR/SCheck
+PRE_DIR=$PWD
+PRJ_ROOT=$PWD
+
+function usage()
+{
+ echo "################ USAGE ################"
+ echo "#"
+ echo "# smalitobosp smali"
+ echo "# smali: the target smali, which will be replace to bosp"
+ echo "#"
+ echo "# ex:"
+ echo "# smalitobosp services.jar.out/smali/com/android/server/pm/ShutdownThread.smali"
+ echo "# it will replace the smali mservices.jar.out/smali/com/android/server/pm/ShutdownThread.smali and the memberclass to bosp!"
+ echo "#"
+ echo "# Attention:"
+ echo "# This is not just simple replace, it will check the methods, fields, class which was used can be found!"
+ echo "#"
+}
+
+if [ $# -lt 1 ]; then
+ usage
+ exit 1
+fi
+
+function getPrjRoot()
+{
+ while [ ! -f $PRJ_ROOT/Makefile ] && [ ! -f $PRJ_ROOT/makefile ]
+ do
+ PRJ_ROOT=`dirname $PRJ_ROOT`
+ if [ $PRJ_ROOT == '/' ]; then
+ echo "Error: can not get the project's root directory!"
+ echo " make sure you run this command in your device directory"
+ exit 1
+ fi
+ done
+
+ if [ ${PRJ_ROOT#$PORT_ROOT*} == $PRJ_ROOT ]; then
+ echo "Error: you should run 'source ./build/envsetup.sh' in coron first!"
+ exit 1
+ fi
+}
+
+function main()
+{
+ if [ $# -lt 1 ]; then
+ usage
+ exit 1
+ fi
+ getPrjRoot
+
+ cd $PRJ_ROOT > /dev/null
+ tmpFile=`mktemp -t methodtobosp.XXXXX`
+ echo "$@" > $tmpFile
+ make smalitobosp SMALI_FILE=$tmpFile
+ rm $tmpFile
+ cd $PRE_DIR > /dev/null
+}
+
+main $@
diff --git a/smaliparser/help/reject_advice b/smaliparser/help/reject_advice
new file mode 100644
index 0000000..6b849be
--- /dev/null
+++ b/smaliparser/help/reject_advice
@@ -0,0 +1,11 @@
+ |
+ | >> Advice:
+ | The reason that these methods can not be replace to bosp
+ | probaly because they are constructor methods,
+ | where the fields which was added by vendor will be initiated!
+ |
+ | You better fix the remain rejects by yourself!
+ |
+ | How to fix the reject:
+ | you can find it in "manifests/Coron-Developer-Guide.pdf"
+ |
diff --git a/smaliparser/help/reject_success b/smaliparser/help/reject_success
new file mode 100644
index 0000000..75548f2
--- /dev/null
+++ b/smaliparser/help/reject_success
@@ -0,0 +1,7 @@
+ |
+ | >> ^_^. All reject has been fixed. Congratulations!
+ |
+ | Advice:
+ | You could go on to `make` out a ROM, flash it into
+ | your device, and then fix bugs depends on real-time logs.
+ |
diff --git a/smaliparser/help/smalitobosp_advice b/smaliparser/help/smalitobosp_advice
new file mode 100644
index 0000000..04cd09f
--- /dev/null
+++ b/smaliparser/help/smalitobosp_advice
@@ -0,0 +1,8 @@
+ |
+ | >> Advice:
+ | The reason that these smali's method can not be replace to bosp
+ | probaly because they are constructor methods,
+ | where the fields which was added by vendor will be initiated!
+ |
+ | You better fix the reject by yourself! There are errors when replace to bosp!
+ |
diff --git a/smaliparser/help/smalitobosp_success b/smaliparser/help/smalitobosp_success
new file mode 100644
index 0000000..eaee52c
--- /dev/null
+++ b/smaliparser/help/smalitobosp_success
@@ -0,0 +1,7 @@
+ |
+ | >> ^_^. All of the smali has been successfully replaced to bosp . Congratulations!
+ |
+ | Advice:
+ | You could go on to `make` out a ROM, flash it into
+ | your device, and then fix bugs depends on real-time logs.
+ |
diff --git a/smaliparser/methodtobosp b/smaliparser/methodtobosp
new file mode 100755
index 0000000..412f0a2
--- /dev/null
+++ b/smaliparser/methodtobosp
@@ -0,0 +1,70 @@
+#!/bin/bash
+
+#**************************************************#
+#This shell used to call SCheck to replace smali
+#**************************************************#
+
+CUR_DIR=$(dirname $0)
+SCHECK=$CUR_DIR/SCheck
+PRE_DIR=$PWD
+PRJ_ROOT=$PWD
+
+function usage()
+{
+ echo "################ USAGE ################"
+ echo "#"
+ echo "# methodtobosp smali methodName"
+ echo "# smali: the target smali, which will be replace to bosp"
+ echo "# methodName: the method's name"
+ echo "#"
+ echo "# ex:"
+ echo "# methodtobosp services.jar.out/smali/com/android/server/am/ActivityManagerService.smali 'moveTaskToFront(IILandroid/os/Bundle;)V'"
+ echo "# it will replace the method moveTaskToFront(IILandroid/os/Bundle;)V in services.jar.out/smali/com/android/server/am/ActivityManagerService.smali by autopatch/bosp/services.jar.out/smali/com/android/server/am/ActivityManagerService.smali"
+ echo "#"
+ echo "# Attention:"
+ echo "# The method name should enclose in the quotes."
+ echo "#"
+}
+
+if [ $# -lt 1 ]; then
+ usage
+ exit 1
+fi
+
+function getPrjRoot()
+{
+ while [ ! -f $PRJ_ROOT/Makefile ] && [ ! -f $PRJ_ROOT/makefile ]
+ do
+ PRJ_ROOT=`dirname $PRJ_ROOT`
+ if [ $PRJ_ROOT == '/' ]; then
+ echo "Error: can not get the project's root directory!"
+ echo " make sure you run this command in your device directory"
+ exit 1
+ fi
+ done
+
+ if [ ${PRJ_ROOT#$PORT_ROOT*} == $PRJ_ROOT ]; then
+ echo "Error: you should run 'source ./build/envsetup.sh' in coron first!"
+ exit 1
+ fi
+}
+
+function main()
+{
+ if [ $# -lt 2 ]; then
+ usage
+ exit 1
+ fi
+
+ getPrjRoot
+
+ cd $PRJ_ROOT > /dev/null
+ tmpFile=`mktemp -t methodtobosp.XXXXX`
+ echo "$2" > $tmpFile
+ make methodtobosp SMALI_FILE=$1 METHOD=$tmpFile
+ rm -rf $tmpFile
+ cd $PRE_DIR > /dev/null
+}
+
+main
+
diff --git a/smaliparser/reject.py b/smaliparser/reject.py
new file mode 100644
index 0000000..46e8481
--- /dev/null
+++ b/smaliparser/reject.py
@@ -0,0 +1,244 @@
+'''
+Created on Jun 5, 2014
+
+@author: tangliuxiang
+'''
+
+import Smali
+import utils
+import re
+import sys
+import os
+import SmaliEntry
+import Replace
+import SAutoCom
+import tempfile
+import LibUtils
+
+from formatters.log import Paint
+
+class reject(object):
+ REJ_BEGIN = '<<<<<<<'
+ REJ_SPLIT = '======='
+ REJ_END = '>>>>>>>'
+
+ REJ_MULTI_STR = '^%s.*$|^%s.*$|^%s.*$' % (REJ_BEGIN, REJ_SPLIT, REJ_END)
+ RE_REJ = re.compile(REJ_MULTI_STR, re.M)
+
+ FLAG_ENTRY_NORMAL = 0
+ FLAG_ENTRY_REJECT = 1
+
+ FLAG_REPLACED_TO_BOARD = 2
+
+ '''
+ classdocs
+ '''
+ def __init__(self, rejectFilePath):
+ self.mASLib = LibUtils.getSmaliLib(utils.AOSP, 1)
+ self.mBSLib = LibUtils.getSmaliLib(utils.BOSP, 1)
+ self.mTSLib = LibUtils.getSmaliLib(utils.TARGET, 1)
+ self.mRejSLib = LibUtils.getOwnLib(rejectFilePath)
+
+ self.rejSmali = Smali.Smali(rejectFilePath)
+ self.mClassName = self.rejSmali.getClassName()
+
+ self.aospSmali = self.mASLib.getFormatSmali(self.mClassName)
+ self.bospSmali = self.mBSLib.getFormatSmali(self.mClassName)
+ self.targetSmali = self.mTSLib.getFormatSmali(self.mClassName)
+
+ self.mFormatList = {}
+ self.mCanNotReplaceEntry = []
+ self.parseReject()
+
+ def parseReject(self):
+ for entry in self.rejSmali.getEntryList():
+ if self.hasReject(entry.getContentStr()):
+ entry.addFlag(reject.FLAG_ENTRY_REJECT)
+
+ def __multiEntryReject__(self):
+ lastEntry = None
+ targetSmaliModified = False
+
+ for entry in self.rejSmali.getEntryList():
+ if self.hasReject(entry.getPreContentStr()):
+ entry.setPreContentStr(self.rmRejectTagLine(entry.getPreContentStr()))
+
+ if not self.targetSmali.hasEntry(entry.getType(), entry.getName()):
+ if not self.isRejectEntry(entry):
+ idx = -1
+ if lastEntry is not None:
+ tEntry = self.targetSmali.getEntry(lastEntry.getType(), lastEntry.getName())
+ idx = self.targetSmali.getIndex(tEntry) + 1
+ utils.SLog.ok("\n>>>> Fix reject %s %s in %s: " % (entry.getType(), entry.getName(), self.rejSmali.getPath()))
+ utils.SLog.ok(" Add %s %s in %s from bosp" % (entry.getType(), entry.getName(), self.getRealTargetPath(self.targetSmali)))
+ self.targetSmali.addEntry(entry, idx, utils.annotation.getAddToBospPreContent(entry))
+ targetSmaliModified = True
+ else:
+ lastEntry = entry
+ if targetSmaliModified:
+ self.targetSmali.out()
+ self.rejSmali.out()
+
+ FIX_EXACT = 0
+ FIX_LIGHT = 1
+ FIX_MIDDLE = 2
+ FIX_HEAVY = 3
+ def fix(self, level=FIX_EXACT):
+ if level >= reject.FIX_EXACT:
+ self.__multiEntryReject__()
+
+ if level >= reject.FIX_LIGHT:
+# print "replace only which can replace methods"
+# print "add missed class"
+# print "add can replace method"
+ if not utils.precheck.shouldIgnore(self.rejSmali):
+ self.handleLightFix()
+ else:
+ utils.SLog.fail(">>>> Failed on fix reject in %s" %(self.getRealTargetPath(self.targetSmali)))
+
+ if level >= reject.FIX_MIDDLE:
+# print "which can not replace method: use blank"
+ self.handleMiddleFix()
+
+ if level >= reject.FIX_HEAVY:
+ print "which missed field, replace class"
+
+ self.__exit()
+
+ def getRejectEntryList(self):
+ rejectEntryList = []
+ for entry in self.rejSmali.getEntryList():
+ if self.isRejectEntry(entry):
+ rejectEntryList.append(entry)
+ return rejectEntryList
+
+ def getRejectEntry(self, oriEntry):
+ for entry in self.getRejectEntryList():
+ if entry.getClassName() == oriEntry.getClassName() \
+ and entry.getType() == oriEntry.getType() \
+ and entry.getName() == oriEntry.getName():
+ return entry
+ return None
+
+ def __exit(self):
+ hasReject = self.hasReject(self.rejSmali.toString())
+ if hasReject:
+ self.rejSmali.out()
+ if hasReject:
+ self.rejSmali = Smali.Smali(self.rejSmali.getPath())
+ self.rejSmali.out(self.getOutRejectFilePath())
+
+ def getOutRejectFilePath(self):
+ start = len(os.path.abspath(utils.REJECT))
+ rejPath = self.rejSmali.getPath()
+ return "%s/%s" % (utils.OUT_REJECT, rejPath[start:])
+
+ def getRealTargetPath(self, tSmali):
+ return "%s/smali/%s" % (utils.getJarNameFromPath(tSmali.getPath()), utils.getBaseSmaliPath(tSmali.getPath()))
+
+ def replaceEntryToBosp(self, entry):
+ if (entry.getFlag() & reject.FLAG_REPLACED_TO_BOARD) == 0:
+ self.mTSLib.replaceEntry(self.mBSLib, entry)
+
+ if self.mRejSLib.getSmali(entry.getClassName()) is not None:
+ self.rejSmali = self.mRejSLib.replaceEntry(self.mBSLib, entry, False, False, True)
+ rejEntry = self.getRejectEntry(entry)
+ if rejEntry is not None:
+ rejEntry.setFlag(reject.FLAG_ENTRY_NORMAL)
+ entry.addFlag(reject.FLAG_REPLACED_TO_BOARD)
+
+ def handleLightFix(self):
+ target = os.path.relpath(self.rejSmali.getPath(), utils.REJECT)
+ print " "
+ print " %s %s" % (Paint.bold("FIX CONFLICTS IN"), target)
+ for mEntry in self.mBSLib.getEntryList(self.getRejectEntryList()):
+ (canReplaceEntry, canNotReplaceEntry) = self.mTSLib.getCanReplaceEntry(self.mBSLib, self.rejSmali.getClassName(), [mEntry], False)
+ if utils.has(canReplaceEntry, mEntry):
+ print " %s %s" % (Paint.green("[PASS]"), mEntry.getName())
+ utils.SLog.ok("\n>>>> Fix reject %s %s in %s: " % (mEntry.getType(), mEntry.getName(), self.rejSmali.getPath()))
+ self.replaceEntryToBosp(mEntry)
+
+ for entry in canReplaceEntry:
+ if entry != mEntry:
+ self.replaceEntryToBosp(entry)
+ if len(canNotReplaceEntry) > 0:
+ self.mCanNotReplaceEntry.extend(canNotReplaceEntry)
+ if utils.has(canNotReplaceEntry, mEntry):
+ print " %s %s" % (Paint.red("[FAIL]"), mEntry.getName())
+
+ utils.SLog.fail(" %s" % target)
+ #utils.SLog.fail(" CONFLICTS: %s %s in %s" % (mEntry.getType(), mEntry.getName(), self.getRealTargetPath(self.mTSLib.getSmali(mEntry.getClassName()))))
+ #utils.SLog.fail(" Can not be replaced by bosp, because of the follows:")
+ for entry in canNotReplaceEntry:
+ #utils.SLog.fail(" %s %s in %s" % (entry.getType(), entry.getName(), self.getRealTargetPath(self.mTSLib.getSmali(entry.getClassName()))))
+ pass
+
+ @staticmethod
+ def missMethod(sLib, entry):
+ try:
+ missedMethodsLen = len(sLib.getMissedMethods(entry))
+ except Exception as e:
+ utils.SLog.d(e)
+ return True
+ return missedMethodsLen > 0
+
+ @staticmethod
+ def missField(sLib, entry):
+ try:
+ missedFieldssLen = len(sLib.getMissedFields(entry))
+ except Exception as e:
+ utils.SLog.d(e)
+ return True
+ return missedFieldssLen > 0
+
+ def handleMiddleFix(self):
+ for entry in self.mCanNotReplaceEntry:
+ if entry.getType() == SmaliEntry.METHOD:
+ clsName = entry.getClassName()
+
+ tSmali = self.mTSLib.getSmali(clsName)
+ bSmali = self.mBSLib.getSmali(clsName)
+
+ if not tSmali.hasEntry(entry.getType(), entry.getName()):
+ Replace.Replace.appendBlankEntry(entry, self.mTSLib.getSmali(entry.getClassName()).getPath())
+
+ @staticmethod
+ def isRejectEntry(entry):
+ return entry.getFlag() & reject.FLAG_ENTRY_REJECT != 0
+
+ @staticmethod
+ def hasReject(string):
+ if string is None:
+ return False
+
+ if bool(reject.RE_REJ.search(string)):
+ return True
+ else:
+ return False
+
+ @staticmethod
+ def rmRejectTagLine(string):
+ if string is None:
+ return string
+ outString = ""
+ for line in string.splitlines():
+ if not reject.hasReject(line):
+ outString = "%s%s" % (outString, line)
+ return outString
+
+
+REJECT_ADVICE = "%s/help/reject_advice" %os.path.dirname(os.path.abspath(__file__))
+REJECT_SUCCESS = "%s/help/reject_success" %os.path.dirname(os.path.abspath(__file__))
+
+def fixReject():
+ utils.annotation.setAction("make autofix")
+ for rejectFile in utils.getSmaliPathList(utils.REJECT, 2):
+ reject(rejectFile).fix(reject.FIX_LIGHT)
+
+ utils.SLog.setAdviceStr(file(REJECT_ADVICE).read())
+ utils.SLog.setSuccessStr(file(REJECT_SUCCESS).read())
+ utils.SLog.conclude()
+ LibUtils.undoFormat()
+
+if __name__ == "__main__":
+ fixReject()
diff --git a/smaliparser/tobosp.py b/smaliparser/tobosp.py
new file mode 100644
index 0000000..8e37104
--- /dev/null
+++ b/smaliparser/tobosp.py
@@ -0,0 +1,24 @@
+'''
+Created on Jun 3, 2014
+
+@author: tangliuxiang
+'''
+
+import SCheck
+import SmaliEntry
+import SmaliFileReplace
+import os
+import utils
+import Replace
+
+def usage():
+ print ""
+
+def tobosp(args):
+ if len(args) <= 0:
+ usage()
+ elif len(args) == 2:
+ Replace.replace(utils.getMatchFile(args[0], utils.BOSP), args[0], SmaliEntry.METHOD, SmaliEntry, args[1])
+ else:
+ for smaliFile in args:
+ SmaliFileReplace.replace(utils.getMatchFile(smaliFile, utils.BOSP), smaliFile)
diff --git a/smaliparser/utils.py b/smaliparser/utils.py
new file mode 100644
index 0000000..3ed5e50
--- /dev/null
+++ b/smaliparser/utils.py
@@ -0,0 +1,334 @@
+'''
+Created on Mar 26, 2014
+
+@author: tangliuxiang
+'''
+
+import os, sys
+import re
+import Smali
+import SmaliEntry
+import time
+import getpass
+
+sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+from formatters.log import Log, Paint
+
+KEY_PUBLIC = "public"
+KEY_PRIVATE = "private"
+KEY_PROTECTED = "protected"
+KEY_STATIC = "static"
+KEY_FINAL = "final"
+KEY_SYNTHETIC = "synthetic"
+
+# only use in methods
+KEY_ABSTRACT = "abstract"
+KEY_CONSTRUCTOR = "constructor"
+KEY_BRIDGE = "bridge"
+KEY_DECLARED_SYNCHRONIZED = "declared-synchronized"
+KEY_NATIVE = "native"
+KEY_SYNCHRONIZED = "synchronized"
+KEY_VARARGS = "varargs"
+
+# only use in field
+KEY_ENUM = "enum"
+KEY_TRANSIENT = "transient"
+KEY_VOLATILE = "volatile"
+
+KEY_INTERFACE = "interface"
+
+class SLog():
+ TAG = "smali-parser"
+ DEBUG = False
+ FAILED_LIST = []
+ SUCCESS_LIST = []
+
+ ADVICE = ""
+ SUCCESS = ""
+ @staticmethod
+ def e(s):
+ Log.e(SLog.TAG, s)
+ @staticmethod
+ def w(s):
+ Log.w(SLog.TAG, s)
+ @staticmethod
+ def d(s):
+ if SLog.DEBUG:
+ Log.i(SLog.TAG, s)
+ else:
+ Log.d(SLog.TAG, s)
+ @staticmethod
+ def i(s):
+ Log.i(SLog.TAG, s)
+ @staticmethod
+ def fail(s):
+ SLog.FAILED_LIST.append(s)
+ @staticmethod
+ def ok(s):
+ SLog.SUCCESS_LIST.append(s)
+ #Log.i(SLog.TAG, s)
+
+ @staticmethod
+ def conclude():
+
+ print Paint.red(" ____________________________________________________________________________________")
+ print " "
+ print Paint.red(" Go through 'autopatch/still-reject' to find out the rest of conflicts after autofix:")
+ print " "
+
+ if len(SLog.FAILED_LIST) > 0:
+ for failed in set(SLog.FAILED_LIST): print Paint.red(failed)
+ #Log.i(SLog.TAG, SLog.ADVICE)
+ else:
+ #Log.i(SLog.TAG, SLog.SUCCESS)
+ pass
+
+
+ @staticmethod
+ def setAdviceStr(str):
+ SLog.ADVICE = str
+
+ @staticmethod
+ def setSuccessStr(str):
+ SLog.SUCCESS = str
+
+PRJ_ROOT = os.getcwd()
+REJECT = '%s/autopatch/reject' %(PRJ_ROOT)
+BOSP = '%s/autopatch/bosp' %(PRJ_ROOT)
+AOSP = '%s/autopatch/aosp' %(PRJ_ROOT)
+TARGET = "%s/out/obj/autofix/target" %(PRJ_ROOT)
+OUT_REJECT = '%s/autopatch/still-reject' %(PRJ_ROOT)
+
+SMALI_POST_SUFFIX = r'\.smali'
+PART_SMALI_POST_SUFFIX = r'\.smali\.part'
+SMALI_POST_SUFFIX_LEN = 6
+
+smaliFileRe = re.compile(r'(?:^.*%s$)|(?:^.*%s$)' % (SMALI_POST_SUFFIX, PART_SMALI_POST_SUFFIX))
+partSmaliFileRe = re.compile(r'(?:^.*%s$)' %(PART_SMALI_POST_SUFFIX))
+
+def has(list, item):
+ for i in list:
+ if i == item:
+ return True
+ return False
+
+def isSmaliFile(smaliPath):
+ return bool(smaliFileRe.match(smaliPath))
+
+def isPartSmaliFile(smaliPath):
+ return bool(partSmaliFileRe.match(smaliPath))
+
+def getSmaliPathList(source, smaliDirMaxDepth = 0):
+ filelist = []
+
+ source = os.path.abspath(source)
+ if os.path.isfile(source):
+ if isSmaliFile(source):
+ filelist.append(source)
+ return filelist
+
+ for root, dirs, files in os.walk(source):
+ if smaliDirMaxDepth > 0:
+ rootWithSuffix = "%s/" %root
+ try:
+ idx = rootWithSuffix.rindex('/smali/')
+ except:
+ idx = -1
+ if idx < len(source) \
+ or (idx > len(source) \
+ and len(rootWithSuffix[len(source) + 1: idx].split('/')) > smaliDirMaxDepth):
+ continue
+ for fn in files:
+ if isSmaliFile(fn):
+ filelist.append("%s/%s" % (root, fn))
+
+ return filelist
+
+def getPackageFromClass(className):
+ try:
+ idx = className.rindex(r'/')
+ except:
+ SLog.e("wrong className: %s" %className)
+ return None
+ return className[1:idx]
+
+def getSmaliDict(smaliDir, smaliDirMaxDepth = 0):
+ sFileList = getSmaliPathList(smaliDir, smaliDirMaxDepth)
+ smaliDict = {}
+
+ for sPath in sFileList:
+ smali = Smali.Smali(sPath)
+ sClass = getClassFromPath(sPath)
+ smaliDict[sClass] = smali
+
+ return smaliDict
+
+def getJarNameFromPath(smaliPath):
+ assert isSmaliFile(smaliPath), "This file is not smali file: %s" % smaliPath
+
+ absSmaliPath = os.path.abspath(smaliPath)
+ #splitArray = absSmaliPath.split("/smali/")
+ splitArray = re.split("/smali/|/smali_classes\\d*/", absSmaliPath)
+
+ assert len(splitArray) >= 2, "This smali is not decode by apktool, doesn't hava /smali/ directory"
+ return os.path.basename(splitArray[len(splitArray) - 2])
+
+def getBaseSmaliPath(smaliPath):
+ assert isSmaliFile(smaliPath), "This file is not smali file: %s" % smaliPath
+
+ absSmaliPath = os.path.abspath(smaliPath)
+ #splitArray = absSmaliPath.split("/smali/")
+ splitArray = re.split("/smali/|/smali_classes\\d*/", absSmaliPath)
+
+ assert len(splitArray) >= 2, "This smali is not decode by apktool, doesn't hava /smali/ directory"
+ return splitArray[len(splitArray) - 1]
+
+def getClassBaseNameFromPath(smaliPath):
+ assert isSmaliFile(smaliPath), "This file is not smali file: %s" % smaliPath
+ sBaseName = os.path.basename(smaliPath)
+ return sBaseName[:sBaseName.rindex('.smali')]
+
+def getClassFromPath(smaliPath):
+ assert isSmaliFile(smaliPath), "This file is not smali file: %s" % smaliPath
+
+ absSmaliPath = os.path.abspath(smaliPath)
+ #splitArray = absSmaliPath.split("/smali/")
+ splitArray = re.split("/smali/|/smali_classes\\d*/", absSmaliPath)
+
+ assert len(splitArray) >= 2, "This smali is not decode by apktool, doesn't hava /smali/ directory"
+ clsNameWithPost = splitArray[len(splitArray) - 1]
+ return 'L%s;' % clsNameWithPost[:clsNameWithPost.rindex('.smali')]
+
+def getReturnType(methodName):
+ splitArray = methodName.split(r')')
+ if len(splitArray) < 2:
+ SLog.w("method %s return nothing!" %methodName)
+ return ""
+ else:
+ return splitArray[-1]
+
+def getMatchFile(smaliFile, targetDir = BOSP):
+ assert isSmaliFile(smaliFile), "%s is not an smali file" %(smaliFile)
+ assert os.path.isdir(targetDir), "%s directory is not exist!" %(targetDir)
+ assert os.path.isfile(smaliFile), "%s is not exist!" %(smaliFile)
+
+ baseSmaliFile = getBaseSmaliPath(smaliFile)
+ jarName = getJarNameFromPath(smaliFile)
+
+ filePath = '%s/smali/%s' %(jarName, baseSmaliFile)
+ if os.path.isfile('%s/%s' %(targetDir, filePath)):
+ return '%s/%s' %(targetDir, filePath)
+ else:
+ cmd = 'find %s -path "*/smali/%s"' %(targetDir, baseSmaliFile)
+
+ matchFiles = os.popen(cmd).readlines()
+ assert len(matchFiles) >= 1, "Error: Can not find match files in %s for %s" % (targetDir, smaliFile)
+ if len(matchFiles) > 1:
+ minLen = len(matchFiles[0].split('/'))
+ minDepthFile = matchFiles[0]
+ for f in matchFiles[1:]:
+ sLen = len(f.split('/'))
+ if sLen < minLen:
+ minDepthFile = f
+ minLen = sLen
+
+ SLog.w(" there are not only one file for %s in %s" % (baseSmaliFile, targetDir))
+ SLog.i(" used: %s" % (minDepthFile))
+ SLog.i(" You can run %s to check it out!" % cmd)
+ return matchFiles[0][:-1]
+
+
+def getSimpleMethodName(methodName):
+ return methodName.split('(')[0]
+
+class precheck():
+ mPreCheck = None
+
+ @staticmethod
+ def getInstance():
+ if precheck.mPreCheck is None:
+ precheck.mPreCheck = precheck()
+ return precheck.mPreCheck
+
+ @staticmethod
+ def setInstance(nInstance):
+ precheck.mPreCheck = nInstance
+
+ @staticmethod
+ def canAddField(field):
+ firstLine = field.getFirstLine()
+ try:
+ if firstLine.index('=') > 0 and field.hasKeyList([KEY_FINAL, KEY_STATIC]):
+ return True
+ except:
+ return False
+ return False
+
+ @staticmethod
+ def shouldIgnore(smali):
+ if smali.getClassName() == 'Lcom/android/server/ServerThread;' or smali.getSuperClassName() == 'Landroid/os/Binder;':
+ return True
+ return False
+
+ def precheck(self, tSmali, bSmali, entry):
+ if tSmali is not None \
+ and bSmali is not None \
+ and entry.getType() == SmaliEntry.METHOD:
+ if entry.isConstructor():
+ for field in tSmali.getEntryList(SmaliEntry.FIELD):
+ if not bSmali.hasEntry(SmaliEntry.FIELD, field.getName()) and not self.canAddField(field):
+ return False
+ elif entry.getSimpleName() == "onTransact":
+ SLog.e(">>> onTransact is an binder interface, it shouldn't be replace, if you want to replace, find the stub and proxy, replace them all!")
+ SLog.e(" such as if you want replace method in IAlarmManager$Stub.smali")
+ SLog.e(" you better use smalitobosp to replace the IAlarmManager$Stub.smali and IAlarmManager$Stub$Proxy.smali both!")
+ return False
+ return True
+
+def preCheck(tSmali, bSmali, entry):
+ return precheck.getInstance().precheck(tSmali, bSmali, entry)
+
+class annotation():
+ CUR_ACTION = None
+ ATTENTION = None
+
+ ENABLE = True
+ @staticmethod
+ def disable():
+ annotation.ENABLE = False
+
+ @staticmethod
+ def enable():
+ annotation.ENABLE = True
+
+ @staticmethod
+ def setAction(action):
+ annotation.CUR_ACTION = action
+
+ @staticmethod
+ def setAttention(attention):
+ annotation.ATTENTION = attention
+
+ @staticmethod
+ def getAppendStr():
+ appendStr = ""
+ if annotation.CUR_ACTION is not None:
+ appendStr = "\n# @action: %s" %(annotation.CUR_ACTION)
+
+ if annotation.ATTENTION is not None:
+ appendStr = "%s\n# @attention: %s" %(annotation.ATTENTION)
+ return appendStr
+
+ @staticmethod
+ def getReplaceToBospPreContent(entry):
+ if annotation.ENABLE:
+ return "# REPLACE:\n# This %s is replaced to bosp at %s\n#%s\n# @author: %s" %(entry.getType(), time.strftime('%Y-%m-%d %H:%M'), annotation.getAppendStr(), getpass.getuser())
+ else:
+ return None
+
+ @staticmethod
+ def getAddToBospPreContent(entry):
+ if annotation.ENABLE:
+ return "# ADD:\n# This %s is replaced to bosp at %s\n#%s\n# @author: %s" %(entry.getType(), time.strftime('%Y-%m-%d %H:%M'), annotation.getAppendStr(), getpass.getuser())
+ else:
+ return None
diff --git a/su-chmod b/su-chmod
new file mode 120000
index 0000000..4af281e
--- /dev/null
+++ b/su-chmod
@@ -0,0 +1 @@
+su-tools/su-chmod
\ No newline at end of file
diff --git a/su-pull b/su-pull
new file mode 120000
index 0000000..2e6c21c
--- /dev/null
+++ b/su-pull
@@ -0,0 +1 @@
+su-tools/su-pull
\ No newline at end of file
diff --git a/su-push b/su-push
new file mode 120000
index 0000000..4a2df98
--- /dev/null
+++ b/su-push
@@ -0,0 +1 @@
+su-tools/su-push
\ No newline at end of file
diff --git a/su-tools/check-su b/su-tools/check-su
new file mode 100755
index 0000000..dbfec4e
--- /dev/null
+++ b/su-tools/check-su
@@ -0,0 +1,45 @@
+#!/bin/bash
+
+prog="$0"
+while [ -h "${prog}" ]; do
+ newProg=`/bin/ls -ld "${prog}"`
+ #echo ${newProg}
+
+
+ newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
+ if expr "x${newProg}" : 'x/' >/dev/null; then
+ prog="${newProg}"
+ else
+ progdir=`dirname "${prog}"`
+ prog="${progdir}/${newProg}"
+ fi
+done
+oldwd=`pwd`
+progdir=`dirname "${prog}"`
+cd "${progdir}"
+progdir=`pwd`
+prog="${progdir}"/`basename "${prog}"`
+cd "${oldwd}"
+
+CHECK_SU=$progdir/phone-check-su
+PHONE_TMP_DIR=/data/local/tmp
+PHONE_CHECK_SU=$PHONE_TMP_DIR/phone-check-su
+
+function getRet()
+{
+ adb wait-for-device
+ adb push $CHECK_SU $PHONE_CHECK_SU
+ adb shell chmod 777 $PHONE_CHECK_SU
+
+ ret=$(adb shell sh $PHONE_CHECK_SU 2>&1 | grep OK)
+ if [ "x$ret" != "x" ]; then
+ echo "su is ok"
+ return 0
+ else
+ echo "Error: su is wrong"
+ return 1
+ fi
+}
+
+getRet
+exit $?
diff --git a/su-tools/phone-check-su b/su-tools/phone-check-su
new file mode 100644
index 0000000..31112a9
--- /dev/null
+++ b/su-tools/phone-check-su
@@ -0,0 +1,12 @@
+#!/system/bin/sh
+
+su -c "mount -o remount,rw /system"
+
+if [ $? == 0 ]; then
+ echo "OK"
+ exit 0
+fi;
+
+echo "ERROR"
+
+
diff --git a/su-tools/phone-chmod b/su-tools/phone-chmod
new file mode 100644
index 0000000..68c874d
--- /dev/null
+++ b/su-tools/phone-chmod
@@ -0,0 +1,35 @@
+#!/system/bin/sh
+self=$0
+
+pushed_file=""
+if [ "x$1" == "x--after-push" ] || [ "x$1" == "x-a" ]; then
+ pushed_file=$2
+ shift
+ shift
+fi
+
+RECURSIVE=false
+if [ "x$1" == "x--recursive" ] || [ "x$1" == "x-R" ]; then
+ RECURSIVE=true
+ shift
+fi
+
+permission=$1
+target=$2
+CHMOD_CMD="chmod"
+
+if [ -d "$target" ]; then
+ pushed_base_name=${pushed_file##*/}
+ if [ "x$pushed_file" != "x" ] && [ -f "$target/$pushed_base_name" ]; then
+ target="$target/$pushed_base_name"
+ fi
+
+ if [ -d "$target" ]; then
+ if [ "$RECURSIVE" == true ]; then
+ CHMOD_CMD="$CHMOD_CMD -R"
+ fi
+ fi
+fi
+
+$CHMOD_CMD $permission $target
+chmod 777 $self
diff --git a/su-tools/phone-cp b/su-tools/phone-cp
new file mode 100644
index 0000000..1d9541e
--- /dev/null
+++ b/su-tools/phone-cp
@@ -0,0 +1,27 @@
+#!/system/bin/sh
+# author: tangliuxiang
+
+self=$0
+src=$1
+dst=$2
+
+if [ ${dst:${#dst}-1} == '/' ]; then
+ dst=${dst::${#dst}-1}
+fi
+
+if [ -d $src ]; then
+ ls $src | while read f; do
+ $self $src/$f $dst/$f
+ done
+else [ -f $src ]
+ if [ -d $dst ]; then
+ srcBN=${src##*/}
+ echo "cat $src > $dst/$srcBN"
+ cat $src > $dst/$srcBN
+ else
+ dstBN=${dst%/*}
+ mkdir -p $dstBN
+ echo "cat $src > $dst"
+ cat $src > $dst
+ fi
+fi
diff --git a/su-tools/su-chmod b/su-tools/su-chmod
new file mode 100755
index 0000000..b884a5c
--- /dev/null
+++ b/su-tools/su-chmod
@@ -0,0 +1,67 @@
+#!/bin/bash
+# author: tangliuxiang
+
+function usage()
+{
+ echo "USAGE: su-chmod [OPTION] MODE FILE"
+ echo " -R, --recursive"
+ echo " change files and directories recursively"
+}
+
+prog="$0"
+while [ -h "${prog}" ]; do
+ newProg=`/bin/ls -ld "${prog}"`
+ #echo ${newProg}
+
+ newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
+ if expr "x${newProg}" : 'x/' >/dev/null; then
+ prog="${newProg}"
+ else
+ progdir=`dirname "${prog}"`
+ prog="${progdir}/${newProg}"
+ fi
+done
+oldwd=`pwd`
+progdir=`dirname "${prog}"`
+cd "${progdir}"
+progdir=`pwd`
+prog="${progdir}"/`basename "${prog}"`
+cd "${oldwd}"
+
+if [ $# -lt 2 ]; then
+ usage
+ exit 1
+fi
+
+pushed_file=""
+if [ "x$1" == "x--after-push" ] || [ "x$1" == "x-a" ]; then
+ pushed_file="--after-push $2"
+ shift
+ shift
+fi
+
+RECURSIVE=""
+if [ "x$1" == "x--recursive" ] || [ "x$1" == "x-R" ]; then
+ RECURSIVE="-R"
+ shift
+fi
+
+permission=$1
+target=$2
+p_chmod="$progdir/phone-chmod"
+phone_chmod=/data/local/tmp/phone-chmod
+
+adb push $p_chmod $phone_chmod
+adb shell chmod 777 $phone_chmod
+
+chmod_cmd="$phone_chmod $pushed_file $RECURSIVE $permission $target"
+
+if [[ $target == "/system/"* ]] || [[ $target == "system/"* ]];then
+ #echo "su -c \"mount -o remount,rw /system; $chmod_cmd ;mount -o remount,ro /system\"; exit $?" | adb shell
+ adb shell su -c mount -o remount,rw /system;
+ adb shell su -c $chmod_cmd
+# adb shell su -c mount -o remount,ro /system
+else
+ #echo "su -c \"$chmod_cmd; \"; exit $?" | adb shell
+ adb shell su -c $chmod_cmd;
+fi
diff --git a/su-tools/su-pull b/su-tools/su-pull
new file mode 100755
index 0000000..8b9ef50
--- /dev/null
+++ b/su-tools/su-pull
@@ -0,0 +1,71 @@
+#!/bin/bash
+# author: tangliuxiang
+
+function usage()
+{
+ echo "USAGE: su-pull "
+ echo " - copy file/dir from device to pc"
+ echo "Just like the usage of adb pull"
+}
+
+prog="$0"
+while [ -h "${prog}" ]; do
+ newProg=`/bin/ls -ld "${prog}"`
+ #echo ${newProg}
+
+ newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
+ if expr "x${newProg}" : 'x/' >/dev/null; then
+ prog="${newProg}"
+ else
+ progdir=`dirname "${prog}"`
+ prog="${progdir}/${newProg}"
+ fi
+done
+oldwd=`pwd`
+progdir=`dirname "${prog}"`
+cd "${progdir}"
+progdir=`pwd`
+prog="${progdir}"/`basename "${prog}"`
+cd "${oldwd}"
+
+if [ $# -lt 2 ]; then
+ usage
+ exit 1
+fi
+
+src=$1
+target=$2
+
+src_basename=`basename $src`
+CHECK_SU=$progdir/check-su
+pcp=$progdir/phone-cp
+ptmp_su_pull=/data/local/tmp/su_pull_tmp
+phone_cp=/data/local/tmp/phone-cp
+
+if [ x"$target" == x ];then
+ target=$PWD/$src_basename
+fi
+
+adb wait-for-device
+
+$CHECK_SU
+if [ $? != 0 ]; then
+ echo "Error: failed to pull $src to $target"
+ echo " You better make sure you have an correct su, you can run check-su to test if su is ok"
+ exit 1
+fi
+
+adb push $pcp $phone_cp
+adb shell chmod 777 $phone_cp
+
+echo "begin copy $src to $ptmp_su_pull/$src_basename"
+#echo "su -c \"rm -rf $ptmp_su_pull; mkdir -p $ptmp_su_pull; chmod 777 $ptmp_su_pull; $phone_cp $src $ptmp_su_pull/$src_basename; chmod -R 777 $ptmp_su_pull/$src_basename\"; exit" | adb shell
+adb shell su -c "rm -rf $ptmp_su_pull";
+adb shell su -c "mkdir -p $ptmp_su_pull";
+adb shell su -c "chmod 777 $ptmp_su_pull";
+adb shell su -c "$phone_cp $src $ptmp_su_pull/$src_basename";
+adb shell su -c "chmod -R 777 $ptmp_su_pull/$src_basename";
+
+adb pull $ptmp_su_pull/$src_basename $target
+#echo "su -c \"rm -rf $ptmp_su_pull\"; exit" | adb shell
+adb shell su -c "rm -rf $ptmp_su_pull";
diff --git a/su-tools/su-push b/su-tools/su-push
new file mode 100755
index 0000000..abe8097
--- /dev/null
+++ b/su-tools/su-push
@@ -0,0 +1,102 @@
+#!/bin/bash
+# author: tangliuxiang
+
+function usage()
+{
+ echo "USAGE: su-push "
+ echo " - copy file/dir to device"
+ echo "Just like the usage of adb push"
+}
+
+prog="$0"
+while [ -h "${prog}" ]; do
+ newProg=`/bin/ls -ld "${prog}"`
+ #echo ${newProg}
+
+ newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
+ if expr "x${newProg}" : 'x/' >/dev/null; then
+ prog="${newProg}"
+ else
+ progdir=`dirname "${prog}"`
+ prog="${progdir}/${newProg}"
+ fi
+done
+oldwd=`pwd`
+progdir=`dirname "${prog}"`
+cd "${progdir}"
+progdir=`pwd`
+prog="${progdir}"/`basename "${prog}"`
+cd "${oldwd}"
+
+PERMISSION=""
+if [ "x$1" == "x-p" ]; then
+ PERMISSION="$2"
+ shift
+ shift
+fi
+
+if [ $# -lt 2 ]; then
+ usage
+ exit 1
+fi
+
+src=$1
+target=$2
+CHECK_SU=$progdir/check-su
+SU_CHMOD=$progdir/su-chmod
+pcp=$progdir/phone-cp
+ptmp_su_push=/data/local/tmp/su_push
+phone_cp=/data/local/tmp/phone-cp
+
+src_basename=`basename $src`
+
+if [ -f $src ] || [ -d $src ]; then
+ adb wait-for-device
+ $CHECK_SU
+ if [ $? != 0 ]; then
+ echo "Error: failed to push $src to $target"
+ echo " You better make sure you have an correct su, you can run check-su to test if su is ok"
+ exit 1
+ fi
+
+ adb shell rm -rf $ptmp_su_push
+ adb shell mkdir -p $ptmp_su_push
+ adb push $src $ptmp_su_push
+ adb push $pcp $phone_cp
+ adb shell chmod 777 $phone_cp
+
+ if [ -d $src ]; then
+ ptmp_src=$ptmp_su_push
+ else
+ ptmp_src=$ptmp_su_push/$src_basename
+ fi
+
+ if [[ $target == "/system/"* ]] || [[ $target == "system/"* ]];then
+ #echo "su -c \"mount -o remount,rw /system; $phone_cp $ptmp_src $target;mount -o remount,ro /system\"; exit $?" | adb shell
+ adb shell su -c mount -o remount,rw /system;
+ adb shell su -c $phone_cp $ptmp_src $target;
+# adb shell su -c mount -o remount,ro /system;
+ else
+ #echo "su -c \"$phone_cp $PERMISSION $ptmp_src $target; \"; exit $?" | adb shell
+ adb shell su -c $phone_cp $PERMISSION $ptmp_src $target;
+ fi
+
+ adb shell rm -rf $ptmp_su_push
+
+ if [ $? == 0 ]; then
+ if [ "x$PERMISSION" != "x" ]; then
+ $SU_CHMOD --after-push $src $PERMISSION $target
+ if [ $? != 0 ]; then
+ echo "Error: Failed to push $src to $target"
+ exit 1
+ fi
+ fi
+ echo "Success push $src to $target"
+ else
+ echo "Error: Failed to push $src to $target"
+ exit 1
+ fi
+else
+ echo "Error: $src doesn't exist!"
+ exit 1
+fi
diff --git a/unpack_bootimg b/unpack_bootimg
new file mode 120000
index 0000000..42a8743
--- /dev/null
+++ b/unpack_bootimg
@@ -0,0 +1 @@
+bootimgpack/unpack_bootimg.py
\ No newline at end of file
diff --git a/workflow/__init__.py b/workflow/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/workflow/coron.py b/workflow/coron.py
new file mode 100755
index 0000000..4ea4a5b
--- /dev/null
+++ b/workflow/coron.py
@@ -0,0 +1,42 @@
+#!/usr/bin/python
+
+# Copyright 2015 Coron
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+"""
+Coron
+"""
+
+__author__ = "duanqz@gmail.com"
+
+
+import sys
+from workflow import CoronStateMachine
+from helpdoc import help
+
+
+if __name__ == "__main__":
+ if len(sys.argv) == 1:
+ help.usage()
+ sys.exit(0)
+
+ arg = sys.argv[1]
+ if arg == "fire":
+ CoronStateMachine().start()
+ elif arg == "help":
+ help.main(sys.argv[1:])
+ else:
+ CoronStateMachine().doAction(sys.argv[1:])
+
diff --git a/workflow/workflow.py b/workflow/workflow.py
new file mode 100755
index 0000000..827d2af
--- /dev/null
+++ b/workflow/workflow.py
@@ -0,0 +1,262 @@
+#!/usr/bin/python
+
+'''
+Workflow
+'''
+
+
+import os, sys
+import subprocess
+import shutil
+import re
+
+
+try:
+ import xml.etree.cElementTree as ET
+except ImportError:
+ import xml.etree.ElementTree as ET
+
+sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+from helpdoc import help
+from formatters.log import Log, Paint
+
+TAG="workflow"
+
+class Phase:
+ """ The model of phase in workflow
+ """
+
+ def __init__(self, tag):
+ """ Initialize the Phase model.
+ The input tag is in workflow
+ """
+
+ self.tag = tag
+ self.action = tag.get('action')
+ self.next = None
+ self.cmds = []
+
+ for child in tag.getchildren():
+ if child.tag == "state":
+ self.state = child.text
+ elif child.tag == "prev":
+ self.prev = child.get('action')
+ elif child.tag == "next":
+ self.next = child.get('action')
+ elif child.tag == "sequence":
+ for cmd in child.getchildren():
+ self.cmds.append(cmd.text)
+
+ def equals(self, otherPhase):
+ return self.action == otherPhase.action
+
+ def updateState(self):
+ """ Publish the state to XML
+ """
+
+ self.tag.find('state').text = self.state
+
+# End of class Phase
+
+
+class Workflow:
+
+ def __init__(self, xmlPath):
+ self.xmlPath = xmlPath
+ self.xmlDom = ET.parse(xmlPath)
+
+ self.phases = {}
+ for tag in self.xmlDom.findall('phase'):
+ phase = Phase(tag)
+ self.phases[phase.action] = phase
+
+ def getAllActions(self):
+ return self.phases.keys()
+
+ def getPhase(self, action):
+ try:
+ return self.phases[action]
+ except KeyError:
+ return None
+
+ def publish(self):
+ """ Publish the workflow to XML
+ """
+
+ self.xmlDom.write(self.xmlPath)
+
+
+
+class State:
+ INACTIVE = "inactive"
+ ACTIVING = "activing"
+ ACTIVED = "actived"
+
+
+class StateMachine:
+
+ def __init__(self, workflow):
+ self.workflow = workflow
+
+ def start(self):
+ self.transit(self.workflow.getPhase("init"))
+
+ def transit(self, phase):
+ if phase.state == State.INACTIVE:
+ self.activate(phase)
+ elif phase.state == State.ACTIVING:
+ self.reActivate(phase)
+ elif phase.state == State.ACTIVED:
+ if phase.next == None: return
+ self.transit(self.workflow.getPhase(phase.next))
+
+
+ def activate(self, phase):
+ """ Activate phase
+ """
+
+ phase.state = State.ACTIVING
+
+ if self.doAction(phase.action):
+ phase.state = State.ACTIVED
+ self.publish(phase)
+ self.transit(phase)
+ else:
+ self.publish(phase)
+
+
+ def reActivate(self, phase):
+ """ Re-activate phase
+ """
+
+ self.activate(phase)
+
+ def publish(self, phase):
+ """ Publish the phase state to XML
+ """
+
+ phase.updateState()
+ self.workflow.publish()
+
+
+ def doAction(self, action):
+ """ Return True: do action succeed. Otherwise return false
+ """
+
+ raise Exception("Should be implemented by subclass")
+
+# End of class StateMachine
+
+
+class CoronStateMachine(StateMachine):
+ """ Coron State Machine
+ """
+
+ XML = os.path.join(os.curdir, "workflow.xml")
+
+
+ def __init__(self):
+ self.createXMLIfNotExist()
+ StateMachine.__init__(self, Workflow(CoronStateMachine.XML))
+
+ def createXMLIfNotExist(self):
+ if os.path.exists(CoronStateMachine.XML): return
+
+ source = os.path.join(os.path.dirname(os.path.realpath(__file__)), "workflow.xml")
+ shutil.copy(source, CoronStateMachine.XML)
+
+ def doAction(self, action):
+ action = " ".join(action)
+ print Paint.blue(">>> %s" % action)
+
+ status = 0
+
+ phase = self.workflow.getPhase(action)
+ if phase != None:
+ for cmd in phase.cmds:
+ status = Shell.run(cmd)
+ if status != 0: break
+ else:
+ cmd = "make %s" % action
+ status = Shell.run(cmd)
+
+ help.show(status, action)
+
+ if status == 0:
+ print Paint.blue("<<< %s succeed" % action)
+ return True
+ else:
+ print Paint.red("<<< %s failed" % action)
+ return False
+
+
+# End of CoronStateMachine
+
+
+class Shell:
+ """ Subprocess to run shell command
+ """
+
+ @staticmethod
+ def run(cmd):
+ """ Run command in shell.
+ Set `isMakeCommand` to True if command is a `make` command
+ """
+
+ subp = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+
+ output = None
+ while True:
+ buff = subp.stdout.readline().strip('\n')
+ print buff
+ if buff == '' and subp.poll() != None:
+ break
+
+ output = buff
+
+ Log.d(TAG, "Shell.run() %s return %s" %(cmd, subp.returncode))
+ status = Shell.parseHelpStatus(subp.returncode, "%s" %output)
+
+ return status
+
+
+ @staticmethod
+ def parseHelpStatus(status, output):
+ """ Parse the error number in `make` command output
+ """
+
+ # GNU make exits with a status of zero if all makefiles were successfully parsed and no targets that were built failed.
+ # A status of one will be returned if the -q flag was used and make determines that a target needs to be rebuilt.
+ # A status of two will be returned if any errors were encountered.
+ errRegex = re.compile("^make: .* Error (?P(?!.*(ignored)).*)")
+ match = errRegex.search(output)
+ if match != None:
+ Log.d(TAG, "Shell.parseHelpStatus() Target in GNU make")
+ status = int(match.group("errNo"))
+
+ Log.d(TAG, "Shell.parseHelpStatus() Output is %s" % output)
+ Log.d(TAG, "Shell.parseHelpStatus() Status is %d" % status)
+
+ return status
+
+
+ @staticmethod
+ def isMakeCommand(cmd):
+ """ Whether is a make command or not
+ """
+
+ makeRegex = re.compile("^make")
+ return makeRegex.match(cmd) != None
+
+# End of class Shell
+
+
+
+if __name__ == "__main__":
+ CoronStateMachine().start()
+
+
+
+
+
+
diff --git a/workflow/workflow.xml b/workflow/workflow.xml
new file mode 100644
index 0000000..d5d60e2
--- /dev/null
+++ b/workflow/workflow.xml
@@ -0,0 +1,85 @@
+
+
+ inactive
+
+
+
+
+ inactive
+
+
+
+ makeconfig
+
+
+
+
+ inactive
+
+
+
+
+ make newproject
+ git init
+ git add --all
+ git commit -am "newproject"
+
+
+
+
+ inactive
+
+
+
+
+ make patchall
+
+
+
+
+ inactive
+
+
+
+
+ make autofix
+
+
+
+
+ inactive
+
+
+
+ make
+
+
+
+
+ inactive
+
+
+
+
+ make clean
+
+
+
+
+
+ make clean-all
+
+
+
+
+
+ make upgrade
+
+
+
+
+
+ make porting
+
+
+
\ No newline at end of file
diff --git a/zipalign b/zipalign
new file mode 100755
index 0000000..82c2b57
Binary files /dev/null and b/zipalign differ