# -*-coding:utf-8-*- import json import os, shutil, yaml import re import xml.etree.ElementTree as ET import ChannelReplace, contants from print_log import printlog androidNS = 'http://schemas.android.com/apk/res/android' namespace = '{' + androidNS + '}' def special_replace(sdk_name, prj_path, key, value): # 获取特殊替换 replaceFun = ChannelReplace.getSpecilReplace(sdk_name) if replaceFun is None: printlog("no such channel: %s" % sdk_name) else: printlog("begin special replace: %s" % sdk_name) replaceFun(prj_path, key, value) pass def modifyRfileSmaliPath(channel, prj_path): printlog("start copy and modify R.smali-------") manifestRoot = ET.parse("%s/AndroidManifest.xml" % prj_path).getroot() pkgName = manifestRoot.attrib["package"] sdk_root_dir = contants.channel_sdk_path sdkRfilePath = "%s/%s/%s/extension/smali" % (sdk_root_dir, channel, channel) printlog("sdkRfilePath:%s" % sdkRfilePath) if os.path.exists(sdkRfilePath): printlog("begin copy R.smali to apkpath---------") apkPkgDir = ET.parse("%s/AndroidManifest.xml" % prj_path).getroot().attrib["package"].replace(".", "/") apkRfilePath = "%s/smali/%s" % (prj_path, apkPkgDir) if not os.path.exists(apkRfilePath): os.makedirs(apkRfilePath) os.system("/bin/cp -r %s/* %s" % (sdkRfilePath, apkRfilePath)) for parent, dirnames, filenames in os.walk(apkRfilePath): printlog("replace apkRfile dirnames:%s" % dirnames) for filename in filenames: filepath = "'%s/%s'" % (apkRfilePath, filename) replacePkgShell = 'sed -i "s#%s#%s#g" %s' % ("package_path", apkPkgDir, filepath) os.system(replacePkgShell) replacePkgShell = 'sed -i "s#%s#%s#g" %s' % ("package_name", pkgName, filepath) os.system(replacePkgShell) printlog("end copy R.smali file -------------------------------") def createRFile2Sdk(pkgAbsoluteRpath, sdkAbsoluteRpath, pkgRpath, sdkRpath): if os.path.exists(sdkAbsoluteRpath): del_file(sdkAbsoluteRpath) else: os.mkdir(sdkAbsoluteRpath) copyAllFile(pkgAbsoluteRpath, sdkAbsoluteRpath) printlog("pkgRpath: -------------%s" % pkgRpath) printlog("sdkRpath: -------------%s" % sdkRpath) for root, dirs, files in os.walk(sdkAbsoluteRpath): for file in files: if "$" in file: file = file.split('$', 1)[0] + "\$" + file.split('$', 1)[1] replaceKeyword(pkgRpath, sdkRpath, os.path.join(root, file)) def replaceKeyword(key, value, file): replaceShell = 'sed -i "s#%s#%s#g" %s' % (key, value, file) printlog("replaceShell:%s" % replaceShell) os.system(replaceShell) pass def mergeLibs(sdkPath, apkPath): printlog("--------------------------- start to merge lib --------------------------------") apkLib = "%s/lib" % apkPath sdkLib = "%s/lib" % sdkPath ret = 0 if not os.path.exists(apkLib) and os.path.exists(sdkLib): shutil.copytree(sdkLib, apkLib) printlog("finished merging lib --------------------------------") return ret apkFiles = os.listdir(apkLib) sdkFiles = os.listdir(sdkLib) for cpFile in sdkFiles: if os.path.isfile(cpFile) == False and cpFile not in apkFiles: continue ret = ret | mergeFiles("%s/lib/%s" % (sdkPath, cpFile), "%s/lib/%s" % (apkPath, cpFile)) printlog("--------------------------- finished merging lib --------------------------------") return ret def mergeFiles(fromDir, toDir): # 必须合并的列表 mergeList = ["attrs.xml", "colors.xml", "dimens.xml", "drawables.xml", "ids.xml", "public.xml", "strings.xml", "style.xml"] printlog(">>>>>>>>>>>>>>>>>>>>>>>>fromDir:<<<<<<<<<<<<<<<<<<<<<<<<<< %s" % fromDir) if not os.path.exists(fromDir): printlog("fromDir or toDir is not exists!") return 1 # value文件夹下的xml如果有同名必须合并 valuePattern = re.compile(r".*/values.*") for parent, dirs, files in os.walk(fromDir): toFileParent = parent.replace(fromDir, toDir) for fileName in files: oriFile = "%s/%s" % (parent, fileName) toFile = "%s/%s" % (toFileParent, fileName) # PrintLog("oriFile: %s"%oriFile) # PrintLog("toFile: %s"%toFile) # PrintLog("parent:%s"%parent) # 是value文件夹 and 渠道资源与包有相同的文件 if valuePattern.match(parent) and os.path.exists(toFile): # PrintLog("merge file %s to file %s-------------------------"%(oriFile, toFile)) mergeResFile(oriFile, toFile) # 当包里没有与渠道相同的文件 elif not os.path.exists(toFile): # PrintLog("copy file %s to file %s--------------------------"%(oriFile, toFile)) os.system("mkdir -p %s" % (os.path.dirname(toFile))) shutil.copyfile(oriFile, toFile) pass return 0 def mergeResFile(fromFile, toFile): if not os.path.exists(fromFile): printlog("fromFile or toFile is not exists") return 1 # PrintLog("start to merge %s and %s "%(fromFile,toFile)) sourceTree = ET.parse(fromFile) desTree = ET.parse(toFile) # 临时打包目录 sourceRoot = sourceTree.getroot() desRoot = desTree.getroot() # SDK sourceChildren = list(sourceRoot) desChildren = list(desRoot) # 获取原文件attrib,用于排重 desNames = [] publicDict = {} prefixDict = {} for child in desChildren: # PrintLog(child.attrib) desNames.append(child.attrib["name"]) # 保存每个属性的最大id值 if fromFile.find("public.xml") > 0: if child.attrib["type"] not in publicDict.keys(): publicDict[child.attrib["type"]] = int(child.attrib["id"], 16) if child.attrib["type"] not in prefixDict.keys(): prefixDict[child.attrib["type"]] = child.attrib["id"][0:6] # PrintLog("prefixDict ---- ") # PrintLog(prefixDict) else: newPublicId = int(child.attrib["id"], 16) oldPublicId = publicDict[child.attrib["type"]] # publicDict[child.attrib["type"]] = newPublicId if newPublicId > oldPublicId else oldPublicId if newPublicId > oldPublicId: publicDict[child.attrib["type"]] = newPublicId # PrintLog("replace type:%s"%publicDict[child.attrib["type"]]) # PrintLog("replace oldPublicId:%d with newPublicId:%d"%(oldPublicId,newPublicId)) # 打印测试信息 # PrintLog("print publicDict ----------------------------") # PrintLog(publicDict) # PrintLog("print publicDict ----------------------------") for child in sourceChildren: if child.attrib["name"] in desNames: # 若有重复,使用原包里的的 index = desNames.index(child.attrib["name"]) # PrintLog("confilct child:%s"%desChildren[index].attrib["name"]) continue # PrintLog("append child name:%s"%child.attrib["name"]) # public.xml需要特殊合并 if fromFile.find("public.xml") > 0: if child.attrib["type"] in publicDict: # PrintLog("old id:%s --------------- type:%s"%(child.attrib["id"],child.attrib["type"])) publicDict[child.attrib["type"]] = publicDict[child.attrib["type"]] + 1 child.attrib["id"] = hex(publicDict[child.attrib["type"]]) # PrintLog("new id:%s ---------------"%child.attrib["id"]) # 如果ID不存在,则直接清0 elif child.attrib["type"] not in publicDict: # PrintLog("reset id to 00") # PrintLog("old id:%s ---------------"%child.attrib["id"]) last_preffix = sorted(prefixDict.values())[-1] # PrintLog("last object:%s"%last_preffix[-2:]) preffixNum = hex(int(last_preffix[-2:], 16) + 1)[last_preffix.index("x") + 1:] if len(preffixNum) < 2: preffixNum = "0" + preffixNum preffix = "0x7f" + preffixNum # PrintLog("preffix_suffix:%s"%preffix) prefixDict[child.attrib["type"]] = preffix publicDict[child.attrib["type"]] = int(preffix + "0000", 16) # PrintLog("publicDict with type:%s and value:%s"%(child.attrib["type"],publicDict[child.attrib["type"]])) child.attrib["id"] = hex(publicDict[child.attrib["type"]]) # PrintLog("publicDict[child.attrib['type']:%s"%publicDict[child.attrib["type"]]) # PrintLog("new id:%s ---------------"%child.attrib["id"]) desRoot.append(child) # PrintLog("print prefixDict ----------------------------") # PrintLog(prefixDict) # PrintLog("print prefixDict ----------------------------") # if fromFile.find("public.xml") > 0: # desRoot.getchildren().sort() # PrintLog("start to write xml") desTree.write(toFile, "utf-8", xml_declaration=True) # PrintLog("finished writing xml") # PrintLog("merging files finished") return 0 def mergeDir(fromDir, toDir): if not os.path.exists(fromDir): return 1 compareFile(fromDir, toDir) return 0 pass def compareFile(cmpFile, oriFile): exceptList = [] for parent, dirName, fileNames in os.walk(cmpFile): oriParent = parent.replace(cmpFile, oriFile) shouldRewrite = parent.rfind("third/sdk") for fileName in fileNames: if shouldRewrite >= 0 or (os.path.exists("%s/%s" % (oriParent, fileName)) == False and parent.find(contants.ignore_package_path) < 0): fromDir = "%s/%s" % (parent, fileName) # PrintLog("fromDir :%s"%fromDir) toDir = "%s/%s" % (oriParent, fileName) # PrintLog("toDir: %s"%toDir) copyFileForCompareFile(fromDir, toDir) pass def copyAllFile(fromFile, toFile): for parent, dirName, fileNames in os.walk(fromFile): oriParent = parent.replace(fromFile, toFile) for fileName in fileNames: fromDir = "%s/%s" % (parent, fileName) # PrintLog("fromDir :%s"%fromDir) toDir = "%s/%s" % (oriParent, fileName) # PrintLog("toDir: %s"%toDir) copyFileForCompareFile(fromDir, toDir) pass def copyFileForCompareFile(fromFile, toFile): if not os.path.exists(fromFile): printlog("file %s is not exists" % fromFile) return filePattern = re.compile(r".*\..*") toDir = toFile if filePattern.match(toFile): toDir = os.path.dirname(toFile) if not os.path.exists(toDir): os.makedirs(toDir) shutil.copy(fromFile, toFile) pass # 文件安全删除 def safeFileDelete(filePath): if os.path.exists(filePath): if os.path.isdir(filePath): shutil.rmtree(filePath) printlog(">>>>>>>>>>>>>>>>>>>>>>>>delete isdir filePath:<<<<<<<<<<<<<<<<<<<<<<<<<< %s" % filePath) elif os.path.isfile(filePath): printlog(">>>>>>>>>>>>>>>>>>>>>>>>delete isfile filePath:<<<<<<<<<<<<<<<<<<<<<<<<<< %s" % filePath) os.remove(filePath) return # 删除文件但不删除文件夹 def del_file(path_data): for i in os.listdir(path_data): # os.listdir(path_data)#返回一个列表,里面是当前目录下面的所有东西的相对路径 file_data = path_data + "/" + i # 当前文件夹的下面的所有东西的绝对路径 if os.path.isfile(file_data): # os.path.isfile判断是否为文件,如果是文件,就删除.如果是文件夹.递归给del_file. os.remove(file_data) else: del_file(file_data) def add_point_smali(sdkPath, apkPath): printlog("--------------------------- start to merge resources ---------------------------") ret = 0 # merge res # sdk_root_dir = apk_utils.getCutSdkRootHome() + "/" + os.path.basename(sdkPath) ret = ret | mergeFiles("%s/res" % sdkPath, "%s/res" % apkPath) # copy lib and assets # 避免由于架构导致复制无用架构 ret = ret | mergeLibs(sdkPath, apkPath) ret = ret | mergeFiles("%s/assets" % sdkPath, "%s/assets" % apkPath) ret = ret | mergeDir("%s/smali" % sdkPath, "%s/smali" % apkPath) def mergeYml(fromfile, tofile): if not os.path.exists(fromfile): return False, "origin yaml file is not exists" if not os.path.exists(tofile): return False, "destination yaml file is not exists" header = "!!brut.androlib.meta.MetaInfo\n" fromcontent = open(fromfile, "r").read() fromcontent = fromcontent.replace(header, "") tocontent = open(tofile, "r").read() tocontent = tocontent.replace(header, "") fromroot = yaml.safe_load(fromcontent) toroot = yaml.safe_load(tocontent) if fromroot is None: printlog("from root is empty") return False, "orgin yaml file cant not be parsed or empty" if toroot is None: printlog("to root is empty") return False, "destination yaml file cant not be parsed or is empty" donotcompresskey = "doNotCompress" if donotcompresskey in fromroot: arr = fromroot[donotcompresskey] if not donotcompresskey in toroot: toroot[donotcompresskey] = arr else: toarr = toroot[donotcompresskey] toroot[donotcompresskey] = list(set(toarr + arr)) unknownfileskey = "unknownFiles" if unknownfileskey in fromroot: fromunknownfilesarr = fromroot[unknownfileskey] else: fromunknownfilesarr = {} tounknownfilesarr = toroot[unknownfileskey] toroot[unknownfileskey] = dict(fromunknownfilesarr.items()).update(tounknownfilesarr.items()) desfile = open(tofile, "w") desfile.writelines(header) yaml.safe_dump(toroot, desfile, default_flow_style=False) return True, "" pass # sdk resources + apk resources def mergeResources(sdkPath, apkPath): printlog("--------------------------- start to merge resources ---------------------------") ret = 0 # merge res # sdk_root_dir = apk_utils.getCutSdkRootHome() + "/" + os.path.basename(sdkPath) ret = ret | mergeFiles("%s/res" % sdkPath, "%s/res" % apkPath) # copy lib and assets # 避免由于架构导致复制无用架构 ret = ret | mergeLibs(sdkPath, apkPath) ret = ret | mergeFiles("%s/assets" % sdkPath, "%s/assets" % apkPath) ret = ret | mergeDir("%s/smali" % sdkPath, "%s/smali" % apkPath) ret = ret | mergeDir("%s/smali_classes2" % sdkPath, "%s/smali_classes2" % apkPath) ret = ret | mergeDir("%s/smali_classes3" % sdkPath, "%s/smali_classes3" % apkPath) ret = ret | mergeDir("%s/smali_classes4" % sdkPath, "%s/smali_classes4" % apkPath) ret = ret | mergeDir("%s/smali_classes5" % sdkPath, "%s/smali_classes5" % apkPath) ret = ret | mergeDir("%s/smali_classes6" % sdkPath, "%s/smali_classes6" % apkPath) ret = ret | mergeDir("%s/unknown" % sdkPath, "%s/unknown" % apkPath) # 暂时的方案是用sdk的apktool.yml覆盖原包apktool.yml 后期需要做apktool.yml合并 if os.path.exists("%s/apktool.yml" % sdkPath): mergeYml("%s/apktool.yml" % sdkPath, "%s/apktool.yml" % apkPath) # shutil.copy("%s/apktool.yml"%sdkPath, "%s/apktool.yml"%apkPath) # 修改微信支付 # ret = ret | File.copyFiles("%s/smali"%sdkPath,"%s/smali"%apkPath) printlog("--------------------------- finished merging resources ---------------------------") return ret def get_lauch_activity_name(apk_decompile_tmp_dir): sdkAM = os.path.join(apk_decompile_tmp_dir, 'AndroidManifest.xml') ET.register_namespace('android', androidNS) tree = ET.parse(sdkAM) activitys = tree.getroot().find("application").findall("activity") for activity in activitys: filters = activity.findall('intent-filter') for filter in filters: categorys = filter.findall('category') for category in categorys: categoryname = category.attrib[namespace + "name"] if categoryname == "android.intent.category.LAUNCHER": actname = activity.attrib[namespace + "name"] return actname def get_lauch_activity_screen_orientation(apk_decompile_tmp_dir): sdkAM = os.path.join(apk_decompile_tmp_dir, 'AndroidManifest.xml') ET.register_namespace('android', androidNS) tree = ET.parse(sdkAM) activitys = tree.getroot().find("application").findall("activity") for activity in activitys: filters = activity.findall('intent-filter') for filter in filters: categorys = filter.findall('category') for category in categorys: categoryname = category.attrib[namespace + "name"] if categoryname == "android.intent.category.LAUNCHER": screenOrientation = activity.attrib[namespace + "screenOrientation"] return screenOrientation def modify_R_id_smali(prj_path, r_path): apkRfilePath = "%s%s" % (prj_path, r_path) publicXml = "%s/res/values/public.xml" % prj_path jarPath = "%s/MergeSamli.jar" % contants.tool_jar_home javaMerCm = 'java -jar %s %s %s' % (jarPath, publicXml, apkRfilePath) printlog("javaMerCm:%s" % javaMerCm) os.system(javaMerCm) pass def replace_manifest_allow_backup(am_path): lines = open(am_path).readlines() fp = open(am_path, "w") isReplaced = False for l in lines: if isReplaced == False and l.find("android:allowBackup=") >= 0: l = re.sub("android:allowBackup=\"true\"", "android:allowBackup=\"false\"", l) fp.write(l) fp.close() pass def replace_xml_value(xml_path, name, value): tree = ET.parse(xml_path) if tree is None: return for child in list(tree.getroot()): if child.attrib["name"] == name: child.text = value tree.write(xml_path, "utf-8", xml_declaration=True) pass def replace_game_launch_activity_attribute_to_default(splash_activity, manifest_path): ET.register_namespace('android', androidNS) tree = ET.parse(manifest_path) game_activity = "" activitys = tree.getroot().find("application").findall("activity") for activity in activitys: filters = activity.findall('intent-filter') for filter in filters: categorys = filter.findall('category') actions = filter.findall('action') for category in categorys: category_name = category.attrib[namespace + "name"] if category_name == "android.intent.category.LAUNCHER": action_name = activity.attrib[namespace + "name"] if action_name != splash_activity: game_activity = activity.attrib[namespace + "name"] printlog("delete :%s" % category_name) category.attrib[namespace + "name"] = "android.intent.category.DEFAULT" printlog("LAUNCHER replace DEFAULT :%s" % category.attrib[namespace + "name"]) for action in actions: action_name = action.attrib[namespace + "name"] if action_name == "android.intent.action.MAIN": printlog("delete :%s" % action_name) filter.remove(action) tree.write(manifest_path, "UTF-8", xml_declaration=True, method='xml') return game_activity def replace_json_value(path, root, key, value): if root == "": with open(path, 'r') as fp: jsonData = json.load(fp) jsonData[key] = value newJsonData = jsonData fp.close() with open(path, 'w') as fp: json.dump(newJsonData, fp) fp.close() else: with open(path, 'r') as fp: jsonData = json.load(fp) jsonData[root][key] = value newJsonData = jsonData fp.close() with open(path, 'w') as fp: json.dump(newJsonData, fp) fp.close() pass if __name__ == "__main__": # replaceAM_Meta_data("/sdk/develop/921sdk_cut/sdk_config/P0000001/anzhi/P0000004/AndroidManifest.xml", "QHOPENSDK_APPKEY", "20151219") # replaceLP_AppVersion(sys.argv[1], sys.argv[2]) # replaceAssets_Param("/var/root/shortcuts/921sdkcut/tools/param.cnf", "SECRET_KEY", "20151226") # special_replace("wdj", "/sdk/develop/tools/outputs/mxd/package/0.01.150608/360/test", "/", "APPKEY_ID", "20151228") pass