From 0ea70324cb2dc23b357984df0b0ea4978514e35c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9E=97=E6=96=87=E6=9D=B0?= Date: Thu, 31 Dec 2020 16:38:20 +0800 Subject: [PATCH] =?UTF-8?q?[add]=E6=B7=BB=E5=8A=A0cordova=E5=AE=8C?= =?UTF-8?q?=E6=95=B4=E6=80=A7=E6=A3=80=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- LICENSE | 21 + README.md | 6 + package.json | 26 ++ plugin.xml | 47 ++ scripts/clear_hashes.js | 30 ++ scripts/helpers/error_exit.js | 4 + scripts/helpers/getFileMapContent.js | 32 ++ scripts/helpers/get_platforms_list.js | 5 + scripts/helpers/index.js | 26 ++ scripts/helpers/is_verbose.js | 4 + scripts/save_assets_hashes.js | 111 +++++ scripts/save_res_hashes.js | 415 ++++++++++++++++++ .../com/cescit/integrity/ApkIntegrity.java | 92 ++++ .../com/cescit/integrity/AssetsIntegrity.java | 87 ++++ .../com/cescit/integrity/CescitIntegrity.java | 98 +++++ src/android/com/cescit/integrity/Config.java | 20 + .../com/cescit/integrity/DebugDetection.java | 28 ++ .../com/cescit/integrity/HttpUtil.java | 58 +++ .../com/cescit/integrity/ResIntegrity.java | 118 +++++ .../cescit/integrity/TamperingException.java | 9 + www/AntiTampering.js | 22 + 21 files changed, 1259 insertions(+) create mode 100644 LICENSE create mode 100644 README.md create mode 100644 package.json create mode 100644 plugin.xml create mode 100644 scripts/clear_hashes.js create mode 100644 scripts/helpers/error_exit.js create mode 100644 scripts/helpers/getFileMapContent.js create mode 100644 scripts/helpers/get_platforms_list.js create mode 100644 scripts/helpers/index.js create mode 100644 scripts/helpers/is_verbose.js create mode 100644 scripts/save_assets_hashes.js create mode 100644 scripts/save_res_hashes.js create mode 100644 src/android/com/cescit/integrity/ApkIntegrity.java create mode 100644 src/android/com/cescit/integrity/AssetsIntegrity.java create mode 100644 src/android/com/cescit/integrity/CescitIntegrity.java create mode 100644 src/android/com/cescit/integrity/Config.java create mode 100644 src/android/com/cescit/integrity/DebugDetection.java create mode 100644 src/android/com/cescit/integrity/HttpUtil.java create mode 100644 src/android/com/cescit/integrity/ResIntegrity.java create mode 100644 src/android/com/cescit/integrity/TamperingException.java create mode 100644 www/AntiTampering.js diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..131b4ee --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License + +Copyright (c) 2016 Davide Doronzo + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..ee79ac5 --- /dev/null +++ b/README.md @@ -0,0 +1,6 @@ +Cescit-Integrity Cordova Plugin +============================= + +本插件基于cordova-plugin-antitampering,有apk完整性检验,res资源检验,assert资源检验的功能。 + +Supports Android diff --git a/package.json b/package.json new file mode 100644 index 0000000..2909054 --- /dev/null +++ b/package.json @@ -0,0 +1,26 @@ +{ + "name": "cordova-plugin-cescit-integrity", + "version": "1.0.0", + "author": { + "name": "lwj" + }, + "description": "本插件基于cordova-plugin-antitampering,有apk完整性检验,res资源检验,assert资源检验的功能。", + "cordova": { + "id": "cordova-plugin-cescit-integrity", + "platforms": [ + "android" + ] + }, + "repository": { + "type": "git", + "url": "git+https://github.com/CESCIT/cordova-plugin-cescit-integrity.git" + }, + "keywords": [ + "ecosystem:cordova", + "cordova-android" + ], + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "license": "ISC" +} \ No newline at end of file diff --git a/plugin.xml b/plugin.xml new file mode 100644 index 0000000..ed516c9 --- /dev/null +++ b/plugin.xml @@ -0,0 +1,47 @@ + + + cescit-integrity + 基于cordova-plugin-antitampering的文件完整性检验 - Android + lwj + MIT + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/scripts/clear_hashes.js b/scripts/clear_hashes.js new file mode 100644 index 0000000..8f61c17 --- /dev/null +++ b/scripts/clear_hashes.js @@ -0,0 +1,30 @@ +#!/usr/bin/env node + +var helpers = require('./helpers'); + +module.exports = function (context) { + var fs = require('fs'); + var fileList = ['AssetsIntegrity', 'ResIntegrity', 'ApkIntegrity']; + process.stdout.write('[完整性检验] Clearing assets hash from previous build\n'); + + helpers.getPlatformsList(context).forEach(function (platform) { + fileList.forEach(fileName => { + var source = helpers.getFileMapContent(context, platform, fileName); + var content = source.content; + + let regexp = '' + if(fileName == 'AssetsIntegrity') regexp = /hashList\s*=.+\s*new.*(\(\d+\)[^\w]*)\);/ + else if(fileName == 'ResIntegrity') regexp = /hashList\s*=.+\s*new.*(\(\d+\)[^\w]*)\);/ + else if(fileName == 'ApkIntegrity') regexp = /hashList\s*=.+\s*new.*(\(\d+\)[^\w]*)\);/ + content = source.content.replace(/\s*put\("[^"]+",\s"[^"]{64}"\);/g, '') + .replace(regexp, function (match, group) { + return match.replace(group, '()\n '); + }); + try { + fs.writeFileSync(source.path, content, 'utf-8'); + } catch (e) { + helpers.exit('Unable to write java class source at path ' + source.path, e); + } + }) + }); +}; diff --git a/scripts/helpers/error_exit.js b/scripts/helpers/error_exit.js new file mode 100644 index 0000000..12a1bb1 --- /dev/null +++ b/scripts/helpers/error_exit.js @@ -0,0 +1,4 @@ +module.exports = function (msg, exception) { + process.stdout.write('\n[完整性检验] ERROR! ' + msg + '\n'); + throw new Error(exception); +}; diff --git a/scripts/helpers/getFileMapContent.js b/scripts/helpers/getFileMapContent.js new file mode 100644 index 0000000..51407cb --- /dev/null +++ b/scripts/helpers/getFileMapContent.js @@ -0,0 +1,32 @@ +var helpers = require('./'); + +module.exports = function (platform, fileName) { + var path = require('path'); + var fs = require('fs'); + var cordovaUtil = this.requireCordovaModule('cordova-lib/src/cordova/util'); + var projectRoot = cordovaUtil.isCordova(); + var platformPath = path.join(projectRoot, 'platforms', platform); + var sourceFile; + var content; + + if (platform === 'android') { + var fileBasename = fileName; + var filePath = 'com/cescit/integrity/' + fileBasename + '.java'; + try { + sourceFile = path.join(platformPath, 'app/src/main/java', filePath); + content = fs.readFileSync(sourceFile, 'utf-8'); + } catch (_e) { + try { + sourceFile = path.join(platformPath, 'src', filePath); + content = fs.readFileSync(sourceFile, 'utf-8'); + } catch (e) { + helpers.exit('Unable to read java class source at path ' + sourceFile, e); + } + } + } + + return { + content: content, + path: sourceFile + }; +}; diff --git a/scripts/helpers/get_platforms_list.js b/scripts/helpers/get_platforms_list.js new file mode 100644 index 0000000..d9810ed --- /dev/null +++ b/scripts/helpers/get_platforms_list.js @@ -0,0 +1,5 @@ +module.exports = function () { + return (this.opts.platforms || this.opts.cordova.platforms || []).filter(function (platform) { + return this.opts.plugin.pluginInfo.getPlatformsArray().indexOf(platform) > -1; + }, this); +}; diff --git a/scripts/helpers/index.js b/scripts/helpers/index.js new file mode 100644 index 0000000..07ad261 --- /dev/null +++ b/scripts/helpers/index.js @@ -0,0 +1,26 @@ +/** + * Exit script with custom error log. + * @param {string} msg - Error message. + * @param {Error} exception + */ +exports.exit = require('./error_exit'); + +/** + * Get the real list of platforms affected by a running plugin hook. + * @param {Object} context - Cordova context. + */ +exports.getPlatformsList = invokeHelper.bind(null, './get_platforms_list'); + +/** + * Detect if the context process is running with verbose option. + * @param {Object} context - Cordova context. + */ +exports.isVerbose = invokeHelper.bind(null, './is_verbose'); + +exports.getFileMapContent = invokeHelper.bind(null, './getFileMapContent'); + +function invokeHelper (path) { + var helper = require(path); + var context = arguments[1]; + return helper.apply(context, Array.prototype.splice.call(arguments, 2)); +} diff --git a/scripts/helpers/is_verbose.js b/scripts/helpers/is_verbose.js new file mode 100644 index 0000000..f759d46 --- /dev/null +++ b/scripts/helpers/is_verbose.js @@ -0,0 +1,4 @@ +module.exports = function () { + return this.opts && this.opts.options && this.opts.options.verbose || + typeof this.cmdLine === 'string' && this.cmdLine.indexOf(' -verbose') > -1; +}; diff --git a/scripts/save_assets_hashes.js b/scripts/save_assets_hashes.js new file mode 100644 index 0000000..e6bfb48 --- /dev/null +++ b/scripts/save_assets_hashes.js @@ -0,0 +1,111 @@ +#!/usr/bin/env node + +var crypto = require('crypto'); +var helpers = require('./helpers'); + +module.exports = function (context) { + var path = require('path'); + var fs = require('fs'); + var cordovaUtil = context.requireCordovaModule('cordova-lib/src/cordova/util'); + var platforms = context.requireCordovaModule('cordova-lib/src/platforms/platforms'); + var projectRoot = cordovaUtil.isCordova(); + + process.stdout.write('[完整性检验] Saving a hash for each platforms asset \n'); + + function getPlatformAssets (dir) { + var assetsList = []; + var list = fs.readdirSync(dir); + list.map(function (file) { + var filePath = path.join(dir, file); + if (fs.statSync(filePath).isDirectory()) { + var subDirList = getPlatformAssets(filePath); + assetsList = assetsList.concat(subDirList); + } + if (fs.statSync(filePath).isFile()) { + assetsList.push(filePath); + } + }); + return assetsList; + } + + helpers.getPlatformsList(context).forEach(function (platform) { + var platformPath = path.join(projectRoot, 'platforms', platform); + var platformApi = platforms.getPlatformApi(platform, platformPath); + var platformInfo = platformApi.getPlatformInfo(); + var platformWww = platformInfo.locations.www; + var platformAssets = path.resolve(platformWww, '../') // assets文件夹 + var source = helpers.getFileMapContent(context, platform, 'AssetsIntegrity'); + var content = source.content; + + var hashes = getPlatformAssets(platformAssets).map(function (file) { + var fileName = file.replace(/\\/g, '/'); + fileName = fileName.replace(platformAssets.replace(/\\/g, '/') + '/', ''); + var hash; + var hashHex; + hash = crypto.createHash('sha256'); + try { + hash.update(fs.readFileSync(file), 'utf8'); + } catch (e) { + helpers.exit('Unable to read file at path ' + file, e); + } + hashHex = hash.digest('hex'); + if (helpers.isVerbose(context)) { + process.stdout.write('Hash: ' + hashHex + ' < ' + fileName + '\n'); + } + return { + // file: Buffer.from(fileName).toString('base64'), + file: fileName, + hash: hashHex + }; + }); + + if (platform === 'android') { + content = content.replace(/\s*put\("[^"]+",\s"[^"]{64}"\);/g, '') + .replace(/hashList\s*=.+\s*new.*(\(\d+\)[^\w]*)\);/, function (match, group) { + return match.replace(group, '()\n' + tab()); + }) + .replace(/hashList\s*=.+\s*new.*(\(.*\))/, function (match, group) { + var replace = match.replace(group, '(' + (hashes.length || '') + ')'); + if (hashes.length) { + replace += ' {{\n' + tab(); + hashes.forEach(function (h) { + replace += tab(2) + 'put("' + h.file + '", "' + h.hash + '");\n' + tab(); + }); + replace += tab() + '}}'; + } + return replace; + }); + + try { + fs.writeFileSync(source.path, content, 'utf-8'); + } catch (e) { + helpers.exit('Unable to write java class source at path ' + source.path, e); + } + } + + if (platform === 'ios') { + content = content.replace(/hashList = (@{([^}]*)});/, function (a, b) { + var list = '@{\n' + tab(); + hashes.forEach(function (h) { + list += tab() + '@"' + h.file + '": @"' + h.hash + '",\n' + tab(); + }); + list += '}'; + return a.replace(b, list); + }); + + try { + fs.writeFileSync(source.path, content, 'utf-8'); + } catch (e) { + helpers.exit('Unable to write obj-c source at path ' + source.path, e); + } + } + }); + + function tab (size) { + var str = ''; + for (var i = 0; i < (size || 1); i++) { + str += ' '; + } + return str; + } +}; diff --git a/scripts/save_res_hashes.js b/scripts/save_res_hashes.js new file mode 100644 index 0000000..68dab1b --- /dev/null +++ b/scripts/save_res_hashes.js @@ -0,0 +1,415 @@ +#!/usr/bin/env node + +var crypto = require('crypto'); +var helpers = require('./helpers'); + +module.exports = function (context) { + var path = require('path'); + var fs = require('fs'); + var cordovaUtil = context.requireCordovaModule('cordova-lib/src/cordova/util'); + var platforms = context.requireCordovaModule('cordova-lib/src/platforms/platforms'); + var projectRoot = cordovaUtil.isCordova(); + + process.stdout.write('[完整性检验] Saving a hash for each platforms res \n'); + + function getHashList() { + let str = ` + put("res/drawable-xhdpi-v4/abc_scrubber_control_to_pressed_mtrl_005.png", "d9cf644f86abcabfb29d7020e1f9bf51daf851ecb5b652eb60e668654cc68cd1") + put("res/drawable-mdpi-v4/abc_textfield_search_default_mtrl_alpha.9.png", "6948c22791982b06d1d911fd6212ee29bf8a819196ec01f60f5ddcf852f5394f") + put("res/drawable-hdpi-v4/abc_list_longpressed_holo.9.png", "d5c644b00ee79bd4f1fcb789ebbf89e79c0c6fea878dfbc4a8c64c3be34b5b03") + put("res/drawable-mdpi-v4/abc_scrubber_control_to_pressed_mtrl_005.png", "6111c327f535aa76f4ceaa6b1f91c23252c3936f86e52100795594ae00e73b0f") + put("res/layout-v17/select_dialog_singlechoice_material.xml", "663a381007b438d9eeaa4f535323bc5a975e23bb2df0207b359545cb493d5347") + put("res/drawable-xxxhdpi-v4/abc_btn_switch_to_on_mtrl_00012.9.png", "dfe5008780f6427172ccb49cd0c2f5ccd0aaa3be3921b875362c9e65a08df76c") + put("res/drawable-hdpi-v4/abc_ic_menu_cut_mtrl_alpha.png", "500419896d42ee0818f210db317439df7458ad9f6f68f8b33690bd6f18c7d05a") + put("res/drawable-xxhdpi-v4/abc_list_divider_mtrl_alpha.9.png", "49903073ab23abfc11a55e1333eb2905c743e1b382006c27c15434fd76e0207c") + put("res/layout/zxing_capture.xml", "91c24841125766e60e238c2cc84eb50e98fd2accbdaa6bcfec4331e0f50cbf64") + put("res/drawable-ldrtl-xxxhdpi-v4/abc_spinner_mtrl_am_alpha.9.png", "21b9bc9ca5bf09b7390dc9848395f81aca27dc6a44dd71f44a4718ee2698176a") + put("res/drawable-xxhdpi-v4/ic_action_remove.png", "a3bc9705eab4c61770b493dab577eb1cfc6d96e70c735a6cfd6da9b44028e728") + put("res/drawable-xxhdpi-v4/abc_scrubber_primary_mtrl_alpha.9.png", "178c653f6fdf2201b3eb6575839af350499f5c357a0fc2b7969a99824adf88f2") + put("res/drawable-xhdpi-v4/abc_switch_track_mtrl_alpha.9.png", "8154b2e598f4cd681133db7634f89194ef4c7dd6d0f48eb48480f93234848400") + put("res/drawable-xxxhdpi-v4/abc_tab_indicator_mtrl_alpha.9.png", "323f7fbb7907c8df09c96bb41ae71ebc0b106a046ce4461510ad8f6424ed44a6") + put("res/layout/abc_action_bar_view_list_nav_layout.xml", "37d81d4b028ad5194a93fdbd2cb6f1b0e5b5317a2415e6d6534f7644a19a3a9b") + put("res/drawable/abc_list_selector_background_transition_holo_light.xml", "3553ef7b4d6984143bc4ee5bfd2556c1627cac4301c1800a1b7ae012c34905bc") + put("res/drawable-hdpi-v4/abc_scrubber_control_to_pressed_mtrl_000.png", "540d0e23cd0c32a128c17bcee2982f1a47ef20ed1bf27fd0ee68db8ce1fabee4") + put("res/drawable-xxhdpi-v4/ic_action_next_item.png", "058f15a4afa192620e0bc8e4a7268f494a89495722337f889ed3109655a88d82") + put("res/color/abc_primary_text_disable_only_material_dark.xml", "b0eaa0e755bc2bf1d8cafe537920e0ceb507ac43f037806cbd2c4b9f92162cfe") + put("res/drawable-xxhdpi-v4/abc_textfield_search_default_mtrl_alpha.9.png", "b6e9825dcb2642d75a4ec0b4fbbec30c2fcd138c151290d3330682b588b3714f") + put("res/anim/abc_slide_in_bottom.xml", "18a752f91c63b61542bd743fab11657ccb0e11df795410c9cbb7b23b94651335") + put("res/drawable-mdpi-v4/abc_textfield_default_mtrl_alpha.9.png", "089fd6dbea6ef15371447199ab8fef7f17d4acee7362f33692a565d4dafe1bdf") + put("res/layout/notification_template_part_chronometer.xml", "d51d12dbdbfa1db0238a35ba91610e628f35f95ded74eca929d243976ce46b36") + put("res/drawable-xhdpi-v4/abc_list_pressed_holo_light.9.png", "270ec2c94be001d62c967e5f5bebd180e24c48c10cfd7c108fd667378cfc95a9") + put("res/layout/abc_alert_dialog_button_bar_material.xml", "31ff21c6b8e73e150630521ab3d39fdc8b802731ec7cd9fd8792e044c470bba4") + put("res/drawable-ldrtl-mdpi-v4/abc_ic_ab_back_mtrl_am_alpha.png", "40a015816f1754c099902ba322b2942d1f9fd91752615e160ad7dfd30a5abdec") + put("res/drawable-mdpi-v4/abc_scrubber_primary_mtrl_alpha.9.png", "dfd5ae1cebc27420abc8ed8a789c3645caa665c1ea2a11577cb6468cb173f895") + put("res/drawable-land-mdpi-v4/screen.png", "bc34a82eb2bd4abc1fd24a5fc0a7d1afda50ac1783327d50f610647f2b25f770") + put("res/drawable-xxxhdpi-v4/abc_ic_menu_cut_mtrl_alpha.png", "4eeb04eb02e6f07453e1d1972c5bea7be288c1100e508c4e106272360f8443d3") + put("res/layout/abc_action_menu_item_layout.xml", "5788c0d59e649fe9bfa1cc6b5d054695472fdc7734ce9e69dc023d421dc464ca") + put("res/drawable-xhdpi-v4/abc_list_selector_disabled_holo_light.9.png", "51d83e107f732b13b915e32b185c78e4ad70fc6d0c23daa200f5ad710e7d4525") + put("res/drawable-xhdpi-v4/abc_list_pressed_holo_dark.9.png", "330791ec39b85952980ff0fa119f4360f6c68be646eae3fcc2e0129c485e017c") + put("res/drawable-mdpi-v4/ic_action_previous_item.png", "903b76da45e94a166cba5adcf1b0a07d24af13eb7b21252a8c7a06c8d81a7c83") + put("res/drawable-xxxhdpi-v4/abc_ic_clear_mtrl_alpha.png", "a7edc1182f345588539e644543e6f8ab4c4aec3b5919bd29becb93147622eb11") + put("res/drawable-mdpi-v4/abc_list_pressed_holo_dark.9.png", "50b3049da5275342546813eb61495d611bb82db21ef771846b9ca2b7d5c2d1fc") + put("res/drawable-hdpi-v4/abc_ab_share_pack_mtrl_alpha.9.png", "81fbbbc931441a4f6d64680a9d2f6c645c79209010a572f2ea0b732143039e73") + put("res/drawable-xxhdpi-v4/abc_list_pressed_holo_dark.9.png", "805839b77513ec41b4bf4b2e5ee12ed06138d319621eed15bb049d31165ce08b") + put("res/drawable/abc_list_selector_background_transition_holo_dark.xml", "2a67a427df005ed7b8d3aef7cdc131684ffa5d41d5a4734bffeeeaf1824f317c") + put("res/drawable-hdpi-v4/abc_btn_switch_to_on_mtrl_00001.9.png", "0d1f5a3e36d02311640190fd768ee8d2607beba7a90b411be77a48a36427cb77") + put("res/drawable-xhdpi-v4/abc_btn_radio_to_on_mtrl_000.png", "d19092254aa4133fba8b7795fb5db560ad5dcd16c6f79201bec31b0f1e9b608b") + put("res/drawable-hdpi-v4/abc_spinner_mtrl_am_alpha.9.png", "9be46560847b3596a1406bda8b7a8449477f3314206c9a19b53e6703653db4d7") + put("res/xml/config.xml", "e2a12496c7a21deeb3a7b5f9b595ab7bc83dda00a4207e04f03f09126df6ce1d") + put("res/layout/abc_search_dropdown_item_icons_2line.xml", "0853dd90f73c408322e623d532ae54fd3947618bb37f19d187efd568d2e0e571") + put("res/drawable-hdpi-v4/abc_tab_indicator_mtrl_alpha.9.png", "7f537a60f30b3e56e0c091bae0747a3407fdcd9d648e6fa61ec6b44331a8bb0d") + put("res/drawable-hdpi-v4/abc_btn_check_to_on_mtrl_015.png", "5ced2ab774853050255d7309e10b76217813d56f3f8ab8fb73ac3f635d90c676") + put("res/drawable/abc_cab_background_top_material.xml", "69b62b2562a9ac8df2efefb63c00c24dfc975e76c0b46561973a10642189394c") + put("res/layout-v17/abc_search_view.xml", "bfcc1746978149788fb7d4efbd4516464a7566583527b1edfdb0333b7fc7815c") + put("res/color-v11/abc_background_cache_hint_selector_material_dark.xml", "261c978a306f7388c522ca0af7edc0700b33d5eb83e08b9b9ddc513ab5c24d90") + put("res/drawable-xxxhdpi-v4/abc_ic_menu_copy_mtrl_am_alpha.png", "b3ca8774b4123d57482664a575a513369c7bda589ffca5c0b10a88b800c52235") + put("res/color/abc_primary_text_material_light.xml", "5859bc2fd64e2bba5f94a50a3bd586596bf3f6cd8945220d7e8c706c671484fa") + put("res/drawable-xhdpi-v4/abc_ic_go_search_api_mtrl_alpha.png", "c79caef928e8b856634a0ead1a31dad8f755ca02918ce3dbe26a2252e1300a96") + put("res/drawable-xhdpi-v4/abc_scrubber_control_off_mtrl_alpha.png", "5c6a585c92347593e7344d8fd432487f9716fb6dec72ebd270ed9c8d5b597cb2") + put("res/drawable-xhdpi-v4/abc_list_selector_disabled_holo_dark.9.png", "4ba313b3b8c9e8dd9536dc180d7ba6147b6954fa69a64e3bf38fc5dac5f1863f") + put("res/layout/abc_screen_simple_overlay_action_mode.xml", "3e8596db7c3f547bc578d0c246678282f8f1d92f3a328a72b52bac4ee1658cc5") + put("res/drawable-xhdpi-v4/abc_ic_voice_search_api_mtrl_alpha.png", "e689b9c84076ff5c339e2a3c6914321458d5c603a0596564e623442cc783e76c") + put("res/drawable-xhdpi-v4/abc_ab_share_pack_mtrl_alpha.9.png", "dd8dbab387958637a6b7d78c8d6cd88da4370b82c6f2767562b959e1db954b8e") + put("res/drawable-ldrtl-xxxhdpi-v4/abc_ic_ab_back_mtrl_am_alpha.png", "753e4526bd3dd72cf154bb2dd6595e0bab1a002540aba793cb7f0e67dd8adfef") + put("res/drawable-xhdpi-v4/abc_ic_menu_moreoverflow_mtrl_alpha.png", "31e46a844a40519da6b9168b7a631cbddf0137671c0cd25485c68a60a1f02fcb") + put("res/layout-v17/notification_template_big_media_narrow.xml", "1f037610ac5a05a48eb512aba265972637302c24706f2ea1e1b90d69280c1544") + put("res/drawable-ldrtl-xxhdpi-v4/abc_ic_menu_copy_mtrl_am_alpha.png", "8b21794e8bcfb520ee54d2c8162521fc74087d091a24ea3d22ffd1031a111fdb") + put("res/drawable-xxhdpi-v4/abc_cab_background_top_mtrl_alpha.9.png", "966103c0bb0723292dfbc415653363cd4af2d874b7762a5c197a9a5aa1997330") + put("res/drawable-ldrtl-xxxhdpi-v4/abc_ic_menu_copy_mtrl_am_alpha.png", "3c287f4b4f894271811814a21408129444d0c79c51cd5729054d65c108b2098d") + put("res/color/abc_primary_text_disable_only_material_light.xml", "d233aef93574787c825c4f41b796ca667ab2d58e8769f1dc01ac611fbdbb4585") + put("res/drawable-xhdpi-v4/ic_action_remove.png", "548204063fbffed3d0458abad54a6a66eb09ea374eb6a9477c60bf199f70d281") + put("res/anim/abc_fade_in.xml", "aac9596dcef2ac796b74f2c2b4c149e9a66b004e864b37e82270752c66b1234d") + put("res/drawable-ldrtl-hdpi-v4/abc_spinner_mtrl_am_alpha.9.png", "0df6d1fe57b9b28bac3a6a2b82941bcddab7e616a025201aacbe5e90c48c2068") + put("res/drawable-land-ldpi-v4/screen.png", "bc34a82eb2bd4abc1fd24a5fc0a7d1afda50ac1783327d50f610647f2b25f770") + put("res/drawable-xhdpi-v4/abc_ic_menu_paste_mtrl_am_alpha.png", "ed2b544497195069d55663bd91b5cbfa5c732b9d4d264ef201394d8ef59deb6e") + put("res/drawable/abc_seekbar_thumb_material.xml", "23aa9a856885c822bee0f578d1a2f17fe4c67e08281223a4669850b6a5e588ef") + put("res/anim/abc_fade_out.xml", "b13a8275d8ff8077b29b8413c6809ca3ff05fc16698ac2564ffc3b3325cf8302") + put("res/anim/abc_popup_exit.xml", "54702462887e035398f7a686b146e0c0660951f57faef59b7d4dbce21231aa6a") + put("res/drawable-hdpi-v4/ic_action_next_item.png", "8d802c3a172ebb13ea9fab6371cf2da9e03ce3cff2cd1fc924eec446a8f19cde") + put("res/color/abc_search_url_text.xml", "01a8d0346e9e0e932fac0dbe2f9329589109651e208905efe700518f2d756858") + put("res/drawable-xxxhdpi-v4/abc_ic_menu_selectall_mtrl_alpha.png", "b78d97845a150185ff66371398582deddeccc81c4562b7ef987c3fd6c0a7631e") + put("res/drawable-xhdpi-v4/abc_scrubber_primary_mtrl_alpha.9.png", "0f5bd2f01b9689db08e4de977078e856427b126674afa8ebe7f0a9ff80aceea2") + put("res/drawable-xxhdpi-v4/abc_ic_menu_copy_mtrl_am_alpha.png", "6a42b5738417c10db0d053cdffbbd6667a94d614f449d47e6c435126da47c599") + put("res/drawable-xhdpi-v4/abc_textfield_activated_mtrl_alpha.9.png", "4914c1f7b9f7185e0361aa4ea6bcc7b6e45a95d534b8f8299282ba45371734ed") + put("res/drawable-hdpi-v4/abc_menu_hardkey_panel_mtrl_mult.9.png", "e81784cdc8ac38373621e586d28d488d585ccf2d695759ed611d848d2bf04ca7") + put("res/drawable-mdpi-v4/abc_btn_switch_to_on_mtrl_00012.9.png", "893e34d7adb3ad5d8ec6b443b0791ac1dd77b8e43496e43a1e2e1f6e5f0af975") + put("res/drawable-xxxhdpi-v4/abc_scrubber_control_to_pressed_mtrl_005.png", "b634d2fb563fb2c7053f3d2831074bac9a06bb53843394bcc4f900ed5fb655e7") + put("res/drawable-xxxhdpi-v4/abc_ic_search_api_mtrl_alpha.png", "e844c8feb07a35c300868ee6bdf72e112523a421933494586bf96015f2a4e4b8") + put("res/drawable-hdpi-v4/abc_ic_menu_paste_mtrl_am_alpha.png", "39aa138b792f6c1e1df99e0c1b748406b50aa6da3a864345f11bdedb3c0710a7") + put("res/drawable-xxhdpi-v4/abc_scrubber_control_to_pressed_mtrl_005.png", "74a6c82d92bd4a42ba5a2a8f819c2dfee5fecfdf29a8539b920f0a239f27418f") + put("res/drawable-xxxhdpi-v4/abc_btn_check_to_on_mtrl_000.png", "e88c61d8f5bdbc2b21024a8f2fd69aae679d3ea4f77b4d6e66dc95a792ec6365") + put("res/layout-v21/abc_screen_toolbar.xml", "69892161f0b97e9ba9c06b00b91c0c725fd9289caac51ebdafee7f2455d72197") + put("res/drawable-hdpi-v4/abc_scrubber_track_mtrl_alpha.9.png", "9aca579e09ed6d8a1eb275b59248755f8a117c05b96178232d31d66ea327971c") + put("res/drawable-land-xhdpi-v4/screen.png", "0399d3e0e58cef3d3c47c892e9f1ffcfc43b96e937c67a891a42bdbdd7f9a23d") + put("res/mipmap-ldpi-v4/icon.png", "7a650c26e007c3f59977f98c292ba3625b6c977572e92e539c0f4e451cab5734") + put("res/drawable-hdpi-v4/abc_btn_radio_to_on_mtrl_000.png", "5080b07eb486ba05ede737b567eb2bb89d7b79a3edb5c2ca8141c6eba012b75d") + put("res/drawable-mdpi-v4/abc_ic_ab_back_mtrl_am_alpha.png", "2be74c8cb7b5f25da58130799a22ca9435de80153a33a7dbbd3eaf9c8a96cb1d") + put("res/drawable-ldrtl-xxxhdpi-v4/abc_ic_menu_cut_mtrl_alpha.png", "160f6327240b59ec3534eefe0d4ea6dbd585e01d1357d870e910c5594016dc96") + put("res/drawable-hdpi-v4/abc_cab_background_top_mtrl_alpha.9.png", "c7a19902a30c635c4708adc2e0c2911fbdb30fa8baf8878e96f44a8c9c012f65") + put("res/drawable-xxxhdpi-v4/abc_ic_ab_back_mtrl_am_alpha.png", "1eb6da19924fabb0116fca45176fe1f4061708f5451e9844de332cb1e413e114") + put("res/drawable/abc_list_selector_holo_light.xml", "a3704ca7cc4c35254cc4719f675a8fa522935bd6f5bba1d0c94f6b3a091ed552") + put("res/drawable-xhdpi-v4/abc_btn_radio_to_on_mtrl_015.png", "02ad0ea6ff5a560e947452c7d64f679afa2056b44f86ea3b707a1cb175c2ceb0") + put("res/drawable-mdpi-v4/abc_ic_menu_cut_mtrl_alpha.png", "dc02a2cc31bace5e8d4930f24af0e6f3de60ea3d991772c107a45f2af56bf957") + put("res/drawable-mdpi-v4/abc_btn_radio_to_on_mtrl_015.png", "18c6c39be0e5d67eed7006dd262f3e850f1d83aca242d836a208fb1f1e3638eb") + put("res/drawable-ldrtl-hdpi-v4/abc_ic_menu_copy_mtrl_am_alpha.png", "641ad19d3467a8e5c4d9714eb7581608de2843f19e9a902a4e9223be0661fb87") + put("res/drawable-hdpi-v4/abc_popup_background_mtrl_mult.9.png", "ad4d6c2d1a343d36781e16e81479c4a4cf75829d1ba2dce23443ed22f909540c") + put("res/drawable-hdpi-v4/abc_textfield_search_default_mtrl_alpha.9.png", "97caeaf819a086da7ff269296eda8b3899ccda2859820553772c2a9ed92b0c1d") + put("res/drawable-xhdpi-v4/abc_list_focused_holo.9.png", "671d27e5a4b42111174b888d57118e05075a556d703bd54f9335202d2a416791") + put("res/drawable-ldrtl-mdpi-v4/abc_spinner_mtrl_am_alpha.9.png", "fe3e9afafacc6d85d8c92bdb5032d84652432d759570909847fa690b67efc137") + put("res/drawable-xhdpi-v4/abc_ic_menu_share_mtrl_alpha.png", "a3c71fec76e45cf16b5b6e508646217539fe6467c5e9fc27f9458b15c6df45fe") + put("res/drawable-hdpi-v4/abc_ic_commit_search_api_mtrl_alpha.png", "239d602c9c65f156fe024613207955ac8fbdd395ae6ec3efa51be98e3a4473f4") + put("res/drawable-hdpi-v4/abc_textfield_search_activated_mtrl_alpha.9.png", "2ab0d18aa581997b2d36ac191151273995b212d3ac2e378ee2497e8aee81bfa6") + put("res/drawable-xxhdpi-v4/abc_switch_track_mtrl_alpha.9.png", "4cdcafce259e3beb0cdcfccd969f6b18fdab421870934715cb4e0272230a36c8") + put("res/drawable-hdpi-v4/abc_ic_menu_selectall_mtrl_alpha.png", "b769fc3384d88cbdcfdcfa316bc372a52b3e3976b87590bb2820e11edb9a00d9") + put("res/drawable-mdpi-v4/abc_ic_menu_selectall_mtrl_alpha.png", "9f68c3256c42aae292504cb7ede8935e1f9c82bdb31eb0d2a55ca0c62ab810b6") + put("res/drawable-xxhdpi-v4/abc_ic_voice_search_api_mtrl_alpha.png", "c3402146e7228cb12772aad91fd071bcab4d668387a5da9b9a47bba8a5bfc7f6") + put("res/drawable-v21/abc_action_bar_item_background_material.xml", "8edbac0403d1abd530ee523971573d4c306a7a303f009f0b7f3befebab52e9db") + put("res/drawable-hdpi-v4/abc_ic_menu_moreoverflow_mtrl_alpha.png", "9ab4435bd62894ca412cd86419c477cd66b14a6b660f316c249a88066006d505") + put("res/xml/file_paths.xml", "e5a20dcc6765209234b2de6098cb99efa270649319199eb00453d1cecded9b6d") + put("res/layout/abc_dialog_title_material.xml", "b3393728c23229c6b0d9600f2240fc8616a925a0159628211f4a744361da9c2d") + put("res/layout/notification_template_part_time.xml", "e3fff5c2c216ebb732be9bbf9c214288548da591f9927752010790bce243223d") + put("res/drawable-xhdpi-v4/abc_ic_clear_mtrl_alpha.png", "287078c9586e942bd0d2a3d3e82db9d8d83f63bcf0d38e8ea148117418b313b7") + put("res/drawable-hdpi-v4/abc_scrubber_control_off_mtrl_alpha.png", "bcf9e688a1710c320c24f682ad26d12cd26b4b5e16e3a01a3ee690e486d57a95") + put("res/drawable-mdpi-v4/abc_spinner_mtrl_am_alpha.9.png", "387c23e784744e75a3611ce17ed156f1c18852c6b025a94d16b13c0b10ac4410") + put("res/drawable-xxhdpi-v4/abc_ic_clear_mtrl_alpha.png", "93b3126fb85e3934cdef24c5f695c455688056171d14b462245fdc5febb10694") + put("res/drawable/abc_dialog_material_background_dark.xml", "cb9ce61156ab53d6344566fa385d7dfefd75af30c37cbddfbe9446515e5933cb") + put("res/drawable-port-xxhdpi-v4/screen.png", "ad1ac21edfdff36e6ca67afe850caa99d13e026c8fb09cf98d935ca493870ba0") + put("res/anim/abc_shrink_fade_out_from_bottom.xml", "e1192ab74c1bf01b33726f22ec05ed5329cb20079c5c77e5dad18f9692da96d0") + put("res/drawable-hdpi-v4/abc_textfield_default_mtrl_alpha.9.png", "cb93f48592ac219831b184b9e8d667edb48d13baca83b16fffef49dcc67b91f7") + put("res/drawable-xxhdpi-v4/abc_btn_rating_star_off_mtrl_alpha.png", "68f02964d1eb0e1095cf76030bdb4ccc4d3bed858dd0819cbb2d6608b3c5e82f") + put("res/drawable/abc_dialog_material_background_light.xml", "ec2c6cd2099d681aa8423c6e6bcbb0a0b5c44fcdc56ac8b63cd724b287735003") + put("res/layout-v17/abc_alert_dialog_button_bar_material.xml", "ad4bedea15c039291e4f5668b3114ca8723ddb2da042568831b37815e6100c60") + put("res/drawable-hdpi-v4/abc_ic_ab_back_mtrl_am_alpha.png", "774a7c9a0175ac0d1e3e87f296e62b6b1597aceea8486d94e4cebce3875b003e") + put("res/layout/select_dialog_multichoice_material.xml", "137466266c666166c43bc5b02fbbe986c6bfd7ada19b0fd6ab30b4b01050f5a6") + put("res/drawable-mdpi-v4/abc_btn_radio_to_on_mtrl_000.png", "00e20ad3da7eedc2389f24c48bab875075c6dcfc902c054cd0e89f9bee02f92a") + put("res/drawable-xxhdpi-v4/abc_ic_menu_moreoverflow_mtrl_alpha.png", "2cf5ecdf6620a0e3a9f9ce3ce787e25cc05fdbe9c1d3c51dac2fe6f496074cab") + put("res/drawable-xxxhdpi-v4/abc_scrubber_control_to_pressed_mtrl_000.png", "ebcb76558e45a7f4c6692aa617173d40c41030034646510f0c76760e9b991d77") + put("res/drawable-xhdpi-v4/abc_popup_background_mtrl_mult.9.png", "fd1d8ff15f443fed99f64bb2de4c8e59148e7f45f0272c9388a624b3fdb5bf47") + put("res/drawable-xxhdpi-v4/abc_scrubber_control_off_mtrl_alpha.png", "450bf8ea02097bc27ac7e21fa40fdc4e4ad521faf2e6bd1765711cd2c02c59b5") + put("res/layout/abc_action_mode_bar.xml", "26e44e7cff3de1aa7d927acff7aeb0a4cddb0f872a277710944c9a85c984269e") + put("res/anim/abc_popup_enter.xml", "414f580b4dc1f808c834f1d90dd2c7db9b7660e6465ec5420b70e55405fb9c9f") + put("res/drawable-hdpi-v4/abc_list_focused_holo.9.png", "a2f1a37af8e699320a2fadbe54eeaa6112d5a3522d57d682edf04fe97be3b47e") + put("res/layout/notification_media_action.xml", "3d27932335f2e4d9ee922909174332f6b0b40277705dd3f5df32deb5dcddd31b") + put("res/drawable-land-hdpi-v4/screen.png", "bc34a82eb2bd4abc1fd24a5fc0a7d1afda50ac1783327d50f610647f2b25f770") + put("res/drawable-xhdpi-v4/abc_btn_rating_star_off_mtrl_alpha.png", "4aa8bc81c7e08307255e73c16afedd2c97fd37a65dbdf2fdbbf9f9e37aff3758") + put("res/drawable-xhdpi-v4/abc_btn_switch_to_on_mtrl_00012.9.png", "afedf965914f9e0bb79f57f9083c4f44972f2e8c69f2645a9e12dfe648f22db5") + put("res/drawable-mdpi-v4/ic_action_remove.png", "fd5b864e446c631b5a41f5d40ff3e522c3073f8d651eb7fa1eb60b80cb42c85d") + put("res/drawable-xxhdpi-v4/abc_btn_switch_to_on_mtrl_00001.9.png", "4bb4e2ec69ded3e748a26522b90a3d1318795687787374a05dc711f2925b4464") + put("res/drawable-xxhdpi-v4/abc_list_pressed_holo_light.9.png", "f0bc6f14305322192eb32eddcd3649025c712f8eec3cfcb5b298d7f4708073f5") + put("res/drawable-port-hdpi-v4/screen.png", "bc34a82eb2bd4abc1fd24a5fc0a7d1afda50ac1783327d50f610647f2b25f770") + put("res/drawable/abc_item_background_holo_dark.xml", "6cc5a10d3387923b2ca66b5daa16b8524201f21ffac8236036b59c76a3e6ef44") + put("res/drawable-xxhdpi-v4/abc_textfield_search_activated_mtrl_alpha.9.png", "f4812c6538630315d02cc3a182ba3fcfb85868a2aa98318ec5dd63677351f872") + put("res/drawable-mdpi-v4/abc_menu_hardkey_panel_mtrl_mult.9.png", "66055f4b925daef0e5640a8e3075cd7b176a34da388085d52857d5c6f38ba632") + put("res/layout-v17/abc_alert_dialog_material.xml", "a198b24b9135e8a03d27d288ef4a9cf012b9a63c21f7ccf4da61de80dad7d62a") + put("res/drawable-ldrtl-xxhdpi-v4/abc_ic_ab_back_mtrl_am_alpha.png", "ee0e3c601dbf603d7001b7ef4cbdb237a959734c9649dd211b0bf615b6ff4de1") + put("res/drawable-xxhdpi-v4/abc_ic_menu_selectall_mtrl_alpha.png", "8c6e2a3c33890d72be345310e6e531778c25b9c9b143762c6c2bdee88f5fb0d0") + put("res/mipmap-mdpi-v4/icon.png", "7a650c26e007c3f59977f98c292ba3625b6c977572e92e539c0f4e451cab5734") + put("res/layout/abc_list_menu_item_checkbox.xml", "6b742ec907cd9ce87bbadfdc83b34e570a6ae1ec760364ddd17c3365e89066e8") + put("res/color/abc_secondary_text_material_dark.xml", "4e82e40e26ae5947329110f135b30ee5883d633689f3ab3f20ea0f68f1737efc") + put("res/layout-v17/abc_dialog_title_material.xml", "78fa8892acc354daa4b030b0d49767262f27b659960b81f92c1a3a129d19fa1a") + put("res/drawable-xxxhdpi-v4/abc_btn_check_to_on_mtrl_015.png", "52a0358e54439373ca39f99cd83cf3a7be73c257f07fae83edd83c9b4891895d") + put("res/drawable-hdpi-v4/abc_btn_radio_to_on_mtrl_015.png", "7442cda653acbacdf9aef256aa191e5ebb6071a1747312aec45e1db8252a3cfb") + put("res/drawable-port-xhdpi-v4/screen.png", "0399d3e0e58cef3d3c47c892e9f1ffcfc43b96e937c67a891a42bdbdd7f9a23d") + put("res/drawable-xxhdpi-v4/abc_btn_check_to_on_mtrl_000.png", "f5ebab3a4b8712f651cd7e42a17a4d0577c3e3deb89e43123d6180c7f6d5137b") + put("res/layout/abc_search_view.xml", "d86ff6a808854f2071c1c9eba22b1d6a8b984319acf108070c3cec77d47f82e3") + put("res/drawable-mdpi-v4/abc_ic_menu_share_mtrl_alpha.png", "1db8f8301b62f43b1f05faf082944e8be93eaa9b54669a406321e1b7ae9b05e8") + put("res/drawable-xxhdpi-v4/abc_popup_background_mtrl_mult.9.png", "4268b15d37eb27ec7ac1bb221a57000cede389fd34beaf75e268afb380901152") + put("res/drawable-mdpi-v4/abc_list_divider_mtrl_alpha.9.png", "0ef7b3d9f9d890a48e4fccc0db07315bd6fcca7049452e20a27a69bd22080415") + put("res/drawable-xxhdpi-v4/abc_ic_commit_search_api_mtrl_alpha.png", "9b6ad33830a9fa48796632fffd0a5386cce8ead83fa7265fb1a96bd44d479306") + put("res/layout/notification_template_media.xml", "208f7b22a6553723c1c60d971fd5fed0400ea1a180f265353a33d2a9bd5436ff") + put("res/drawable-mdpi-v4/abc_list_selector_disabled_holo_dark.9.png", "5f997c46e51b3726b57a582f13663aedfeed55e4e61fcfe6dcf9999ead38f1d4") + put("res/drawable-hdpi-v4/abc_ic_menu_share_mtrl_alpha.png", "44b4d2e07f32af386071fac95b80581e3c955b5e2281749d42774de9dfc87535") + put("res/drawable/abc_ratingbar_full_material.xml", "c323ab086275b807a25d041108bf3b1e73b1291bd276f3fbfbfe0f298766974e") + put("res/drawable-hdpi-v4/abc_ic_clear_mtrl_alpha.png", "e394bbb629366e5727227fdee2d3e3ad9bc5759950a8b358bbf5a4777ffe968d") + put("res/layout/notification_template_lines.xml", "0f6ca4f3efcbfd75584343039fe374222e89162ae1895413b76a930f4554d0df") + put("res/drawable-xhdpi-v4/abc_ic_menu_selectall_mtrl_alpha.png", "94b80df3ae6d8fbbb43ca98f062c7083ffbdf604715e1610a9a622f927a6fcdb") + put("res/layout/support_simple_spinner_dropdown_item.xml", "596b32eee9c6dc78af72f86bd82015d755778399ab4de6e9520706dd19bee4ef") + put("res/drawable-xhdpi-v4/abc_cab_background_top_mtrl_alpha.9.png", "052762422b60e16802dd4dcdbc998f9c2ef911049e086592650faf895ec2bcd4") + put("res/drawable-ldrtl-xxhdpi-v4/abc_spinner_mtrl_am_alpha.9.png", "ebd1e1432b6a54cc6161da7f69837e8fa45ed95eb196b421d693fffb040be7be") + put("res/drawable-xxxhdpi-v4/abc_btn_switch_to_on_mtrl_00001.9.png", "079843966864bf4099457cb72f1febe7c57bb5a70bf0f544a9169a078b7a5b01") + put("res/drawable-xxxhdpi-v4/abc_spinner_mtrl_am_alpha.9.png", "4b787425a2b1eabb721cd99bd23ef235db80f41900a66028c2872fa068011078") + put("res/drawable-ldrtl-mdpi-v4/abc_ic_menu_copy_mtrl_am_alpha.png", "0f079710ca543c4c7042d0a463764c07ba6ba4bda26ec9b625c26485c98fc569") + put("res/drawable-xxhdpi-v4/abc_list_focused_holo.9.png", "8c6846f4105ef1c02107e842142e9ce60b08819296e9510b2d0c12cf4560c5e1") + put("res/drawable-xxhdpi-v4/abc_ic_menu_cut_mtrl_alpha.png", "d523fe7dc93908f07a61569bfc52f4be6e9c6033775d74394d18566832ccc408") + put("res/drawable-xxhdpi-v4/abc_list_selector_disabled_holo_dark.9.png", "0eedc51deea0c0f3f675a400d667eb476553b92cbd12683b6154e36306031c19") + put("res/layout/abc_select_dialog_material.xml", "15a1a201de1c2f663436eecee51b80545aa6f5f93811a45b324165a5e8716435") + put("res/anim/abc_slide_in_top.xml", "05cc140039277e0a2299e4d1650ee39f7d12ff5e09cecf84067c9f2f340ecd06") + put("res/drawable-hdpi-v4/abc_list_divider_mtrl_alpha.9.png", "0ef7b3d9f9d890a48e4fccc0db07315bd6fcca7049452e20a27a69bd22080415") + put("res/drawable-hdpi-v4/abc_list_pressed_holo_dark.9.png", "24e31720f77bc442bfd450d80747a99db2df3675a53447a291622006df52815a") + put("res/drawable-mdpi-v4/abc_ic_menu_paste_mtrl_am_alpha.png", "cacfc7acb35eb382effa1b8ee9881e1be435988bc98db9bf08199c3b6f8bca82") + put("res/layout-v17/notification_template_part_time.xml", "ee3fefac46bdfe73e01c2775944cd3cc2acab99d7c657efe20a3cc56a08a4805") + put("res/drawable-xxhdpi-v4/abc_btn_check_to_on_mtrl_015.png", "71a2ba63a5d4e91f7c14324fa1c3eb9290b73cb7b3c7828018d05adb1cfaab45") + put("res/drawable-xhdpi-v4/abc_btn_rating_star_on_mtrl_alpha.png", "e00c96c08036d89fc1fbe27df31257ce08ecaf2e06806786f3751ba7c970ff91") + put("res/color/abc_primary_text_material_dark.xml", "116a77ff0f38762bc4c47fcd4e4157490dc970625415ddebc2dfeb9aa17970f5") + put("res/drawable-xhdpi-v4/abc_textfield_search_default_mtrl_alpha.9.png", "2ae7387b4db93f8ecbc01d288b752d41373ce7b7d808969fca1e3b2e9d049219") + put("res/drawable-mdpi-v4/abc_scrubber_track_mtrl_alpha.9.png", "c618fafa6107721f6b07922ab06ed1922074412c7963dfcc9e34e1db93daacd5") + put("res/layout-v17/notification_template_part_chronometer.xml", "5e800e8032e365bd2f4f2964bbd3f2ec2ea443201ca06c47590bb346e72929cd") + put("res/drawable-xxxhdpi-v4/abc_switch_track_mtrl_alpha.9.png", "add9abce47185fdac6290fa13cc9cbf46cf4148c7dbf4f3147f0e7b821ca223f") + put("res/drawable-ldrtl-hdpi-v4/abc_ic_ab_back_mtrl_am_alpha.png", "8945707810ae4c8b568981bdcb74fcc92998ea09045f18649d587ea75ed8b985") + put("res/color-v11/abc_background_cache_hint_selector_material_light.xml", "ebf2196624ef10188cd1a0935b5426b2fdf8232a362277a57d09c4a1f2c507da") + put("res/drawable-mdpi-v4/abc_popup_background_mtrl_mult.9.png", "9e96a642ab9139cca3fb822ce199c711443087b5a7463e0c7fed012d9a773285") + put("res/layout/abc_popup_menu_item_layout.xml", "88986f7cc025876a7093ff01e98f0df8a8f452a43bc1b6438b089d4d2036cea6") + put("res/drawable-xxhdpi-v4/abc_ic_search_api_mtrl_alpha.png", "269f388a6a672af16e49203bce389020f51da34b43e7117e954c3cc0ab96c840") + put("res/drawable-xxhdpi-v4/abc_btn_radio_to_on_mtrl_015.png", "523733ec9c1ed308943cfb03770d64909c02ea6e866d535d389f9ae663a4c78f") + put("res/layout/abc_expanded_menu_layout.xml", "9d026182191a5c46211ba40fd2abf85483bfec033e28d21895195aa27b5aea0c") + put("res/drawable-hdpi-v4/abc_btn_switch_to_on_mtrl_00012.9.png", "8dd453207afc153e8f1712fb630568e05cf2697fa22ca1789e53c247de2cb960") + put("res/drawable-xhdpi-v4/abc_tab_indicator_mtrl_alpha.9.png", "3c93dc6550e031dec53f2c1646b46fe52e6c574a7b5eddd33d029100363df916") + put("res/xml/provider_paths.xml", "2e4306b5c4d5603f5db9c29b176c253e9420b46be253daac581c4c98f1aa108b") + put("res/drawable/abc_textfield_search_material.xml", "820b5412de01a1488720d18aacbeceadd07a0288a2d13dd3af3d977590a6ff9d") + put("res/drawable-xhdpi-v4/abc_spinner_mtrl_am_alpha.9.png", "c6a403e2ee84f5ba230fc1acaa5c6b87b9defab26f7b679696ac885b7dfe4a21") + put("res/drawable-xxhdpi-v4/abc_ic_menu_share_mtrl_alpha.png", "bb54f3ff22d6e6bcaaaeef3e9b0765137485f924a8b7888e7dc2b9b826832913") + put("res/mipmap-xxhdpi-v4/icon.png", "7a650c26e007c3f59977f98c292ba3625b6c977572e92e539c0f4e451cab5734") + put("res/layout/abc_screen_toolbar.xml", "f1670f2cd6c6dcb86f3c50b0e840a0195322cdbcd460e46828806ca1cd918ba0") + put("res/drawable-xhdpi-v4/abc_scrubber_control_to_pressed_mtrl_000.png", "9dc401d6dcb691b4904d007d4c57be4af0b38a2b103fafca5812ac02b6f0e0cd") + put("res/drawable/abc_btn_borderless_material.xml", "0822257cdc5124d811dfb13ae57f242e4ae550abce462250231c0befa29e082a") + put("res/layout/abc_alert_dialog_material.xml", "3825181f8cffc231f4fa7fcfc3078eae492f19d39c202de2bcd21c537a59d7ac") + put("res/drawable-v23/abc_control_background_material.xml", "3ce4a969a079c570bb634572d111be190443a90f0fa754e0f99e645dc6c25a82") + put("res/color-v23/abc_color_highlight_material.xml", "b476ac65ddf4fcc665d5d9225365a6bcb613b186649a6490232aa4c458fa7c55") + put("res/drawable-xxhdpi-v4/abc_ab_share_pack_mtrl_alpha.9.png", "2310172cf1c75ca08b63baa38d29b6a145da35e9bd23ac0b78d2571f00d9d88a") + put("res/drawable-v21/abc_btn_colored_material.xml", "ede942fcb0f3325b45ed32e0c76ab0c760229d2a4201455c245e04fd8d6ff01a") + put("res/drawable-xxhdpi-v4/abc_scrubber_control_to_pressed_mtrl_000.png", "20f88839f937925401106c71e57f7e4a24636dbb5ed714ae702bac11c05a0fe7") + put("res/drawable-mdpi-v4/abc_ic_voice_search_api_mtrl_alpha.png", "5c621922ae954f1c7b17a2c81e82caf8e26c3c8f7bb1f494536e12996298befe") + put("res/drawable-xxhdpi-v4/abc_menu_hardkey_panel_mtrl_mult.9.png", "a8252f52638c80df119db031626198a4a8d4b32ee618e96c3a36b9b946e26894") + put("res/drawable/abc_list_selector_holo_dark.xml", "c0a8c5fa064df16c8130e6c1b89c407ca3e6907a77138f9354fd7f279a309cb5") + put("res/drawable-mdpi-v4/abc_btn_rating_star_on_mtrl_alpha.png", "91bc27962779745e35aa681dc7a74fd5e829f090e59d0ff9557bab37e2ca2d40") + put("res/drawable-mdpi-v4/abc_list_longpressed_holo.9.png", "dbc0af61f440bb3f5382b8ea3fa578659f76c7a6aef620a68afc43a909f2e0e5") + put("res/drawable-xxhdpi-v4/abc_btn_radio_to_on_mtrl_000.png", "0eed99d0a5e36a468b557605e6ccea495c029f3569844a45f4b5d8dadd77d4a7") + put("res/layout-v17/notification_template_big_media.xml", "1eac34b2099c68d22e45f05253ad6d357dde52b57765cc6bf787fcb12e03265b") + put("res/drawable-xxhdpi-v4/abc_tab_indicator_mtrl_alpha.9.png", "75c6f650f790493b89c50d6a3bc46bcecc677291a1ac1513b067cb4dd73aafa6") + put("res/drawable/abc_tab_indicator_material.xml", "8663108d80dc207cc2d9e2e89dfe75ac1b68f0b804546874f8363a7fa797a90f") + put("res/drawable-xxxhdpi-v4/abc_btn_radio_to_on_mtrl_000.png", "6002eb56783b956b5f91e6ddffaf36cc867402a01a225e627b4c25c8d21a4e23") + put("res/drawable-mdpi-v4/ic_action_next_item.png", "c01422fff1e45b864e7cc9b807a83d80868dac2bd0113e138385076b1a6fbf6d") + put("res/drawable-xhdpi-v4/abc_ic_menu_copy_mtrl_am_alpha.png", "624af94ddb9b168bf22243043429772bede14c779e771dba98c6cc9ba597c4c3") + put("res/drawable-xhdpi-v4/abc_textfield_default_mtrl_alpha.9.png", "3d7bbf47c8ac2b75977a579ca1aa2c12be8494f916450e971cf172bbc8a1f311") + put("res/drawable-xxhdpi-v4/abc_btn_rating_star_on_mtrl_alpha.png", "7d230ee7f0709fe3016a360538d493f0a3b565337e576dccb1e798985fc473d9") + put("res/drawable-xxhdpi-v4/abc_list_selector_disabled_holo_light.9.png", "550e8f5f4a6004c0ff14c681b20ae675c6943e5ee299da36939f790fa8818fe0") + put("res/layout-v17/select_dialog_multichoice_material.xml", "b8dd03f6cdaddda24427698c320c9fe7fbdeda4e360a67f901cc99f6b676b0b2") + put("res/drawable-xhdpi-v4/abc_textfield_search_activated_mtrl_alpha.9.png", "247152ba9b093d65d697d071cc7e986ac7e64c4ec95cf42c40b6f054ef9bb661") + put("res/layout/abc_action_bar_up_container.xml", "0903525f885f6aba13a17daf5be0355c455689dac8839418dc57908c8c7008a4") + put("res/drawable/abc_spinner_textfield_background_material.xml", "a875de881d4296c070560ae4208bb00810874ec9252e5569ddad3c50e5e580a7") + put("res/drawable-hdpi-v4/abc_ic_go_search_api_mtrl_alpha.png", "8eefaa5bbcce1a47294ada4eca76c92dec81913de9ab2d4cd09c1bf9fa10990f") + put("res/drawable-hdpi-v4/abc_btn_rating_star_off_mtrl_alpha.png", "180a880eb2c603e1e1c903a2cd9cdbf8afa6fc2fe0bf6618d2f9b57b637832ac") + put("res/drawable-xhdpi-v4/ic_action_previous_item.png", "1dc3b456866e6c9a612ef9eec0b900394bae784c84af4e9dbdadd07d02cd394d") + put("res/layout/abc_activity_chooser_view_list_item.xml", "0c45295e533ffa94b98572b6b782872524cc25f3021d43646b62af4e66f3391e") + put("res/drawable-hdpi-v4/abc_ic_menu_copy_mtrl_am_alpha.png", "b948c74fe67d905b365ad3dc96a6ee416c9927dcadf3c555b5b2359540100b18") + put("res/drawable-mdpi-v4/abc_cab_background_top_mtrl_alpha.9.png", "9c05e26f9e74ce6d54e1e9e3d0fc1d6e7353ce7515e1195843fc627ad734156b") + put("res/drawable-xhdpi-v4/abc_ic_commit_search_api_mtrl_alpha.png", "7fb735d88839bff8190cdcadd7896326365456c85561d098d1d9ab98bf291838") + put("res/drawable-xxhdpi-v4/abc_ic_ab_back_mtrl_am_alpha.png", "a55f232779d32baeea6c7ed6c84c6e985d3288c6d244e29cc8856d9d0334f32c") + put("res/drawable-hdpi-v4/abc_scrubber_control_to_pressed_mtrl_005.png", "ffb431a4128e5aac93bf838154e272768a6dd51ab36a61517c95d53700daa0b9") + put("res/layout/abc_action_bar_title_item.xml", "9203c3aee1dc30c22c3909a126c087f20507a27e0e27c09d0f86de2303b0a64c") + put("res/drawable-port-mdpi-v4/screen.png", "bc34a82eb2bd4abc1fd24a5fc0a7d1afda50ac1783327d50f610647f2b25f770") + put("res/drawable/abc_btn_check_material.xml", "12e9ca66114da2c67fc8f8ea38b4f28de73155154c2d15f9e7cc9a0fb4e2a4e6") + put("res/layout/notification_media_cancel_action.xml", "109cddab44f02e57fac23430d5ecd17a54fac1f5fad5444887cb2c68be84fb80") + put("res/drawable-ldrtl-mdpi-v4/abc_ic_menu_cut_mtrl_alpha.png", "3a283a4f4cc377084337680f3d3c693259902fc8793f5395afdfeaa0bf9ca105") + put("res/drawable-ldrtl-xhdpi-v4/abc_ic_ab_back_mtrl_am_alpha.png", "3cc16a0cc86ab8c137f9355a5577927a95caba18565b657d815b76461de3962e") + put("res/drawable-mdpi-v4/abc_ic_go_search_api_mtrl_alpha.png", "acf23a1bc642062f1f93f94247463c4077cd00d48521c3609268dff657c488ac") + put("res/drawable-xxhdpi-v4/ic_action_previous_item.png", "0f18602b3446953de962a1a8d412db3532d2ef180ec0bc8b3d33fd35e8cbb5cc") + put("res/raw/zxing_beep.ogg", "9bd6f5856d9162d4a41c016976edc4bfc2dfbbff9d98f05a362a50fa678dd5bc") + put("res/drawable-port-ldpi-v4/screen.png", "bc34a82eb2bd4abc1fd24a5fc0a7d1afda50ac1783327d50f610647f2b25f770") + put("res/drawable-mdpi-v4/abc_ic_clear_mtrl_alpha.png", "c550947a9eab1396c706ba97a39b3e4ce51c91329a5e597f6d6647bfece68fed") + put("res/drawable-xxhdpi-v4/abc_scrubber_track_mtrl_alpha.9.png", "05b76fbf46593122597b8b4e2fd2358fa0734a9fbc2d24f5198ee2a72e77a9e9") + put("res/drawable-xxhdpi-v4/abc_textfield_default_mtrl_alpha.9.png", "025d8a1155d2ce4769e307a89ff7ec6c175280df90ec51a8c61cea4551992cef") + put("res/drawable-xxxhdpi-v4/abc_btn_radio_to_on_mtrl_015.png", "53340b0b0f529b26db6af632a0112708233bf312448c5b89a1357b2cd70b3f42") + put("res/anim/abc_slide_out_bottom.xml", "e33c8ff0e038ba561eac7101963096094d58b5f1a748c1067bad5290328148df") + put("res/drawable-hdpi-v4/abc_ic_voice_search_api_mtrl_alpha.png", "e80e3db92ba1b47e32b7279a00778aac3c68150f88a4158941096adea34561bb") + put("res/drawable-xhdpi-v4/abc_ic_ab_back_mtrl_am_alpha.png", "cac329555c22a8b70581bc59c12c1c3e19c86748dd5a9e2c1251de16ee579170") + put("res/drawable-hdpi-v4/abc_scrubber_primary_mtrl_alpha.9.png", "ea50d85209d909ecd07013b992929444b7f51fd22da25a6be2936631ad1d28c4") + put("res/layout-v17/notification_template_lines.xml", "cb5c99d9244a9d994903ce2c3e12c346be80c8661f87845cdd609ed377a13b5c") + put("res/drawable-mdpi-v4/abc_switch_track_mtrl_alpha.9.png", "7b73c914c459938dc3257fa1151c736609eb37bb437ee42071b81eb4860bdc04") + put("res/drawable/abc_edit_text_material.xml", "3ee45694043973b0dd0f43c29cae923c63f053af6cab0d5b501162153f7f84a5") + put("res/drawable-ldrtl-xhdpi-v4/abc_ic_menu_cut_mtrl_alpha.png", "5d01a80f82b4c5874f20d78183037fb55504c6c3384458ad29b4535bdb951cac") + put("res/drawable-mdpi-v4/abc_btn_check_to_on_mtrl_015.png", "d33c0277bfd52f3b32daac3c30a420d7fc0a7214addb30dfe3b44dfead940b11") + put("res/drawable/abc_btn_radio_material.xml", "206c4cd4d38f53ee940c2ed149cdb5d7483dda1d89bcdb7d2b68adb5d6392846") + put("res/drawable-xhdpi-v4/abc_btn_switch_to_on_mtrl_00001.9.png", "915cfe2e2a1f9f2ad498669b0b1c6ec4a26735b7e690c4fd6b8339aa1747e5d7") + put("res/drawable-hdpi-v4/abc_list_selector_disabled_holo_dark.9.png", "c0e4c8fdbf4fef50f8a7033e529c3ef8da2c994b3567d4e9d89cfa099e7ac15b") + put("res/drawable-xhdpi-v4/abc_btn_check_to_on_mtrl_015.png", "2fa524cda78d7ebd66164a896a042277b7a56b8658fb318fc700a114cb04f7b2") + put("res/drawable-xhdpi-v4/abc_ic_search_api_mtrl_alpha.png", "0bdd69dcc421f49c9feb0baa908694baa968464dce0f09b55a8a8add3724e996") + put("res/drawable-xxxhdpi-v4/abc_ic_menu_share_mtrl_alpha.png", "1c515bd153b3f9d1eb1bb53c2dcb0c168ed3cf0763986ef1652cd39aa3a0bbc2") + put("res/layout/notification_template_big_media.xml", "5195d15e19b8bab1fb9fc2973708cb3968f25f066758b854801b3ff7674b32bf") + put("res/drawable-xxhdpi-v4/abc_textfield_activated_mtrl_alpha.9.png", "bfa384ba37a3e1024b738e0a82a7890f012f1a3291307135f7c819d0c9c3d425") + put("res/drawable-ldrtl-xhdpi-v4/abc_ic_menu_copy_mtrl_am_alpha.png", "e89ed0715d5e67ab55ddcb3f2a56b41ac50d2e6a3b9216fea4d8ee23693c8f64") + put("res/drawable-ldrtl-xxhdpi-v4/abc_ic_menu_cut_mtrl_alpha.png", "173404ec34c97c2142f4955ac0950606649b195fab48df5e8f33560cbd9c5654") + put("res/drawable-xxhdpi-v4/abc_ic_go_search_api_mtrl_alpha.png", "9b699740ec1aa941bf4af584ae2fc2a3a37f3036769736b8885e1a682330c248") + put("res/drawable-xxhdpi-v4/abc_list_longpressed_holo.9.png", "44c29766b2d88b78a71393b3e3a771ca7eef5f552630c6074f33d6291241d0b9") + put("res/drawable-mdpi-v4/abc_list_selector_disabled_holo_light.9.png", "ae964252406ac9465a442ad66c9fe077e8d16391de4ece7ead6128b97f954362") + put("res/drawable-hdpi-v4/abc_list_selector_disabled_holo_light.9.png", "e71502ac4d35951d0e063ef8b76885e27846a2dc0cdc616959d2a513f4852a2d") + put("res/anim/abc_grow_fade_in_from_bottom.xml", "a5fbceaa55ddc0aaed2077543b1f32ab7dd4b1de32a8775cac4e65ba09ed04a9") + put("res/drawable-land-xxhdpi-v4/screen.png", "ad1ac21edfdff36e6ca67afe850caa99d13e026c8fb09cf98d935ca493870ba0") + put("res/drawable-mdpi-v4/abc_ic_search_api_mtrl_alpha.png", "7ad683f3e730c98b12ae85edc0f9b2a61476f1dcf3df411721924d919ae98f5b") + put("res/drawable/abc_cab_background_internal_bg.xml", "ec7dc93f92559eb10d1d91fa944e746e95c3c7f03bbffab87ff22c1b3b002f49") + put("res/drawable-mdpi-v4/abc_list_pressed_holo_light.9.png", "35c56471c6414f33645a376c0d1a8a126f4cdc728f2344d08bccd3828e810e2e") + put("res/drawable/abc_item_background_holo_light.xml", "daf638c66b3fe5f24b5c7038193ca6e0a462663eeec720462d61cb4c61fbbf2c") + put("res/color/switch_thumb_material_dark.xml", "2c505932939633d0a61684d98161bde0a05b8c32e967114031335ee96387a76d") + put("res/drawable-ldrtl-xhdpi-v4/abc_spinner_mtrl_am_alpha.9.png", "fe21f096ab18f8947a27624ae952e86be4ff8d645ae714b653d398f64f8e39ee") + put("res/layout/abc_screen_content_include.xml", "bfdb030a0c978bb66333ac0dc6c4748caf3d856835f7370aaa8e06ec3269534c") + put("res/drawable-xxxhdpi-v4/abc_ic_voice_search_api_mtrl_alpha.png", "9e81ace23f53713fff9f07794897def554cf72df8bcc0e78e3ca2e1184e75e5b") + put("res/drawable-hdpi-v4/abc_btn_check_to_on_mtrl_000.png", "5eca0280bd38a6887b6e41765cf5da573133874625001eb90156eaeb35fbe088") + put("res/drawable-mdpi-v4/abc_textfield_activated_mtrl_alpha.9.png", "16a04f086d374b00ba759124c4a372de58cb1b4eb070db121ca747073fef4745") + put("res/drawable/abc_seekbar_track_material.xml", "a209cb89db7acaf32a22ad77209ceccdd2a1c8a401c9d5e0df99961468338f50") + put("res/mipmap-hdpi-v4/icon.png", "7a650c26e007c3f59977f98c292ba3625b6c977572e92e539c0f4e451cab5734") + put("res/drawable-xxhdpi-v4/abc_btn_switch_to_on_mtrl_00012.9.png", "40ae459db3548ac42fb949d69ecd5d3c452866db0fcf4d1b1081dd676a9431ec") + put("res/drawable-xxxhdpi-v4/abc_ic_menu_moreoverflow_mtrl_alpha.png", "2c3122e4c041da2a88b5e513d2891edd20ff7dc1216708e2d3b7b14678d08232") + put("res/drawable-hdpi-v4/abc_switch_track_mtrl_alpha.9.png", "2c770f581a2ca530edeb27812f9153ca16a7b79f5245e02d6911b534a4c80c5e") + put("res/drawable-mdpi-v4/abc_scrubber_control_to_pressed_mtrl_000.png", "4a494c652ef9019cc723afcb39de29a523e72e7b648739900fd786161cd47bf8") + put("res/drawable-mdpi-v4/abc_btn_check_to_on_mtrl_000.png", "055d5f87b1bbb6677096bc7eb7eace57e40216cb97f2b2b926d26c1263f9abce") + put("res/layout/select_dialog_singlechoice_material.xml", "7f7d5e80a17d35312f1ab8e84da3c0d1a6a1213dbc71ecdbb1e1507f00c2ad07") + put("res/drawable-hdpi-v4/ic_action_previous_item.png", "d326359f839c3c87dc22c9c133fcf8a6789eec76f6276300d0e045a5354feb36") + put("res/layout/select_dialog_item_material.xml", "16482ac145fdbda41d7eb9fba7020475dd6c96b2e11d70d2bab7c0a8bef0a286") + put("res/drawable-xhdpi-v4/abc_ic_menu_cut_mtrl_alpha.png", "236db6dffdfccab00c623ed956723f19660cacbc9d9e2daa0ecd91e4afc40d2b") + put("res/drawable-xxhdpi-v4/abc_ic_menu_paste_mtrl_am_alpha.png", "cf8755669eda61de90624c9e21cbce526ca313ca206d7563f5a6066b1876abce") + put("res/drawable-ldrtl-hdpi-v4/abc_ic_menu_cut_mtrl_alpha.png", "de207ef50c941d5a22eb48586346228ff5b98c6f0540d6731af06658aeb8ecb7") + put("res/drawable-mdpi-v4/abc_textfield_search_activated_mtrl_alpha.9.png", "b1e9c8ff1b5662b3a5c7bab6b223ff14b68fe14e2d097ffc3ccdc3d5859eac88") + put("res/drawable-xhdpi-v4/abc_list_longpressed_holo.9.png", "727d78fec3f903a271c0476a50e8c064d28b32f7829b016ae0a07da53b83b281") + put("res/layout-v17/notification_template_media.xml", "8bb61dab2c80af12d33d99e23df5c9b604d76294ff0ddb4ffa3c9d880ad2bd35") + put("res/drawable-xxhdpi-v4/abc_spinner_mtrl_am_alpha.9.png", "54b757ced1278d78846c070ae2802ca65f88c17657236e4fd72e46e23a2c9a22") + put("res/layout/abc_action_mode_close_item_material.xml", "0b5cff6338e6ce6fd63b8f24846558e269ac39f9e1f70cf6b8e45c1ec67cc260") + put("res/drawable-mdpi-v4/abc_list_focused_holo.9.png", "8436624220fba246ae816a3aab379dfd7ff2a484284ab24c3af4989778705bb3") + put("res/drawable-xhdpi-v4/ic_action_next_item.png", "9c57cd4759313169898d4f4b69a96bd963c3b2257532d5ed43275b502ca98e64") + put("res/drawable/abc_text_cursor_material.xml", "14e09849019f466a6780ffb38bfffdee36c21519c7627f1fdb17224b60e09f6b") + put("res/drawable-mdpi-v4/abc_ic_menu_copy_mtrl_am_alpha.png", "1df76313283b0046c002e9792e35b1f4087ef98e79bb757aededc9d27bd54897") + put("res/drawable-mdpi-v4/abc_ic_menu_moreoverflow_mtrl_alpha.png", "4ad77975a92c859163b4f39ba17d01e3bde2d03f1fc2177d113401fa84a67c73") + put("res/drawable-xhdpi-v4/abc_menu_hardkey_panel_mtrl_mult.9.png", "395c5e56806478d86eb4fae5570163962bc6845971dad92ea29356690511ec56") + put("res/layout/abc_list_menu_item_layout.xml", "af7dd160067d2397d998be2399dac8f8fe69b9890825896ffbbd447dc44caba0") + put("res/color/abc_secondary_text_material_light.xml", "47407173ce8bc17e8d19577230c43f62e9d5f38cae3d23ebaa92f785e0c4932b") + put("res/color/switch_thumb_material_light.xml", "45177bbc0542c30e514408dd4b23c83a17a84a5e594989c0e094c159a39cb721") + put("res/drawable-hdpi-v4/abc_list_pressed_holo_light.9.png", "7e12fcbd0c61e037cd1cafe510c307b4074d156adac0022d91f3dcdcae692818") + put("res/drawable-xxxhdpi-v4/abc_ic_menu_paste_mtrl_am_alpha.png", "307cc34bbb3a22f5ad82decd369e14404d467655097cbbce7b03f6aac0f82264") + put("res/drawable-mdpi-v4/abc_btn_rating_star_off_mtrl_alpha.png", "da7b78e6af0d3a21b965cec54ce80c54271a0206d1da3787673f9cf504dbe3a4") + put("res/drawable-mdpi-v4/abc_scrubber_control_off_mtrl_alpha.png", "aa44c927eb4e9a886438f3b6083c948d8cf67c52b3e5fe034b9720648f5fa78e") + put("res/drawable/abc_btn_colored_material.xml", "363cf11a26c4c89772b73bf267da6a7b3229e0831b8eff5998076b12011f5603") + put("res/anim/abc_slide_out_top.xml", "2f5ba7fce4651ca8fcf598e8bb6df46b4d32912606c5d20a39de9b0554971105") + put("res/layout/abc_list_menu_item_radio.xml", "af7c4fa541f9e90b6ec8d6f8971c1d404cfabb86610bd9054de2b2dd211b33e6") + put("res/drawable-xhdpi-v4/abc_scrubber_track_mtrl_alpha.9.png", "b15c567058a56d15661e044bb4f8408512d87a79d11779ec8d7189dbb30eaefb") + put("res/layout/abc_activity_chooser_view.xml", "dd991ccd05f4240d13c6ad9b685debe84f6801d454b5edd0689a4cc9f1fb27e2") + put("res/layout/abc_screen_simple.xml", "b444aa5a73a22ebaa8745ac91729180ef54ce675138dd8bf3f75a649b0fb140d") + put("res/layout/zxing_barcode_scanner.xml", "b52f5db8970b2d92e3e5c352741dbb4385df7f0d7270db648842e05950b8e92f") + put("res/drawable-mdpi-v4/abc_ab_share_pack_mtrl_alpha.9.png", "cf2d1096edd99c09a185d330a8a202a35b0d21ec1305df4ae7d359bcf0179b4d") + put("res/drawable-mdpi-v4/abc_tab_indicator_mtrl_alpha.9.png", "36e3737c04ce9ff9322ea71c2908a36ae532437d0b899f70e4ff33f20f810ba8") + put("res/drawable/abc_btn_default_mtrl_shape.xml", "f0f21a53cce548fa7d611f1cded76fbc15b100bee7857ef1e6b073138057dd82") + put("res/drawable-mdpi-v4/abc_btn_switch_to_on_mtrl_00001.9.png", "27c227cad79fa831621558b8ad808e28c939fb6dbdfa040ae57136b4b50f19d4") + put("res/mipmap-xhdpi-v4/icon.png", "7a650c26e007c3f59977f98c292ba3625b6c977572e92e539c0f4e451cab5734") + put("res/drawable-hdpi-v4/abc_ic_search_api_mtrl_alpha.png", "4bf6cadfd829c96627a3cc6bdb894ee48edf7567b341aee4a4c38917e65f7fcd") + put("res/drawable/abc_switch_thumb_material.xml", "652d8486916207186bc3bf5155d0d9b437f473c5c58ed793932098bd6f22a207") + put("res/drawable-xhdpi-v4/abc_btn_check_to_on_mtrl_000.png", "43ccf1f0a5e9e9ed9a6df33e1b3d378d49dbc8dc60d83b1cdb985e5e48f52497") + put("res/drawable-hdpi-v4/abc_textfield_activated_mtrl_alpha.9.png", "076be4630bb4f4ba10579cba1b3900e3f3f2286cafcbc15892d97fa4b45a27ce") + put("res/layout/abc_action_menu_layout.xml", "78e8b182c9982b18cc3bf705e95511e4ef4554b1f15c91d4c443b62587547f97") + put("res/layout/notification_template_big_media_narrow.xml", "edc0530f0084410597bedd5623e0cf8e0f1d0f2180ee3377e85ebc3ce7966968") + put("res/drawable-hdpi-v4/abc_btn_rating_star_on_mtrl_alpha.png", "b6ed0b4f4e4f64989a78969a49ddf9eb7c2f56a9473691c080d8fc10154f8d73") + put("res/drawable-hdpi-v4/ic_action_remove.png", "8ed4fff301384afff335f560209a5216bcf90946f26421eca413aadb07a01d6c") + put("res/layout/abc_list_menu_item_icon.xml", "69c2869ea56579b9d02e761d4cf817870b080e9229a32dd4f30ab59255e2fc95") + put("res/drawable-mdpi-v4/abc_ic_commit_search_api_mtrl_alpha.png", "7d15a2bdf032c5ae801f762f92104a30d247a83529ac91c2a5900a7b17307f8b") + put("res/drawable-xhdpi-v4/abc_list_divider_mtrl_alpha.9.png", "0ef7b3d9f9d890a48e4fccc0db07315bd6fcca7049452e20a27a69bd22080415") + ` + let arr = str.split(/\n/ig).filter(v => v.trim()) + let arr2 = arr.map(v => { + // console.log(v) + v = v.trim().replace(/\s*put\(/, '').replace(/\)$/, '') + vArr = v.split(', ') + return { file: vArr[0].replace(/\"/ig, ''), hash: vArr[1].replace(/\"/ig, '') } + }) + return arr2 + } + + helpers.getPlatformsList(context).forEach(function (platform) { + var platformPath = path.join(projectRoot, 'platforms', platform); + var platformApi = platforms.getPlatformApi(platform, platformPath); + var platformInfo = platformApi.getPlatformInfo(); + var platformRes = platformInfo.locations.res; + var source = helpers.getFileMapContent(context, platform, 'ResIntegrity'); + var content = source.content; + + hashes = getHashList() + + if (platform === 'android') { + content = content.replace(/\s*put\("[^"]+",\s"[^"]{64}"\);/g, '') + .replace(/hashList\s*=.+\s*new.*(\(\d+\)[^\w]*)\);/, function (match, group) { + return match.replace(group, '()\n' + tab()); + }) + .replace(/hashList\s*=.+\s*new.*(\(.*\))/, function (match, group) { + var replace = match.replace(group, '(' + (hashes.length || '') + ')'); + if (hashes.length) { + replace += ' {{\n' + tab(); + hashes.forEach(function (h) { + replace += tab(2) + 'put("' + h.file + '", "' + h.hash + '");\n' + tab(); + }); + replace += tab() + '}}'; + } + return replace; + }); + // console.log(content) + try { + fs.writeFileSync(source.path, content, 'utf-8'); + } catch (e) { + helpers.exit('Unable to write java class source at path ' + source.path, e); + } + } + }); + + function tab(size) { + var str = ''; + for (var i = 0; i < (size || 1); i++) { + str += ' '; + } + return str; + } +}; \ No newline at end of file diff --git a/src/android/com/cescit/integrity/ApkIntegrity.java b/src/android/com/cescit/integrity/ApkIntegrity.java new file mode 100644 index 0000000..f3dc086 --- /dev/null +++ b/src/android/com/cescit/integrity/ApkIntegrity.java @@ -0,0 +1,92 @@ +package com.cescit.integrity; + +import org.json.JSONObject; +import android.content.Context; +import java.io.File; +import java.io.ByteArrayOutputStream; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.nio.charset.StandardCharsets; + +// apk完整性检验 +class ApkIntegrity { + + private static final String MESSAGE_DIGEST_ALGORITHM = "SHA-256"; + private static final String ASSETS_BASE_PATH = ""; + + // Collections.unmodifiableMap 使得返回的内容只能只读访问 + private static final Map hashList = Collections.unmodifiableMap( + new HashMap() + ); + + public static JSONObject check(Context context) throws Exception { + JSONObject result = new JSONObject(); + Map nowHashList = getHashMap(context); + String ret = HttpUtil.getHttpRequestData((String) Config.getConfig("APK_HASH_URL")); + JSONObject obj = new JSONObject(ret); + String upHash = obj.getString("apk"); + String nowHash = nowHashList.get("apk"); + if (!upHash.equals(nowHash)) { + throw new Exception("Content of APK has been tampered"); + } + result.put("res conunt", hashList.size()); + return result; + } + + // 获取文件对应的hash值 + public static Map getHashMap(Context context) throws Exception{ + File file = new File(context.getPackageCodePath()); + InputStream fis = new FileInputStream(file); + String fileHash = getFileHash(fis); + // res资源(路径:文件hash)键值对 + Map nowHashList = new HashMap(); + nowHashList.put("apk", fileHash); + return nowHashList; + } + + // 获取Res文件对应的hash值构造的String + public static String getHashString(Context context) throws Exception{ + Map nowHashList = getHashMap(context); + String str = ""; + // 遍历对比文件hash + for (Map.Entry entry : nowHashList.entrySet()) { + String fileName = entry.getKey(); + String presetHash = entry.getValue(); + if (!presetHash.equals("")) { + str += "put(\"" + fileName + "\", \"" + presetHash + "\");"; + } + } + // 用默认字符编码解码字符串。 + byte[] bs = str.getBytes(); + str = new String(bs, StandardCharsets.UTF_8); + return str; + } + + private static String getFileHash(InputStream file) throws IOException, NoSuchAlgorithmException { + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + int nRead; + byte[] data = new byte[16384]; + while ((nRead = file.read(data, 0, data.length)) != -1) { + buffer.write(data, 0, nRead); + } + buffer.flush(); + MessageDigest digest = MessageDigest.getInstance(MESSAGE_DIGEST_ALGORITHM); + byte[] hashBytes = digest.digest(buffer.toByteArray()); + StringBuffer hexString = new StringBuffer(); + for (int i = 0; i < hashBytes.length; i++) { + if ((0xff & hashBytes[i]) < 0x10) { + hexString.append("0"); + } + hexString.append(Integer.toHexString(0xFF & hashBytes[i])); + } + // Log.d("AntiTampering", String(hexString)); + return new String(hexString); + } + +} diff --git a/src/android/com/cescit/integrity/AssetsIntegrity.java b/src/android/com/cescit/integrity/AssetsIntegrity.java new file mode 100644 index 0000000..f78066f --- /dev/null +++ b/src/android/com/cescit/integrity/AssetsIntegrity.java @@ -0,0 +1,87 @@ +package com.cescit.integrity; + +import android.content.res.AssetManager; +import android.util.Base64; +import android.app.Activity; +import android.content.Context; + +import org.json.JSONObject; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + + +class AssetsIntegrity { + + private static final String MESSAGE_DIGEST_ALGORITHM = "SHA-256"; + private static final String ASSETS_BASE_PATH = ""; + + private static final Map hashList = Collections.unmodifiableMap( + new HashMap() + ); + + public static JSONObject check(Context context) throws Exception { + AssetManager assets = context.getAssets(); + for (Map.Entry entry : hashList.entrySet()) { + // byte[] fileNameDecode = Base64.decode(entry.getKey(), 0); + // String fileName = new String(fileNameDecode, StandardCharsets.UTF_8); + // Log.d("AntiTampering", fileName + " -> " + entry.getValue()); + String filePath = entry.getKey(); + InputStream file = assets.open(filePath); + String hash = getFileHash(file); + if (entry.getValue() == null || !entry.getValue().equals(hash)) { + throw new Exception("Content of " + filePath + " has been tampered"); + } + } + JSONObject result = new JSONObject(); + result.put("count", hashList.size()); + return result; + } + + // 获取Res文件对应的hash值构造的String + public static String getHashString(Context context) throws Exception{ + AssetManager assets = context.getAssets(); + String str = ""; + // 遍历对比文件hash + for (Map.Entry entry : hashList.entrySet()) { + String fileName = entry.getKey(); + String presetHash = entry.getValue(); + if (!presetHash.equals("")) { + str += "put(\"" + fileName + "\", \"" + presetHash + "\");"; + } + } + // 用默认字符编码解码字符串。 + byte[] bs = str.getBytes(); + str = new String(bs, StandardCharsets.UTF_8); + return str; + } + + private static String getFileHash(InputStream file) throws IOException, NoSuchAlgorithmException { + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + int nRead; + byte[] data = new byte[16384]; + while ((nRead = file.read(data, 0, data.length)) != -1) { + buffer.write(data, 0, nRead); + } + buffer.flush(); + MessageDigest digest = MessageDigest.getInstance(MESSAGE_DIGEST_ALGORITHM); + byte[] hashBytes = digest.digest(buffer.toByteArray()); + StringBuffer hexString = new StringBuffer(); + for (int i = 0; i < hashBytes.length; i++) { + if ((0xff & hashBytes[i]) < 0x10) { + hexString.append("0"); + } + hexString.append(Integer.toHexString(0xFF & hashBytes[i])); + } + // Log.d("AntiTampering", String(hexString)); + return new String(hexString); + } + +} diff --git a/src/android/com/cescit/integrity/CescitIntegrity.java b/src/android/com/cescit/integrity/CescitIntegrity.java new file mode 100644 index 0000000..c08d1c9 --- /dev/null +++ b/src/android/com/cescit/integrity/CescitIntegrity.java @@ -0,0 +1,98 @@ +package com.cescit.integrity; + +import android.app.Activity; +import android.content.Context; + +import org.apache.cordova.CallbackContext; +import org.apache.cordova.CordovaInterface; +import org.apache.cordova.CordovaPlugin; +import org.apache.cordova.CordovaWebView; +import org.apache.cordova.PluginResult; +import org.json.JSONArray; +import org.json.JSONObject; +import org.json.JSONException; + +public class CescitIntegrity extends CordovaPlugin { + + private Activity activity; + private Context context; + + public void initialize(CordovaInterface cordova, CordovaWebView webView) { + activity = cordova.getActivity(); + context = activity.getApplicationContext(); + checkAndStopExecution(); + super.initialize(cordova, webView); + } + + private void checkAndStopExecution() { + cordova.getThreadPool().execute(new Runnable() { + @Override + public void run () { + try { + String ret = HttpUtil.getHttpRequestData((String) Config.getConfig("APK_HASH_URL")); + JSONObject obj = new JSONObject(ret); + if(obj.getBoolean("ApkIntegrity")) { + ApkIntegrity.check(context); + } + if(obj.getBoolean("ResIntegrity")) { + ResIntegrity.check(context); + } + if(obj.getBoolean("AssetsIntegrity")) { + AssetsIntegrity.check(context); + } + } catch (final Exception e) { + e.printStackTrace(); + throw new TamperingException("Anti-Tampering check failed"); + } + } + }); + } + + public boolean execute(String action, JSONArray args, final CallbackContext callbackContext) throws JSONException { + + if ("verify".equals(action)) { + cordova.getThreadPool().execute(new Runnable() { + @Override + public void run () { + PluginResult result; + try { + // DebugDetection.check(activity.getPackageName()); + JSONObject response = new JSONObject(); + response.put("assets", AssetsIntegrity.check(context)); + response.put("res", ResIntegrity.check(context)); + response.put("apk", ApkIntegrity.check(context)); + result = new PluginResult(PluginResult.Status.OK, response); + } catch (Exception e) { + result = new PluginResult(PluginResult.Status.ERROR, e.toString()); + } + callbackContext.sendPluginResult(result); + } + }); + return true; + } + + if ("getList".equals(action)) { + cordova.getThreadPool().execute(new Runnable() { + @Override + public void run () { + PluginResult result; + try { + JSONObject response = new JSONObject(); + response.put("assets", AssetsIntegrity.getHashString(context)); + response.put("res", ResIntegrity.getHashString(context)); + response.put("apk", ApkIntegrity.getHashString(context)); + result = new PluginResult(PluginResult.Status.OK, response); + } catch (Exception e) { + result = new PluginResult(PluginResult.Status.ERROR, e.toString()); + } + callbackContext.sendPluginResult(result); + } + }); + return true; + } + + return false; + + } + +} diff --git a/src/android/com/cescit/integrity/Config.java b/src/android/com/cescit/integrity/Config.java new file mode 100644 index 0000000..98dede0 --- /dev/null +++ b/src/android/com/cescit/integrity/Config.java @@ -0,0 +1,20 @@ +package com.cescit.integrity; + +import java.util.HashMap; +import java.util.Map; + +class Config { + private static final String APK_HASH_URL = "http://webf.cewater.com.cn/apk-hash/jm.json"; + + // 获取配置 + public static T getConfig(String key) throws Exception{ + // res资源(路径:文件hash)键值对 + Map config = new HashMap(); + config.put("APK_HASH_URL", (T) APK_HASH_URL); + T result = null; + for(String configKey : config.keySet()){ + result = config.get(configKey); + } + return (T) result; + } +} \ No newline at end of file diff --git a/src/android/com/cescit/integrity/DebugDetection.java b/src/android/com/cescit/integrity/DebugDetection.java new file mode 100644 index 0000000..5e78ae1 --- /dev/null +++ b/src/android/com/cescit/integrity/DebugDetection.java @@ -0,0 +1,28 @@ +package com.cescit.integrity; + +import android.os.Debug; + +import java.lang.reflect.Field; + + +class DebugDetection { + + public static void check(String packageName) throws Exception { + if (hasDebuggerAttached()) { + throw new Exception("Debugger attached"); + } else if (getDebugField(packageName)) { + throw new Exception("App running in Debug mode"); + } + } + + private static Boolean getDebugField(String packageName) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException { + Class buildConfigClass = Class.forName(packageName.concat(".BuildConfig")); + Field debugField = buildConfigClass.getField("DEBUG"); + return debugField.getBoolean(null); + } + + private static Boolean hasDebuggerAttached() { + return Debug.isDebuggerConnected() || Debug.waitingForDebugger(); + } + +} diff --git a/src/android/com/cescit/integrity/HttpUtil.java b/src/android/com/cescit/integrity/HttpUtil.java new file mode 100644 index 0000000..fba9b10 --- /dev/null +++ b/src/android/com/cescit/integrity/HttpUtil.java @@ -0,0 +1,58 @@ +package com.cescit.integrity; + +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; + +public class HttpUtil { + + public static String getHttpRequestData(String urlPath) { + + // 首先抓取异常并处理 + String returnString = ""; + try{ + // 代码实现以GET请求方式为主,POST跳过 + /** 1 GET方式请求数据 start*/ + + // 1 创建URL对象,接收用户传递访问地址对象链接 + URL url = new URL(urlPath); + + // 2 打开用户传递URL参数地址 + HttpURLConnection connect = (HttpURLConnection) url.openConnection(); + + // 3 设置HTTP请求的一些参数信息 + connect.setRequestMethod("GET"); // 参数必须大写 + connect.connect(); + + // 4 获取URL请求到的数据,并创建数据流接收 + InputStream isString = connect.getInputStream(); + + // 5 构建一个字符流缓冲对象,承载URL读取到的数据 + BufferedReader isRead = new BufferedReader(new InputStreamReader(isString)); + + // 6 输出打印获取到的文件流 + String str = ""; + while ((str = isRead.readLine()) != null) { + str = new String(str.getBytes(),"UTF-8"); //解决中文乱码问题 +// System.out.println("文件解析打印:"); +// System.out.println(str); + returnString += str; + } + + // 7 关闭流 + isString.close(); + connect.disconnect(); + + // 8 JSON转List对象 + // do somthings + + + }catch(Exception e){ + e.printStackTrace(); + } + + return returnString; + } +} \ No newline at end of file diff --git a/src/android/com/cescit/integrity/ResIntegrity.java b/src/android/com/cescit/integrity/ResIntegrity.java new file mode 100644 index 0000000..8a39bdd --- /dev/null +++ b/src/android/com/cescit/integrity/ResIntegrity.java @@ -0,0 +1,118 @@ +package com.cescit.integrity; + +import org.json.JSONObject; +import android.content.Context; +import java.io.File; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; +import java.util.regex.*; +import java.nio.charset.StandardCharsets; + +// apk res资源完整性检验 +class ResIntegrity { + + private static final String MESSAGE_DIGEST_ALGORITHM = "SHA-256"; + private static final String ASSETS_BASE_PATH = ""; + + // Collections.unmodifiableMap 使得返回的内容只能只读访问 + private static final Map hashList = Collections.unmodifiableMap( + new HashMap() + ); + + public static JSONObject check(Context context) throws Exception { + JSONObject result = new JSONObject(); + Map resHashList = getResHashMap(context); + // 遍历对比文件hash + for (Map.Entry entry : hashList.entrySet()) { + // byte[] fileNameDecode = Base64.decode(entry.getKey(), 0); + // String fileName = new String(fileNameDecode, StandardCharsets.UTF_8); + String fileName = entry.getKey(); + String presetHash = entry.getValue(); + String filePath = ASSETS_BASE_PATH.concat(fileName); + String nowHash = resHashList.get(filePath); + if (presetHash == null || !presetHash.equals(nowHash)) { + throw new Exception("Content of " + filePath + " has been tampered"); + } + } + result.put("res conunt", hashList.size()); + return result; + } + + // 获取Res文件对应的hash值 + public static Map getResHashMap(Context context) throws Exception{ + File file = new File(context.getPackageCodePath()); + ZipFile zf = new ZipFile(file); + // res资源(路径:文件)键值对 + Map resList = new HashMap(); + // res资源(路径:文件hash)键值对 + Map resHashList = new HashMap(); + // 提取出apk里的res文件资源 + Enumeration entries = zf.entries(); + while(entries.hasMoreElements()) { + ZipEntry zipEntry = (ZipEntry) entries.nextElement(); + String pattern = "^res\\/.*"; + boolean isMatch = Pattern.matches(pattern, zipEntry.getName()); + if(isMatch) { + resList.put(zipEntry.getName(), zipEntry); + } + } + // 获取文件hash + for (Map.Entry resListItem : resList.entrySet()) { + String key = resListItem.getKey(); + ZipEntry fileEntry = resListItem.getValue(); + InputStream fileSteam= zf.getInputStream(fileEntry); + String fileHash = getFileHash(fileSteam); + resHashList.put(key, fileHash); + } + return resHashList; + } + + // 获取Res文件对应的hash值构造的String + public static String getHashString(Context context) throws Exception{ + Map resHashList = getResHashMap(context); + String str = ""; + // 遍历对比文件hash + for (Map.Entry entry : resHashList.entrySet()) { + String fileName = entry.getKey(); + String presetHash = entry.getValue(); + if (!presetHash.equals("")) { + str += "put(\"" + fileName + "\", \"" + presetHash + "\");"; + } + } + // 用默认字符编码解码字符串。 + byte[] bs = str.getBytes(); + str = new String(bs, StandardCharsets.UTF_8); + return str; + } + + private static String getFileHash(InputStream file) throws IOException, NoSuchAlgorithmException { + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + int nRead; + byte[] data = new byte[16384]; + while ((nRead = file.read(data, 0, data.length)) != -1) { + buffer.write(data, 0, nRead); + } + buffer.flush(); + MessageDigest digest = MessageDigest.getInstance(MESSAGE_DIGEST_ALGORITHM); + byte[] hashBytes = digest.digest(buffer.toByteArray()); + StringBuffer hexString = new StringBuffer(); + for (int i = 0; i < hashBytes.length; i++) { + if ((0xff & hashBytes[i]) < 0x10) { + hexString.append("0"); + } + hexString.append(Integer.toHexString(0xFF & hashBytes[i])); + } + // Log.d("AntiTampering", String(hexString)); + return new String(hexString); + } + +} diff --git a/src/android/com/cescit/integrity/TamperingException.java b/src/android/com/cescit/integrity/TamperingException.java new file mode 100644 index 0000000..98b65a0 --- /dev/null +++ b/src/android/com/cescit/integrity/TamperingException.java @@ -0,0 +1,9 @@ +package com.cescit.integrity; + +class TamperingException extends SecurityException { + + public TamperingException(String message) { + super(message); + } + +} \ No newline at end of file diff --git a/www/AntiTampering.js b/www/AntiTampering.js new file mode 100644 index 0000000..ef13858 --- /dev/null +++ b/www/AntiTampering.js @@ -0,0 +1,22 @@ +var exec = require('cordova/exec'); + +function AntiTampering () {} + +AntiTampering.prototype.verify = function (successCallback, errorCallback) { + exec(successCallback, + errorCallback, + 'CescitIntegrity', + 'verify', + []); +}; + +AntiTampering.prototype.getList = function (successCallback, errorCallback) { + exec(successCallback, + errorCallback, + 'CescitIntegrity', + 'getList', + []); +}; + + +module.exports = new AntiTampering();