Replace.py 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528
  1. # -*-coding:utf-8-*-
  2. import json
  3. import os, shutil, yaml
  4. import re
  5. import xml.etree.ElementTree as ET
  6. import ChannelReplace, contants
  7. from print_log import printlog
  8. androidNS = 'http://schemas.android.com/apk/res/android'
  9. namespace = '{' + androidNS + '}'
  10. def special_replace(sdk_name, prj_path, key, value):
  11. # 获取特殊替换
  12. replaceFun = ChannelReplace.getSpecilReplace(sdk_name)
  13. if replaceFun is None:
  14. printlog("no such channel: %s" % sdk_name)
  15. else:
  16. printlog("begin special replace: %s" % sdk_name)
  17. replaceFun(prj_path, key, value)
  18. pass
  19. def modifyRfileSmaliPath(channel, prj_path):
  20. printlog("start copy and modify R.smali-------")
  21. manifestRoot = ET.parse("%s/AndroidManifest.xml" % prj_path).getroot()
  22. pkgName = manifestRoot.attrib["package"]
  23. sdk_root_dir = contants.channel_sdk_path
  24. sdkRfilePath = "%s/%s/%s/extension/smali" % (sdk_root_dir, channel, channel)
  25. printlog("sdkRfilePath:%s" % sdkRfilePath)
  26. if os.path.exists(sdkRfilePath):
  27. printlog("begin copy R.smali to apkpath---------")
  28. apkPkgDir = ET.parse("%s/AndroidManifest.xml" % prj_path).getroot().attrib["package"].replace(".", "/")
  29. apkRfilePath = "%s/smali/%s" % (prj_path, apkPkgDir)
  30. if not os.path.exists(apkRfilePath):
  31. os.makedirs(apkRfilePath)
  32. os.system("/bin/cp -r %s/* %s" % (sdkRfilePath, apkRfilePath))
  33. for parent, dirnames, filenames in os.walk(apkRfilePath):
  34. printlog("replace apkRfile dirnames:%s" % dirnames)
  35. for filename in filenames:
  36. filepath = "'%s/%s'" % (apkRfilePath, filename)
  37. replacePkgShell = 'sed -i "s#%s#%s#g" %s' % ("package_path", apkPkgDir, filepath)
  38. os.system(replacePkgShell)
  39. replacePkgShell = 'sed -i "s#%s#%s#g" %s' % ("package_name", pkgName, filepath)
  40. os.system(replacePkgShell)
  41. printlog("end copy R.smali file -------------------------------")
  42. def createRFile2Sdk(pkgAbsoluteRpath, sdkAbsoluteRpath, pkgRpath, sdkRpath):
  43. if os.path.exists(sdkAbsoluteRpath):
  44. del_file(sdkAbsoluteRpath)
  45. else:
  46. os.mkdir(sdkAbsoluteRpath)
  47. copyAllFile(pkgAbsoluteRpath, sdkAbsoluteRpath)
  48. printlog("pkgRpath: -------------%s" % pkgRpath)
  49. printlog("sdkRpath: -------------%s" % sdkRpath)
  50. for root, dirs, files in os.walk(sdkAbsoluteRpath):
  51. for file in files:
  52. if "$" in file:
  53. file = file.split('$', 1)[0] + "\$" + file.split('$', 1)[1]
  54. replaceKeyword(pkgRpath, sdkRpath, os.path.join(root, file))
  55. def replaceKeyword(key, value, file):
  56. replaceShell = 'sed -i "s#%s#%s#g" %s' % (key, value, file)
  57. printlog("replaceShell:%s" % replaceShell)
  58. os.system(replaceShell)
  59. pass
  60. def mergeLibs(sdkPath, apkPath):
  61. printlog("--------------------------- start to merge lib --------------------------------")
  62. apkLib = "%s/lib" % apkPath
  63. sdkLib = "%s/lib" % sdkPath
  64. ret = 0
  65. if not os.path.exists(apkLib) and os.path.exists(sdkLib):
  66. shutil.copytree(sdkLib, apkLib)
  67. printlog("finished merging lib --------------------------------")
  68. return ret
  69. apkFiles = os.listdir(apkLib)
  70. sdkFiles = os.listdir(sdkLib)
  71. for cpFile in sdkFiles:
  72. if os.path.isfile(cpFile) == False and cpFile not in apkFiles:
  73. continue
  74. ret = ret | mergeFiles("%s/lib/%s" % (sdkPath, cpFile), "%s/lib/%s" % (apkPath, cpFile))
  75. printlog("--------------------------- finished merging lib --------------------------------")
  76. return ret
  77. def mergeFiles(fromDir, toDir):
  78. # 必须合并的列表
  79. mergeList = ["attrs.xml", "colors.xml", "dimens.xml", "drawables.xml", "ids.xml", "public.xml", "strings.xml",
  80. "style.xml"]
  81. printlog(">>>>>>>>>>>>>>>>>>>>>>>>fromDir:<<<<<<<<<<<<<<<<<<<<<<<<<< %s" % fromDir)
  82. if not os.path.exists(fromDir):
  83. printlog("fromDir or toDir is not exists!")
  84. return 1
  85. # value文件夹下的xml如果有同名必须合并
  86. valuePattern = re.compile(r".*/values.*")
  87. for parent, dirs, files in os.walk(fromDir):
  88. toFileParent = parent.replace(fromDir, toDir)
  89. for fileName in files:
  90. oriFile = "%s/%s" % (parent, fileName)
  91. toFile = "%s/%s" % (toFileParent, fileName)
  92. # PrintLog("oriFile: %s"%oriFile)
  93. # PrintLog("toFile: %s"%toFile)
  94. # PrintLog("parent:%s"%parent)
  95. # 是value文件夹 and 渠道资源与包有相同的文件
  96. if valuePattern.match(parent) and os.path.exists(toFile):
  97. # PrintLog("merge file %s to file %s-------------------------"%(oriFile, toFile))
  98. mergeResFile(oriFile, toFile)
  99. # 当包里没有与渠道相同的文件
  100. elif not os.path.exists(toFile):
  101. # PrintLog("copy file %s to file %s--------------------------"%(oriFile, toFile))
  102. os.system("mkdir -p %s" % (os.path.dirname(toFile)))
  103. shutil.copyfile(oriFile, toFile)
  104. pass
  105. return 0
  106. def mergeResFile(fromFile, toFile):
  107. if not os.path.exists(fromFile):
  108. printlog("fromFile or toFile is not exists")
  109. return 1
  110. # PrintLog("start to merge %s and %s "%(fromFile,toFile))
  111. sourceTree = ET.parse(fromFile)
  112. desTree = ET.parse(toFile)
  113. # 临时打包目录
  114. sourceRoot = sourceTree.getroot()
  115. desRoot = desTree.getroot()
  116. # SDK
  117. sourceChildren = list(sourceRoot)
  118. desChildren = list(desRoot)
  119. # 获取原文件attrib,用于排重
  120. desNames = []
  121. publicDict = {}
  122. prefixDict = {}
  123. for child in desChildren:
  124. # PrintLog(child.attrib)
  125. desNames.append(child.attrib["name"])
  126. # 保存每个属性的最大id值
  127. if fromFile.find("public.xml") > 0:
  128. if child.attrib["type"] not in publicDict.keys():
  129. publicDict[child.attrib["type"]] = int(child.attrib["id"], 16)
  130. if child.attrib["type"] not in prefixDict.keys():
  131. prefixDict[child.attrib["type"]] = child.attrib["id"][0:6]
  132. # PrintLog("prefixDict ---- ")
  133. # PrintLog(prefixDict)
  134. else:
  135. newPublicId = int(child.attrib["id"], 16)
  136. oldPublicId = publicDict[child.attrib["type"]]
  137. # publicDict[child.attrib["type"]] = newPublicId if newPublicId > oldPublicId else oldPublicId
  138. if newPublicId > oldPublicId:
  139. publicDict[child.attrib["type"]] = newPublicId
  140. # PrintLog("replace type:%s"%publicDict[child.attrib["type"]])
  141. # PrintLog("replace oldPublicId:%d with newPublicId:%d"%(oldPublicId,newPublicId))
  142. # 打印测试信息
  143. # PrintLog("print publicDict ----------------------------")
  144. # PrintLog(publicDict)
  145. # PrintLog("print publicDict ----------------------------")
  146. for child in sourceChildren:
  147. if child.attrib["name"] in desNames:
  148. # 若有重复,使用原包里的的
  149. index = desNames.index(child.attrib["name"])
  150. # PrintLog("confilct child:%s"%desChildren[index].attrib["name"])
  151. continue
  152. # PrintLog("append child name:%s"%child.attrib["name"])
  153. # public.xml需要特殊合并
  154. if fromFile.find("public.xml") > 0:
  155. if child.attrib["type"] in publicDict:
  156. # PrintLog("old id:%s --------------- type:%s"%(child.attrib["id"],child.attrib["type"]))
  157. publicDict[child.attrib["type"]] = publicDict[child.attrib["type"]] + 1
  158. child.attrib["id"] = hex(publicDict[child.attrib["type"]])
  159. # PrintLog("new id:%s ---------------"%child.attrib["id"])
  160. # 如果ID不存在,则直接清0
  161. elif child.attrib["type"] not in publicDict:
  162. # PrintLog("reset id to 00")
  163. # PrintLog("old id:%s ---------------"%child.attrib["id"])
  164. last_preffix = sorted(prefixDict.values())[-1]
  165. # PrintLog("last object:%s"%last_preffix[-2:])
  166. preffixNum = hex(int(last_preffix[-2:], 16) + 1)[last_preffix.index("x") + 1:]
  167. if len(preffixNum) < 2:
  168. preffixNum = "0" + preffixNum
  169. preffix = "0x7f" + preffixNum
  170. # PrintLog("preffix_suffix:%s"%preffix)
  171. prefixDict[child.attrib["type"]] = preffix
  172. publicDict[child.attrib["type"]] = int(preffix + "0000", 16)
  173. # PrintLog("publicDict with type:%s and value:%s"%(child.attrib["type"],publicDict[child.attrib["type"]]))
  174. child.attrib["id"] = hex(publicDict[child.attrib["type"]])
  175. # PrintLog("publicDict[child.attrib['type']:%s"%publicDict[child.attrib["type"]])
  176. # PrintLog("new id:%s ---------------"%child.attrib["id"])
  177. desRoot.append(child)
  178. # PrintLog("print prefixDict ----------------------------")
  179. # PrintLog(prefixDict)
  180. # PrintLog("print prefixDict ----------------------------")
  181. # if fromFile.find("public.xml") > 0:
  182. # desRoot.getchildren().sort()
  183. # PrintLog("start to write xml")
  184. desTree.write(toFile, "utf-8", xml_declaration=True)
  185. # PrintLog("finished writing xml")
  186. # PrintLog("merging files finished")
  187. return 0
  188. def mergeDir(fromDir, toDir):
  189. if not os.path.exists(fromDir):
  190. return 1
  191. compareFile(fromDir, toDir)
  192. return 0
  193. pass
  194. def compareFile(cmpFile, oriFile):
  195. exceptList = []
  196. for parent, dirName, fileNames in os.walk(cmpFile):
  197. oriParent = parent.replace(cmpFile, oriFile)
  198. shouldRewrite = parent.rfind("third/sdk")
  199. for fileName in fileNames:
  200. if shouldRewrite >= 0 or (os.path.exists("%s/%s" % (oriParent, fileName)) == False
  201. and parent.find(contants.ignore_package_path) < 0):
  202. fromDir = "%s/%s" % (parent, fileName)
  203. # PrintLog("fromDir :%s"%fromDir)
  204. toDir = "%s/%s" % (oriParent, fileName)
  205. # PrintLog("toDir: %s"%toDir)
  206. copyFileForCompareFile(fromDir, toDir)
  207. pass
  208. def copyAllFile(fromFile, toFile):
  209. for parent, dirName, fileNames in os.walk(fromFile):
  210. oriParent = parent.replace(fromFile, toFile)
  211. for fileName in fileNames:
  212. fromDir = "%s/%s" % (parent, fileName)
  213. # PrintLog("fromDir :%s"%fromDir)
  214. toDir = "%s/%s" % (oriParent, fileName)
  215. # PrintLog("toDir: %s"%toDir)
  216. copyFileForCompareFile(fromDir, toDir)
  217. pass
  218. def copyFileForCompareFile(fromFile, toFile):
  219. if not os.path.exists(fromFile):
  220. printlog("file %s is not exists" % fromFile)
  221. return
  222. filePattern = re.compile(r".*\..*")
  223. toDir = toFile
  224. if filePattern.match(toFile):
  225. toDir = os.path.dirname(toFile)
  226. if not os.path.exists(toDir):
  227. os.makedirs(toDir)
  228. shutil.copy(fromFile, toFile)
  229. pass
  230. # 文件安全删除
  231. def safeFileDelete(filePath):
  232. if os.path.exists(filePath):
  233. if os.path.isdir(filePath):
  234. shutil.rmtree(filePath)
  235. printlog(">>>>>>>>>>>>>>>>>>>>>>>>delete isdir filePath:<<<<<<<<<<<<<<<<<<<<<<<<<< %s" % filePath)
  236. elif os.path.isfile(filePath):
  237. printlog(">>>>>>>>>>>>>>>>>>>>>>>>delete isfile filePath:<<<<<<<<<<<<<<<<<<<<<<<<<< %s" % filePath)
  238. os.remove(filePath)
  239. return
  240. # 删除文件但不删除文件夹
  241. def del_file(path_data):
  242. for i in os.listdir(path_data): # os.listdir(path_data)#返回一个列表,里面是当前目录下面的所有东西的相对路径
  243. file_data = path_data + "/" + i # 当前文件夹的下面的所有东西的绝对路径
  244. if os.path.isfile(file_data): # os.path.isfile判断是否为文件,如果是文件,就删除.如果是文件夹.递归给del_file.
  245. os.remove(file_data)
  246. else:
  247. del_file(file_data)
  248. def add_point_smali(sdkPath, apkPath):
  249. printlog("--------------------------- start to merge resources ---------------------------")
  250. ret = 0
  251. # merge res
  252. # sdk_root_dir = apk_utils.getCutSdkRootHome() + "/" + os.path.basename(sdkPath)
  253. ret = ret | mergeFiles("%s/res" % sdkPath, "%s/res" % apkPath)
  254. # copy lib and assets
  255. # 避免由于架构导致复制无用架构
  256. ret = ret | mergeLibs(sdkPath, apkPath)
  257. ret = ret | mergeFiles("%s/assets" % sdkPath, "%s/assets" % apkPath)
  258. ret = ret | mergeDir("%s/smali" % sdkPath, "%s/smali" % apkPath)
  259. def mergeYml(fromfile, tofile):
  260. if not os.path.exists(fromfile):
  261. return False, "origin yaml file is not exists"
  262. if not os.path.exists(tofile):
  263. return False, "destination yaml file is not exists"
  264. header = "!!brut.androlib.meta.MetaInfo\n"
  265. fromcontent = open(fromfile, "r").read()
  266. fromcontent = fromcontent.replace(header, "")
  267. tocontent = open(tofile, "r").read()
  268. tocontent = tocontent.replace(header, "")
  269. fromroot = yaml.safe_load(fromcontent)
  270. toroot = yaml.safe_load(tocontent)
  271. if fromroot is None:
  272. printlog("from root is empty")
  273. return False, "orgin yaml file cant not be parsed or empty"
  274. if toroot is None:
  275. printlog("to root is empty")
  276. return False, "destination yaml file cant not be parsed or is empty"
  277. donotcompresskey = "doNotCompress"
  278. if donotcompresskey in fromroot:
  279. arr = fromroot[donotcompresskey]
  280. if not donotcompresskey in toroot:
  281. toroot[donotcompresskey] = arr
  282. else:
  283. toarr = toroot[donotcompresskey]
  284. toroot[donotcompresskey] = list(set(toarr + arr))
  285. unknownfileskey = "unknownFiles"
  286. if unknownfileskey in fromroot:
  287. fromunknownfilesarr = fromroot[unknownfileskey]
  288. else:
  289. fromunknownfilesarr = {}
  290. tounknownfilesarr = toroot[unknownfileskey]
  291. toroot[unknownfileskey] = dict(fromunknownfilesarr.items()).update(tounknownfilesarr.items())
  292. desfile = open(tofile, "w")
  293. desfile.writelines(header)
  294. yaml.safe_dump(toroot, desfile, default_flow_style=False)
  295. return True, ""
  296. pass
  297. # sdk resources + apk resources
  298. def mergeResources(sdkPath, apkPath):
  299. printlog("--------------------------- start to merge resources ---------------------------")
  300. ret = 0
  301. # merge res
  302. # sdk_root_dir = apk_utils.getCutSdkRootHome() + "/" + os.path.basename(sdkPath)
  303. ret = ret | mergeFiles("%s/res" % sdkPath, "%s/res" % apkPath)
  304. # copy lib and assets
  305. # 避免由于架构导致复制无用架构
  306. ret = ret | mergeLibs(sdkPath, apkPath)
  307. ret = ret | mergeFiles("%s/assets" % sdkPath, "%s/assets" % apkPath)
  308. ret = ret | mergeDir("%s/smali" % sdkPath, "%s/smali" % apkPath)
  309. ret = ret | mergeDir("%s/smali_classes2" % sdkPath, "%s/smali_classes2" % apkPath)
  310. ret = ret | mergeDir("%s/smali_classes3" % sdkPath, "%s/smali_classes3" % apkPath)
  311. ret = ret | mergeDir("%s/smali_classes4" % sdkPath, "%s/smali_classes4" % apkPath)
  312. ret = ret | mergeDir("%s/smali_classes5" % sdkPath, "%s/smali_classes5" % apkPath)
  313. ret = ret | mergeDir("%s/smali_classes6" % sdkPath, "%s/smali_classes6" % apkPath)
  314. ret = ret | mergeDir("%s/unknown" % sdkPath, "%s/unknown" % apkPath)
  315. # 暂时的方案是用sdk的apktool.yml覆盖原包apktool.yml 后期需要做apktool.yml合并
  316. if os.path.exists("%s/apktool.yml" % sdkPath):
  317. mergeYml("%s/apktool.yml" % sdkPath, "%s/apktool.yml" % apkPath)
  318. # shutil.copy("%s/apktool.yml"%sdkPath, "%s/apktool.yml"%apkPath)
  319. # 修改微信支付
  320. # ret = ret | File.copyFiles("%s/smali"%sdkPath,"%s/smali"%apkPath)
  321. printlog("--------------------------- finished merging resources ---------------------------")
  322. return ret
  323. def get_lauch_activity_name(apk_decompile_tmp_dir):
  324. sdkAM = os.path.join(apk_decompile_tmp_dir, 'AndroidManifest.xml')
  325. ET.register_namespace('android', androidNS)
  326. tree = ET.parse(sdkAM)
  327. activitys = tree.getroot().find("application").findall("activity")
  328. for activity in activitys:
  329. filters = activity.findall('intent-filter')
  330. for filter in filters:
  331. categorys = filter.findall('category')
  332. for category in categorys:
  333. categoryname = category.attrib[namespace + "name"]
  334. if categoryname == "android.intent.category.LAUNCHER":
  335. actname = activity.attrib[namespace + "name"]
  336. return actname
  337. def get_lauch_activity_screen_orientation(apk_decompile_tmp_dir):
  338. sdkAM = os.path.join(apk_decompile_tmp_dir, 'AndroidManifest.xml')
  339. ET.register_namespace('android', androidNS)
  340. tree = ET.parse(sdkAM)
  341. activitys = tree.getroot().find("application").findall("activity")
  342. for activity in activitys:
  343. filters = activity.findall('intent-filter')
  344. for filter in filters:
  345. categorys = filter.findall('category')
  346. for category in categorys:
  347. categoryname = category.attrib[namespace + "name"]
  348. if categoryname == "android.intent.category.LAUNCHER":
  349. screenOrientation = activity.attrib[namespace + "screenOrientation"]
  350. return screenOrientation
  351. def modify_R_id_smali(prj_path, r_path):
  352. apkRfilePath = "%s%s" % (prj_path, r_path)
  353. publicXml = "%s/res/values/public.xml" % prj_path
  354. jarPath = "%s/MergeSamli.jar" % contants.tool_jar_home
  355. javaMerCm = 'java -jar %s %s %s' % (jarPath, publicXml, apkRfilePath)
  356. printlog("javaMerCm:%s" % javaMerCm)
  357. os.system(javaMerCm)
  358. pass
  359. def replace_manifest_allow_backup(am_path):
  360. lines = open(am_path).readlines()
  361. fp = open(am_path, "w")
  362. isReplaced = False
  363. for l in lines:
  364. if isReplaced == False and l.find("android:allowBackup=") >= 0:
  365. l = re.sub("android:allowBackup=\"true\"", "android:allowBackup=\"false\"", l)
  366. fp.write(l)
  367. fp.close()
  368. pass
  369. def replace_xml_value(xml_path, name, value):
  370. tree = ET.parse(xml_path)
  371. if tree is None:
  372. return
  373. for child in list(tree.getroot()):
  374. if child.attrib["name"] == name:
  375. child.text = value
  376. tree.write(xml_path, "utf-8", xml_declaration=True)
  377. pass
  378. def replace_game_launch_activity_attribute_to_default(splash_activity, manifest_path):
  379. ET.register_namespace('android', androidNS)
  380. tree = ET.parse(manifest_path)
  381. game_activity = ""
  382. activitys = tree.getroot().find("application").findall("activity")
  383. for activity in activitys:
  384. filters = activity.findall('intent-filter')
  385. for filter in filters:
  386. categorys = filter.findall('category')
  387. actions = filter.findall('action')
  388. for category in categorys:
  389. category_name = category.attrib[namespace + "name"]
  390. if category_name == "android.intent.category.LAUNCHER":
  391. action_name = activity.attrib[namespace + "name"]
  392. if action_name != splash_activity:
  393. game_activity = activity.attrib[namespace + "name"]
  394. printlog("delete :%s" % category_name)
  395. category.attrib[namespace + "name"] = "android.intent.category.DEFAULT"
  396. printlog("LAUNCHER replace DEFAULT :%s" % category.attrib[namespace + "name"])
  397. for action in actions:
  398. action_name = action.attrib[namespace + "name"]
  399. if action_name == "android.intent.action.MAIN":
  400. printlog("delete :%s" % action_name)
  401. filter.remove(action)
  402. tree.write(manifest_path, "UTF-8", xml_declaration=True, method='xml')
  403. return game_activity
  404. def replace_json_value(path, root, key, value):
  405. if root == "":
  406. with open(path, 'r') as fp:
  407. jsonData = json.load(fp)
  408. jsonData[key] = value
  409. newJsonData = jsonData
  410. fp.close()
  411. with open(path, 'w') as fp:
  412. json.dump(newJsonData, fp)
  413. fp.close()
  414. else:
  415. with open(path, 'r') as fp:
  416. jsonData = json.load(fp)
  417. jsonData[root][key] = value
  418. newJsonData = jsonData
  419. fp.close()
  420. with open(path, 'w') as fp:
  421. json.dump(newJsonData, fp)
  422. fp.close()
  423. pass
  424. if __name__ == "__main__":
  425. # replaceAM_Meta_data("/sdk/develop/921sdk_cut/sdk_config/P0000001/anzhi/P0000004/AndroidManifest.xml", "QHOPENSDK_APPKEY", "20151219")
  426. # replaceLP_AppVersion(sys.argv[1], sys.argv[2])
  427. # replaceAssets_Param("/var/root/shortcuts/921sdkcut/tools/param.cnf", "SECRET_KEY", "20151226")
  428. # special_replace("wdj", "/sdk/develop/tools/outputs/mxd/package/0.01.150608/360/test", "/", "APPKEY_ID", "20151228")
  429. pass