mirror of
https://github.com/revanced/Apktool.git
synced 2025-04-30 06:04:25 +02:00
Merge branch 'upstream'
# Conflicts: # brut.apktool/apktool-lib/build.gradle.kts # build.gradle.kts
This commit is contained in:
commit
35e23a9ad7
3
.dockerignore
Normal file
3
.dockerignore
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
./docker
|
||||||
|
./github
|
||||||
|
*.md
|
1
.github/assets/sponsors/emerge-tools-vertical-black.svg
vendored
Normal file
1
.github/assets/sponsors/emerge-tools-vertical-black.svg
vendored
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 9.4 KiB |
1
.github/assets/sponsors/emerge-tools-vertical-white.svg
vendored
Normal file
1
.github/assets/sponsors/emerge-tools-vertical-white.svg
vendored
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 9.5 KiB |
217
.github/assets/sponsors/sourcetoad-horizontal.svg
vendored
Normal file
217
.github/assets/sponsors/sourcetoad-horizontal.svg
vendored
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 962.84482 136.337"
|
||||||
|
version="1.1"
|
||||||
|
id="svg3387"
|
||||||
|
width="150.84479"
|
||||||
|
height="136.33701"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<style
|
||||||
|
id="style3356">
|
||||||
|
.head, .body, .hindlimb-second, .foot {
|
||||||
|
fill: rgb(132, 189, 0);
|
||||||
|
}
|
||||||
|
.forelimb, .first-toe, .second-toe, .third-toe, .hindlimb-first {
|
||||||
|
fill: rgb(167, 213, 0);
|
||||||
|
}
|
||||||
|
.center-stripe, .hindlimb-third {
|
||||||
|
fill: rgb(84, 146, 0);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<defs
|
||||||
|
id="defs3383">
|
||||||
|
<g
|
||||||
|
id="left-half-def">
|
||||||
|
<path
|
||||||
|
class="head"
|
||||||
|
d="M 80.95,2.827 C 76.575,2.395 30.015,54.22 30.015,54.22 l 17.201,21.661 z"
|
||||||
|
id="path3358" />
|
||||||
|
<path
|
||||||
|
class="body"
|
||||||
|
d="M 34.499,109.709 77.779,14.768 V 134.516 Z"
|
||||||
|
id="path3360" />
|
||||||
|
<path
|
||||||
|
class="center-stripe"
|
||||||
|
d="m 80.402,9.112 h 0.548 v 127.225 l -3.171,-1.821 V 14.768 Z"
|
||||||
|
id="path3362" />
|
||||||
|
<path
|
||||||
|
class="center-stripe"
|
||||||
|
d="m 80.95,136.337 -3.171,-1.821 V 14.768 l 2.623,-5.656 h 0.548 z"
|
||||||
|
id="path3364" />
|
||||||
|
<path
|
||||||
|
class="forelimb"
|
||||||
|
d="M 28.473,51.545 47.75,31.06 30.837,28.98 Z"
|
||||||
|
id="path3366" />
|
||||||
|
<path
|
||||||
|
class="first-toe"
|
||||||
|
d="M 33.71,27.168 47.971,11.58 41.571,28.063 Z"
|
||||||
|
id="path3368" />
|
||||||
|
<path
|
||||||
|
class="second-toe"
|
||||||
|
d="M 37.563,19.132 31.993,13.184 34.644,0 l 6.61,15.132 z"
|
||||||
|
id="path3370" />
|
||||||
|
<path
|
||||||
|
class="third-toe"
|
||||||
|
d="M 31.463,26.207 22.135,5.884 35.95,20.859 Z"
|
||||||
|
id="path3372" />
|
||||||
|
<path
|
||||||
|
class="hindlimb-first"
|
||||||
|
d="M 33.153,106.835 21.269,86.614 41.38,89.218 Z"
|
||||||
|
id="path3374" />
|
||||||
|
<path
|
||||||
|
class="hindlimb-second"
|
||||||
|
d="m 19.005,87.743 -5.2,22.166 18.249,-0.076 z"
|
||||||
|
id="path3376" />
|
||||||
|
<path
|
||||||
|
class="hindlimb-third"
|
||||||
|
d="m 47.966,120.204 -24.08,-0.12 -9.971,-8.004 19.356,0.073 z"
|
||||||
|
id="path3378" />
|
||||||
|
<path
|
||||||
|
class="foot"
|
||||||
|
d="M 15.52,117.01 0,135.967 l 41.224,-1.406 6.106,-11.917 -24.445,0.22 z"
|
||||||
|
id="path3380" />
|
||||||
|
</g>
|
||||||
|
</defs>
|
||||||
|
<g
|
||||||
|
id="g11935">
|
||||||
|
<g
|
||||||
|
id="g11678">
|
||||||
|
<path
|
||||||
|
class="head"
|
||||||
|
d="M 80.95,2.827 C 76.575,2.395 30.015,54.22 30.015,54.22 l 17.201,21.661 z"
|
||||||
|
id="path10911" />
|
||||||
|
<path
|
||||||
|
class="body"
|
||||||
|
d="M 34.499,109.709 77.779,14.768 V 134.516 Z"
|
||||||
|
id="path10913" />
|
||||||
|
<path
|
||||||
|
class="center-stripe"
|
||||||
|
d="m 80.402,9.112 h 0.548 v 127.225 l -3.171,-1.821 V 14.768 Z"
|
||||||
|
id="path10915" />
|
||||||
|
<path
|
||||||
|
class="forelimb"
|
||||||
|
d="M 28.473,51.545 47.75,31.06 30.837,28.98 Z"
|
||||||
|
id="path10919" />
|
||||||
|
<path
|
||||||
|
class="first-toe"
|
||||||
|
d="M 33.71,27.168 47.971,11.58 41.571,28.063 Z"
|
||||||
|
id="path10921" />
|
||||||
|
<path
|
||||||
|
class="second-toe"
|
||||||
|
d="M 37.563,19.132 31.993,13.184 34.644,0 l 6.61,15.132 z"
|
||||||
|
id="path10923" />
|
||||||
|
<path
|
||||||
|
class="third-toe"
|
||||||
|
d="M 31.463,26.207 22.135,5.884 35.95,20.859 Z"
|
||||||
|
id="path10925" />
|
||||||
|
<path
|
||||||
|
class="hindlimb-first"
|
||||||
|
d="M 33.153,106.835 21.269,86.614 41.38,89.218 Z"
|
||||||
|
id="path10927" />
|
||||||
|
<path
|
||||||
|
class="hindlimb-second"
|
||||||
|
d="m 19.005,87.743 -5.2,22.166 18.249,-0.076 z"
|
||||||
|
id="path10929" />
|
||||||
|
<path
|
||||||
|
class="hindlimb-third"
|
||||||
|
d="m 47.966,120.204 -24.08,-0.12 -9.971,-8.004 19.356,0.073 z"
|
||||||
|
id="path10931" />
|
||||||
|
<path
|
||||||
|
class="foot"
|
||||||
|
d="M 15.52,117.01 0,135.967 l 41.224,-1.406 6.106,-11.917 -24.445,0.22 z"
|
||||||
|
id="path10933" />
|
||||||
|
<path
|
||||||
|
class="head"
|
||||||
|
d="M 80.95,2.827 C 85.325,2.395 131.885,54.22 131.885,54.22 L 114.684,75.881 Z"
|
||||||
|
id="path10937" />
|
||||||
|
<path
|
||||||
|
class="body"
|
||||||
|
d="M 127.401,109.709 84.121,14.768 v 119.748 z"
|
||||||
|
id="path10939" />
|
||||||
|
<path
|
||||||
|
class="center-stripe"
|
||||||
|
d="M 81.498,9.112 H 80.95 v 127.225 l 3.171,-1.821 V 14.768 Z"
|
||||||
|
id="path10941" />
|
||||||
|
<path
|
||||||
|
id="path10943"
|
||||||
|
class="center-stripe"
|
||||||
|
d="M 77.779,134.516 V 14.768 l 2.623,-5.656 h 1.096 l 2.623,5.656 v 119.748 l -3.171,1.821 z" />
|
||||||
|
<path
|
||||||
|
class="forelimb"
|
||||||
|
d="M 133.427,51.545 114.15,31.06 131.063,28.98 Z"
|
||||||
|
id="path10945" />
|
||||||
|
<path
|
||||||
|
class="first-toe"
|
||||||
|
d="m 128.19,27.168 -14.261,-15.588 6.4,16.483 z"
|
||||||
|
id="path10947" />
|
||||||
|
<path
|
||||||
|
class="second-toe"
|
||||||
|
d="m 124.337,19.132 5.57,-5.948 L 127.256,0 l -6.61,15.132 z"
|
||||||
|
id="path10949" />
|
||||||
|
<path
|
||||||
|
class="third-toe"
|
||||||
|
d="M 130.437,26.207 139.765,5.884 125.95,20.859 Z"
|
||||||
|
id="path10951" />
|
||||||
|
<path
|
||||||
|
class="hindlimb-first"
|
||||||
|
d="M 128.747,106.835 140.631,86.614 120.52,89.218 Z"
|
||||||
|
id="path10953" />
|
||||||
|
<path
|
||||||
|
class="hindlimb-second"
|
||||||
|
d="m 142.895,87.743 5.2,22.166 -18.249,-0.076 z"
|
||||||
|
id="path10955" />
|
||||||
|
<path
|
||||||
|
class="hindlimb-third"
|
||||||
|
d="m 113.934,120.204 24.08,-0.12 9.971,-8.004 -19.356,0.073 z"
|
||||||
|
id="path10957" />
|
||||||
|
<path
|
||||||
|
class="foot"
|
||||||
|
d="m 146.38,117.01 15.52,18.957 -41.224,-1.406 -6.106,-11.917 24.445,0.22 z"
|
||||||
|
id="path10959" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g11898">
|
||||||
|
<path
|
||||||
|
d="m 227.66966,44.130741 -6.35791,6.569851 q -7.91208,-7.700147 -15.47094,-7.700147 -4.80376,0 -8.2653,3.178959 -3.39089,3.17896 -3.39089,7.417573 0,3.744109 2.82574,7.134999 2.82575,3.461534 11.86812,8.124008 11.02039,5.722128 14.97643,11.020395 3.8854,5.368909 3.8854,12.080047 0,9.466234 -6.6405,16.036084 -6.64049,6.56985 -16.60123,6.56985 -6.64049,0 -12.71584,-2.89638 -6.0047,-2.89639 -9.96074,-7.98272 l 6.21663,-7.06436 q 7.55886,8.54787 16.03609,8.54787 5.93406,0 10.10203,-3.81475 4.16797,-3.814754 4.16797,-8.971733 0,-4.238613 -2.7551,-7.55886 -2.7551,-3.249603 -12.43327,-8.194652 -10.3846,-5.36891 -14.12871,-10.596533 -3.74411,-5.227622 -3.74411,-11.93876 0,-8.7598 5.93406,-14.552571 6.0047,-5.792772 15.11772,-5.792772 10.59653,0 21.33435,10.384602 z"
|
||||||
|
id="path11847"
|
||||||
|
style="font-size:144.678px;line-height:1.25;font-family:'Century Gothic';-inkscape-font-specification:'Century Gothic, Normal';fill:#84bd00;stroke-width:3.61692" />
|
||||||
|
<path
|
||||||
|
d="m 281.64134,33.746139 q 17.73153,0 29.38771,12.857127 10.59654,11.726829 10.59654,27.762916 0,16.106729 -11.23233,28.186778 -11.16168,12.0094 -28.75192,12.0094 -17.66089,0 -28.89322,-12.0094 -11.16168,-12.080049 -11.16168,-28.186778 0,-15.965443 10.59654,-27.692273 11.65618,-12.92777 29.45836,-12.92777 z m 0,9.678167 q -12.29198,0 -21.12243,9.113018 -8.83044,9.113018 -8.83044,22.040788 0,8.335939 4.02668,15.541582 4.02669,7.205642 10.87911,11.161676 6.85242,3.8854 15.04708,3.8854 8.19465,0 15.04707,-3.8854 6.85243,-3.956034 10.87911,-11.161676 4.02668,-7.205643 4.02668,-15.541582 0,-12.92777 -8.90109,-22.040788 -8.83044,-9.113018 -21.05177,-9.113018 z"
|
||||||
|
id="path11849"
|
||||||
|
style="font-size:144.678px;line-height:1.25;font-family:'Century Gothic';-inkscape-font-specification:'Century Gothic, Normal';fill:#84bd00;stroke-width:3.61692" />
|
||||||
|
<path
|
||||||
|
d="m 338.65068,35.724159 h 9.96074 v 35.886924 q 0,13.139701 1.41287,18.084749 2.11931,7.064355 8.05337,11.161678 6.0047,4.09733 14.26999,4.09733 8.2653,0 13.98743,-3.95604 5.79277,-4.026682 7.98272,-10.525889 1.48351,-4.450543 1.48351,-18.861828 V 35.724159 h 10.10203 v 37.723656 q 0,15.894799 -3.74411,23.948164 -3.67346,8.053361 -11.16168,12.645201 -7.41757,4.52118 -18.6499,4.52118 -11.23232,0 -18.79118,-4.52118 -7.48822,-4.59184 -11.23232,-12.715844 -3.67347,-8.194652 -3.67347,-24.583956 z"
|
||||||
|
id="path11851"
|
||||||
|
style="font-size:144.678px;line-height:1.25;font-family:'Century Gothic';-inkscape-font-specification:'Century Gothic, Normal';fill:#84bd00;stroke-width:3.61692" />
|
||||||
|
<path
|
||||||
|
d="m 424.05874,35.724159 h 10.10203 v 11.232324 q 4.52119,-6.640493 9.53688,-9.890097 5.01569,-3.320247 10.45524,-3.320247 4.09733,0 8.7598,2.613812 l -5.15697,8.335939 q -3.10832,-1.342228 -5.22763,-1.342228 -4.94505,0 -9.53688,4.097326 -4.59183,4.026683 -6.99371,12.574553 -1.83673,6.56985 -1.83673,26.561975 v 25.996824 h -10.10203 z"
|
||||||
|
id="path11853"
|
||||||
|
style="font-size:144.678px;line-height:1.25;font-family:'Century Gothic';-inkscape-font-specification:'Century Gothic, Normal';fill:#84bd00;stroke-width:3.61692" />
|
||||||
|
<path
|
||||||
|
d="m 544.8592,51.689601 -7.84144,4.874405 q -10.17267,-13.492918 -27.76291,-13.492918 -14.05807,0 -23.38302,9.042375 -9.2543,9.042374 -9.2543,21.970144 0,8.406583 4.23861,15.824156 4.30926,7.417573 11.72683,11.514897 7.48821,4.09733 16.74252,4.09733 16.95445,0 27.69227,-13.492921 l 7.84144,5.15698 q -5.5102,8.265291 -14.83515,12.857131 -9.2543,4.52118 -21.12242,4.52118 -18.22604,0 -30.23544,-11.58554 -12.00941,-11.585543 -12.00941,-28.186777 0,-11.161681 5.58085,-20.698561 5.65148,-9.607523 15.47093,-14.976433 9.81946,-5.36891 21.97015,-5.36891 7.6295,0 14.69386,2.331238 7.135,2.331237 12.08004,6.075345 4.94505,3.744108 8.40659,9.536879 z"
|
||||||
|
id="path11855"
|
||||||
|
style="font-size:144.678px;line-height:1.25;font-family:'Century Gothic';-inkscape-font-specification:'Century Gothic, Normal';fill:#84bd00;stroke-width:3.61692" />
|
||||||
|
<path
|
||||||
|
d="m 628.43053,87.082021 8.33593,4.3799 q -4.09732,8.053365 -9.46623,12.998409 -5.36891,4.94505 -12.08005,7.55886 -6.71114,2.54317 -15.18836,2.54317 -18.79119,0 -29.38772,-12.29198 -10.59653,-12.362617 -10.59653,-27.904198 0,-14.623216 8.97173,-26.067471 11.37361,-14.552572 30.44737,-14.552572 19.63891,0 31.36574,14.90579 8.33594,10.525889 8.47722,26.279401 h -69.08939 q 0.28257,13.422275 8.54787,22.040788 8.26529,8.547872 20.41598,8.547872 5.86342,0 11.37362,-2.04867 5.58084,-2.04866 9.46623,-5.439549 3.8854,-3.39089 8.40659,-10.94975 z m 0,-20.698561 q -1.97802,-7.912078 -5.79278,-12.645196 -3.7441,-4.733118 -9.96074,-7.629503 -6.21663,-2.896386 -13.06905,-2.896386 -11.30297,0 -19.42698,7.276286 -5.93406,5.298266 -8.97173,15.894799 z"
|
||||||
|
id="path11857"
|
||||||
|
style="font-size:144.678px;line-height:1.25;font-family:'Century Gothic';-inkscape-font-specification:'Century Gothic, Normal';fill:#84bd00;stroke-width:3.61692" />
|
||||||
|
<path
|
||||||
|
d="m 657.45273,7.3960946 h 19.21504 V 35.724159 h 11.44426 V 52.325393 H 676.66777 V 112.58434 H 657.45273 V 52.325393 h -9.8901 V 35.724159 h 9.8901 z"
|
||||||
|
id="path11838"
|
||||||
|
style="font-weight:bold;font-size:144.678px;line-height:1.25;font-family:'Century Gothic';-inkscape-font-specification:'Century Gothic, Bold';fill:#84bd00;stroke-width:3.61692" />
|
||||||
|
<path
|
||||||
|
d="m 734.73677,33.746139 q 10.87911,0 20.41599,5.439554 9.60752,5.439553 14.97643,14.764502 5.36891,9.324949 5.36891,20.133412 0,10.879107 -5.43955,20.345343 -5.36891,9.46624 -14.69386,14.83515 -9.32495,5.29826 -20.55727,5.29826 -16.5306,0 -28.25743,-11.72683 -11.65618,-11.79747 -11.65618,-28.610636 0,-18.014105 13.21034,-30.023509 11.58555,-10.455246 26.63262,-10.455246 z m 0.28258,18.155393 q -8.97174,0 -14.97644,6.287276 -5.93406,6.216633 -5.93406,15.965443 0,10.031384 5.86342,16.248017 5.93406,6.216632 14.97643,6.216632 9.04238,0 15.04708,-6.287276 6.0047,-6.287276 6.0047,-16.177373 0,-9.890097 -5.93406,-16.036086 -5.86341,-6.216633 -15.04707,-6.216633 z"
|
||||||
|
id="path11840"
|
||||||
|
style="font-weight:bold;font-size:144.678px;line-height:1.25;font-family:'Century Gothic';-inkscape-font-specification:'Century Gothic, Bold';fill:#84bd00;stroke-width:3.61692" />
|
||||||
|
<path
|
||||||
|
d="m 848.11968,35.724159 h 19.21505 v 76.860181 h -19.21505 v -8.12401 q -5.65148,5.36891 -11.37361,7.7708 -5.65148,2.33123 -12.29198,2.33123 -14.90579,0 -25.78489,-11.5149 -10.87911,-11.585539 -10.87911,-28.751922 0,-17.802175 10.52589,-29.175787 10.52589,-11.373612 25.57296,-11.373612 6.92307,0 12.99842,2.613812 6.07534,2.613811 11.23232,7.841434 z m -20.2747,15.824155 q -8.97173,0 -14.90579,6.35792 -5.93406,6.287276 -5.93406,16.177373 0,9.960741 6.00471,16.389304 6.07534,6.428563 14.90579,6.428563 9.11301,0 15.11772,-6.287276 6.0047,-6.357919 6.0047,-16.601234 0,-10.031385 -6.0047,-16.248017 -6.00471,-6.216633 -15.18837,-6.216633 z"
|
||||||
|
id="path11842"
|
||||||
|
style="font-weight:bold;font-size:144.678px;line-height:1.25;font-family:'Century Gothic';-inkscape-font-specification:'Century Gothic, Bold';fill:#84bd00;stroke-width:3.61692" />
|
||||||
|
<path
|
||||||
|
d="M 943.62975,6.0538671 H 962.8448 V 112.58434 h -19.21505 v -8.12401 q -5.65148,5.36891 -11.37361,7.7708 -5.65148,2.33123 -12.29198,2.33123 -14.90579,0 -25.78489,-11.5149 -10.87911,-11.585539 -10.87911,-28.751922 0,-17.802175 10.52589,-29.175787 10.52589,-11.373612 25.57296,-11.373612 6.92307,0 12.99842,2.613812 6.07534,2.613811 11.23232,7.841434 z m -20.2747,45.4944469 q -8.97173,0 -14.90579,6.35792 -5.93405,6.287276 -5.93405,16.177373 0,9.960741 6.0047,16.389304 6.07534,6.428563 14.90579,6.428563 9.11301,0 15.11772,-6.287276 6.0047,-6.357919 6.0047,-16.601234 0,-10.031385 -6.0047,-16.248017 -6.00471,-6.216633 -15.18837,-6.216633 z"
|
||||||
|
id="path11844"
|
||||||
|
style="font-weight:bold;font-size:144.678px;line-height:1.25;font-family:'Century Gothic';-inkscape-font-specification:'Century Gothic, Bold';fill:#84bd00;stroke-width:3.61692" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 13 KiB |
2
.github/workflows/analyze.yml
vendored
2
.github/workflows/analyze.yml
vendored
@ -14,7 +14,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@v2
|
uses: github/codeql-action/init@v2
|
||||||
|
16
.github/workflows/build.yml
vendored
16
.github/workflows/build.yml
vendored
@ -10,6 +10,7 @@ on:
|
|||||||
- 'brut.apktool/apktool-lib/src/main/resources/**'
|
- 'brut.apktool/apktool-lib/src/main/resources/**'
|
||||||
- 'brut.apktool/apktool-lib/src/test/**'
|
- 'brut.apktool/apktool-lib/src/test/**'
|
||||||
- '.github/workflows/**'
|
- '.github/workflows/**'
|
||||||
|
- 'gradle/libs.versions.toml'
|
||||||
- 'gradle/wrapper/**'
|
- 'gradle/wrapper/**'
|
||||||
- 'gradlew'
|
- 'gradlew'
|
||||||
- 'gradlew.bat'
|
- 'gradlew.bat'
|
||||||
@ -27,7 +28,7 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
file: [ aapt_64, aapt2_64 ]
|
file: [ aapt_64, aapt2_64 ]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Verify Executable
|
- name: Verify Executable
|
||||||
run: ${{ env.BINARY_PATH }}/macosx/${{ matrix.file }} version
|
run: ${{ env.BINARY_PATH }}/macosx/${{ matrix.file }} version
|
||||||
- name: Output Static
|
- name: Output Static
|
||||||
@ -39,7 +40,7 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
file: [ aapt, aapt_64, aapt2, aapt2_64 ]
|
file: [ aapt, aapt_64, aapt2, aapt2_64 ]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Verify Executable
|
- name: Verify Executable
|
||||||
run: ${{ env.BINARY_PATH }}/linux/${{ matrix.file }} version
|
run: ${{ env.BINARY_PATH }}/linux/${{ matrix.file }} version
|
||||||
- name: Output Static
|
- name: Output Static
|
||||||
@ -51,7 +52,7 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
file: [ aapt.exe, aapt_64.exe, aapt2.exe, aapt2_64.exe ]
|
file: [ aapt.exe, aapt_64.exe, aapt2.exe, aapt2_64.exe ]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Verify Executable
|
- name: Verify Executable
|
||||||
run: ${{ env.BINARY_PATH }}/windows/${{ matrix.file }} version
|
run: ${{ env.BINARY_PATH }}/windows/${{ matrix.file }} version
|
||||||
- name: Output Static
|
- name: Output Static
|
||||||
@ -70,13 +71,13 @@ jobs:
|
|||||||
os: [ ubuntu-latest, macOS-latest, windows-latest ]
|
os: [ ubuntu-latest, macOS-latest, windows-latest ]
|
||||||
java: [ 8, 11, 17, 18, 19, 20 ]
|
java: [ 8, 11, 17, 18, 19, 20 ]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-java@v3
|
- uses: actions/setup-java@v3
|
||||||
with:
|
with:
|
||||||
distribution: 'zulu'
|
distribution: 'zulu'
|
||||||
java-version: ${{ matrix.java }}
|
java-version: ${{ matrix.java }}
|
||||||
- name: Build and test
|
- name: Build and test
|
||||||
uses: gradle/gradle-build-action@v2.7.1
|
uses: gradle/gradle-build-action@v2.9.0
|
||||||
with:
|
with:
|
||||||
arguments: build shadowJar proguard
|
arguments: build shadowJar proguard
|
||||||
|
|
||||||
@ -87,7 +88,7 @@ jobs:
|
|||||||
needs:
|
needs:
|
||||||
- build-and-test-with-Java-8-and-later
|
- build-and-test-with-Java-8-and-later
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- uses: actions/setup-java@v3
|
- uses: actions/setup-java@v3
|
||||||
@ -95,8 +96,9 @@ jobs:
|
|||||||
distribution: 'zulu'
|
distribution: 'zulu'
|
||||||
java-version: 17
|
java-version: 17
|
||||||
- name: Build
|
- name: Build
|
||||||
uses: gradle/gradle-build-action@v2.7.1
|
uses: gradle/gradle-build-action@v2.9.0
|
||||||
with:
|
with:
|
||||||
|
dependency-graph: generate-and-submit
|
||||||
arguments: build shadowJar proguard
|
arguments: build shadowJar proguard
|
||||||
- name: Upload
|
- name: Upload
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
|
47
.github/workflows/docker-beta.yml
vendored
Normal file
47
.github/workflows/docker-beta.yml
vendored
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
name: Build and push docker (beta)
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches: [master]
|
||||||
|
types: [closed]
|
||||||
|
|
||||||
|
env:
|
||||||
|
REGISTRY: ghcr.io
|
||||||
|
IMAGE_NAME: ibotpeaches/apktool
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
|
||||||
|
build_and_push:
|
||||||
|
name: Build and push
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Log in to the Container registry
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Get hash
|
||||||
|
id: hash
|
||||||
|
run: echo "APKTOOL_HASH=$(echo $GITHUB_SHA | cut -c 1-6)" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v2
|
||||||
|
|
||||||
|
- name: Build and push Docker image
|
||||||
|
uses: docker/build-push-action@v4
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
platforms: linux/amd64
|
||||||
|
file: ./docker/Dockerfile
|
||||||
|
push: true
|
||||||
|
tags: |
|
||||||
|
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.APKTOOL_HASH }}
|
||||||
|
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:beta
|
||||||
|
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:master
|
50
.github/workflows/docker-release.yml
vendored
Normal file
50
.github/workflows/docker-release.yml
vendored
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
name: Build and push docker (release)
|
||||||
|
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: [published]
|
||||||
|
|
||||||
|
env:
|
||||||
|
REGISTRY: ghcr.io
|
||||||
|
IMAGE_NAME: ibotpeaches/apktool
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build_and_push:
|
||||||
|
name: Build and push
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Log in to the Container registry
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Get hash
|
||||||
|
id: hash
|
||||||
|
run: echo "APKTOOL_HASH=$(echo $GITHUB_SHA | cut -c 1-6)" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Get release tag
|
||||||
|
id: tag
|
||||||
|
run: echo "APKTOOL_TAG=$(echo ${GITHUB_REF:-no-tag} |sed 's|refs/tags/||g')" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v2
|
||||||
|
|
||||||
|
- name: Build and push Docker image
|
||||||
|
uses: docker/build-push-action@v4
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
platforms: linux/amd64
|
||||||
|
file: ./docker/Dockerfile
|
||||||
|
push: true
|
||||||
|
tags: |
|
||||||
|
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.APKTOOL_HASH }}
|
||||||
|
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.APKTOOL_TAG }}
|
||||||
|
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:prod
|
||||||
|
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
|
2
.github/workflows/gradle.yml
vendored
2
.github/workflows/gradle.yml
vendored
@ -16,7 +16,7 @@ jobs:
|
|||||||
validate-gradle-wrapper:
|
validate-gradle-wrapper:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-java@v3
|
- uses: actions/setup-java@v3
|
||||||
with:
|
with:
|
||||||
distribution: 'zulu'
|
distribution: 'zulu'
|
||||||
|
@ -251,8 +251,8 @@ we aren't building the entire AOSP package, the initial build is to just see if
|
|||||||
|
|
||||||
We check out a certain tag or branch. Currently we use
|
We check out a certain tag or branch. Currently we use
|
||||||
|
|
||||||
* aapt2 - `master`.
|
* aapt2 - `android-14.0.0_r2`.
|
||||||
* aapt1 - `master`.
|
* aapt1 - `android-14.0.0_r2`.
|
||||||
|
|
||||||
### Including our modified `frameworks/base` package.
|
### Including our modified `frameworks/base` package.
|
||||||
|
|
||||||
|
34
README.md
34
README.md
@ -12,14 +12,6 @@ It is NOT intended for piracy and other non-legal uses. It could be used for loc
|
|||||||
- [Project Page](https://ibotpeaches.github.io/Apktool/)
|
- [Project Page](https://ibotpeaches.github.io/Apktool/)
|
||||||
- [#apktool on libera.chat](https://web.libera.chat/)
|
- [#apktool on libera.chat](https://web.libera.chat/)
|
||||||
|
|
||||||
#### Sponsored by
|
|
||||||
|
|
||||||
* [Sourcetoad](https://www.sourcetoad.com/cool-tools/apktool/) - helping with a weekly sponsorship for continued improvement and maintenance of the project.
|
|
||||||
|
|
||||||
#### IDE of Choice
|
|
||||||
|
|
||||||
* [JetBrains IntelliJ](https://www.jetbrains.com/idea/)
|
|
||||||
|
|
||||||
#### Security Vulnerabilities
|
#### Security Vulnerabilities
|
||||||
|
|
||||||
If you discover a security vulnerability within Apktool, please send an e-mail to Connor Tumbleson at connor.tumbleson(at)gmail.com. All security vulnerabilities will be promptly addressed.
|
If you discover a security vulnerability within Apktool, please send an e-mail to Connor Tumbleson at connor.tumbleson(at)gmail.com. All security vulnerabilities will be promptly addressed.
|
||||||
@ -29,9 +21,35 @@ If you discover a security vulnerability within Apktool, please send an e-mail t
|
|||||||
- [Downloads Mirror](https://connortumbleson.com/apktool/)
|
- [Downloads Mirror](https://connortumbleson.com/apktool/)
|
||||||
- [How to Build](https://ibotpeaches.github.io/Apktool/build/)
|
- [How to Build](https://ibotpeaches.github.io/Apktool/build/)
|
||||||
- [Documentation](https://ibotpeaches.github.io/Apktool/documentation/)
|
- [Documentation](https://ibotpeaches.github.io/Apktool/documentation/)
|
||||||
|
- [Use in Docker](./docker/README.md)
|
||||||
- [Bug Reports](https://github.com/iBotPeaches/Apktool/issues)
|
- [Bug Reports](https://github.com/iBotPeaches/Apktool/issues)
|
||||||
- [Changelog/Information](https://ibotpeaches.github.io/Apktool/changes/)
|
- [Changelog/Information](https://ibotpeaches.github.io/Apktool/changes/)
|
||||||
- [XDA Post](https://forum.xda-developers.com/t/util-dec-2-2020-apktool-tool-for-reverse-engineering-apk-files.1755243/)
|
- [XDA Post](https://forum.xda-developers.com/t/util-dec-2-2020-apktool-tool-for-reverse-engineering-apk-files.1755243/)
|
||||||
- [Source (Github)](https://github.com/iBotPeaches/Apktool)
|
- [Source (Github)](https://github.com/iBotPeaches/Apktool)
|
||||||
- [Source (Bitbucket)](https://bitbucket.org/iBotPeaches/apktool/)
|
- [Source (Bitbucket)](https://bitbucket.org/iBotPeaches/apktool/)
|
||||||
|
|
||||||
|
|
||||||
|
## Sponsors
|
||||||
|
|
||||||
|
Special thanks goes to the following sponsors:
|
||||||
|
|
||||||
|
### Sourcetoad
|
||||||
|
[Sourcetoad](https://sourcetoad.com/) is an award-winning software and app development firm committed to the co-creation of technology solutions that solve complex business problems, delight users, and help our clients achieve their goals.
|
||||||
|
|
||||||
|
<a href="https://www.sourcetoad.com" alt="Sourcetoad">
|
||||||
|
<picture>
|
||||||
|
<img src="https://github.com/ibotpeaches/apktool/raw/master/.github/assets/sponsors/sourcetoad-horizontal.svg">
|
||||||
|
</picture>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
### Emerge Tools
|
||||||
|
|
||||||
|
[Emerge Tools](https://www.emergetools.com) is a suite of revolutionary products designed to supercharge mobile apps and the teams that build them.
|
||||||
|
|
||||||
|
<a href="https://www.emergetools.com" alt="Emerge Tools">
|
||||||
|
<picture>
|
||||||
|
<source media="(prefers-color-scheme: dark)" srcset="https://github.com/ibotpeaches/apktool/raw/master/.github/assets/sponsors/emerge-tools-vertical-white.svg">
|
||||||
|
<source media="(prefers-color-scheme: light)" srcset="https://github.com/ibotpeaches/apktool/raw/master/.github/assets/sponsors/emerge-tools-vertical-black.svg">
|
||||||
|
<img src="https://github.com/ibotpeaches/apktool/raw/master/.github/assets/sponsors/emerge-tools-vertical-black.svg">
|
||||||
|
</picture>
|
||||||
|
</a>
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
import proguard.gradle.ProGuardTask
|
import proguard.gradle.ProGuardTask
|
||||||
|
|
||||||
val commonsCliVersion: String by rootProject.extra
|
|
||||||
val apktoolVersion: String by rootProject.extra
|
val apktoolVersion: String by rootProject.extra
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("com.github.johnrengelman.shadow")
|
alias(libs.plugins.shadow)
|
||||||
application
|
application
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -14,12 +13,12 @@ plugins {
|
|||||||
buildscript {
|
buildscript {
|
||||||
dependencies {
|
dependencies {
|
||||||
// Proguard doesn't support plugin DSL - https://github.com/Guardsquare/proguard/issues/225
|
// Proguard doesn't support plugin DSL - https://github.com/Guardsquare/proguard/issues/225
|
||||||
classpath("com.guardsquare:proguard-gradle:7.3.2")
|
classpath(libs.proguard)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation("commons-cli:commons-cli:$commonsCliVersion")
|
implementation(libs.commons.cli)
|
||||||
implementation(project(":brut.apktool:apktool-lib"))
|
implementation(project(":brut.apktool:apktool-lib"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@ public class Main {
|
|||||||
CommandLine commandLine;
|
CommandLine commandLine;
|
||||||
|
|
||||||
// load options
|
// load options
|
||||||
_Options();
|
_options();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
commandLine = parser.parse(allOptions, args, false);
|
commandLine = parser.parse(allOptions, args, false);
|
||||||
@ -167,6 +167,34 @@ public class Main {
|
|||||||
if (cli.hasOption("m") || cli.hasOption("match-original")) {
|
if (cli.hasOption("m") || cli.hasOption("match-original")) {
|
||||||
config.analysisMode = true;
|
config.analysisMode = true;
|
||||||
}
|
}
|
||||||
|
if (cli.hasOption("resm") || cli.hasOption("res-mode") || cli.hasOption("resolve-resources-mode")) {
|
||||||
|
String mode = cli.getOptionValue("resm");
|
||||||
|
if (mode == null) {
|
||||||
|
mode = cli.getOptionValue("res-mode");
|
||||||
|
}
|
||||||
|
if (mode == null) {
|
||||||
|
mode = cli.getOptionValue("resolve-resources-mode");
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (mode) {
|
||||||
|
case "remove":
|
||||||
|
case "delete":
|
||||||
|
config.setDecodeResolveMode(Config.DECODE_RES_RESOLVE_REMOVE);
|
||||||
|
break;
|
||||||
|
case "dummy":
|
||||||
|
case "dummies":
|
||||||
|
config.setDecodeResolveMode(Config.DECODE_RES_RESOLVE_DUMMY);
|
||||||
|
break;
|
||||||
|
case "keep":
|
||||||
|
case "preserve":
|
||||||
|
config.setDecodeResolveMode(Config.DECODE_RES_RESOLVE_RETAIN);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
System.err.println("Unknown resolve resources mode: " + mode);
|
||||||
|
System.err.println("Expect: 'remove', 'dummy' or 'keep'.");
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
File outDir;
|
File outDir;
|
||||||
if (cli.hasOption("o") || cli.hasOption("output")) {
|
if (cli.hasOption("o") || cli.hasOption("output")) {
|
||||||
@ -239,11 +267,13 @@ public class Main {
|
|||||||
if (cli.hasOption("nc") || cli.hasOption("no-crunch")) {
|
if (cli.hasOption("nc") || cli.hasOption("no-crunch")) {
|
||||||
config.noCrunch = true;
|
config.noCrunch = true;
|
||||||
}
|
}
|
||||||
|
if (cli.hasOption("use-aapt1")) {
|
||||||
|
config.useAapt2 = false;
|
||||||
|
}
|
||||||
|
|
||||||
// Temporary flag to enable the use of aapt2. This will transform in time to a use-aapt1 flag, which will be
|
if (cli.hasOption("use-aapt1") && cli.hasOption("use-aapt2")) {
|
||||||
// legacy and eventually removed.
|
System.err.println("You can only use one of --use-aapt1 or --use-aapt2.");
|
||||||
if (cli.hasOption("use-aapt2")) {
|
System.exit(1);
|
||||||
config.useAapt2 = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
File outFile;
|
File outFile;
|
||||||
@ -300,9 +330,7 @@ public class Main {
|
|||||||
System.out.println(ApktoolProperties.getVersion());
|
System.out.println(ApktoolProperties.getVersion());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void _Options() {
|
private static void _options() {
|
||||||
|
|
||||||
// create options
|
|
||||||
Option versionOption = Option.builder("version")
|
Option versionOption = Option.builder("version")
|
||||||
.longOpt("version")
|
.longOpt("version")
|
||||||
.desc("Print the version.")
|
.desc("Print the version.")
|
||||||
@ -409,6 +437,13 @@ public class Main {
|
|||||||
.desc("Skip changes detection and build all files.")
|
.desc("Skip changes detection and build all files.")
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
Option resolveResModeOption = Option.builder("resm")
|
||||||
|
.longOpt("resource-mode")
|
||||||
|
.desc("Sets the resolve resources mode. Possible values are: 'remove' (default), 'dummy' or 'keep'.")
|
||||||
|
.hasArg(true)
|
||||||
|
.argName("mode")
|
||||||
|
.build();
|
||||||
|
|
||||||
Option aaptOption = Option.builder("a")
|
Option aaptOption = Option.builder("a")
|
||||||
.longOpt("aapt")
|
.longOpt("aapt")
|
||||||
.hasArg(true)
|
.hasArg(true)
|
||||||
@ -416,9 +451,14 @@ public class Main {
|
|||||||
.desc("Load aapt from specified location.")
|
.desc("Load aapt from specified location.")
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
Option aapt1Option = Option.builder()
|
||||||
|
.longOpt("use-aapt1")
|
||||||
|
.desc("Use aapt binary instead of aapt2 during the build step.")
|
||||||
|
.build();
|
||||||
|
|
||||||
Option aapt2Option = Option.builder()
|
Option aapt2Option = Option.builder()
|
||||||
.longOpt("use-aapt2")
|
.longOpt("use-aapt2")
|
||||||
.desc("Use aapt2 binary instead of aapt1 during the build step.")
|
.desc("Use aapt2 binary instead of aapt during the build step.")
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
Option originalOption = Option.builder("c")
|
Option originalOption = Option.builder("c")
|
||||||
@ -469,13 +509,14 @@ public class Main {
|
|||||||
decodeOptions.addOption(apiLevelOption);
|
decodeOptions.addOption(apiLevelOption);
|
||||||
decodeOptions.addOption(noAssetOption);
|
decodeOptions.addOption(noAssetOption);
|
||||||
decodeOptions.addOption(forceManOption);
|
decodeOptions.addOption(forceManOption);
|
||||||
|
decodeOptions.addOption(resolveResModeOption);
|
||||||
|
|
||||||
buildOptions.addOption(apiLevelOption);
|
buildOptions.addOption(apiLevelOption);
|
||||||
buildOptions.addOption(debugBuiOption);
|
buildOptions.addOption(debugBuiOption);
|
||||||
buildOptions.addOption(netSecConfOption);
|
buildOptions.addOption(netSecConfOption);
|
||||||
buildOptions.addOption(aaptOption);
|
buildOptions.addOption(aaptOption);
|
||||||
buildOptions.addOption(originalOption);
|
buildOptions.addOption(originalOption);
|
||||||
buildOptions.addOption(aapt2Option);
|
buildOptions.addOption(aapt1Option);
|
||||||
buildOptions.addOption(noCrunchOption);
|
buildOptions.addOption(noCrunchOption);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -525,6 +566,7 @@ public class Main {
|
|||||||
allOptions.addOption(debugDecOption);
|
allOptions.addOption(debugDecOption);
|
||||||
allOptions.addOption(noDbgOption);
|
allOptions.addOption(noDbgOption);
|
||||||
allOptions.addOption(forceManOption);
|
allOptions.addOption(forceManOption);
|
||||||
|
allOptions.addOption(resolveResModeOption);
|
||||||
allOptions.addOption(noAssetOption);
|
allOptions.addOption(noAssetOption);
|
||||||
allOptions.addOption(keepResOption);
|
allOptions.addOption(keepResOption);
|
||||||
allOptions.addOption(debugBuiOption);
|
allOptions.addOption(debugBuiOption);
|
||||||
@ -533,6 +575,7 @@ public class Main {
|
|||||||
allOptions.addOption(originalOption);
|
allOptions.addOption(originalOption);
|
||||||
allOptions.addOption(verboseOption);
|
allOptions.addOption(verboseOption);
|
||||||
allOptions.addOption(quietOption);
|
allOptions.addOption(quietOption);
|
||||||
|
allOptions.addOption(aapt1Option);
|
||||||
allOptions.addOption(aapt2Option);
|
allOptions.addOption(aapt2Option);
|
||||||
allOptions.addOption(noCrunchOption);
|
allOptions.addOption(noCrunchOption);
|
||||||
allOptions.addOption(onlyMainClassesOption);
|
allOptions.addOption(onlyMainClassesOption);
|
||||||
@ -547,7 +590,7 @@ public class Main {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void usage() {
|
private static void usage() {
|
||||||
_Options();
|
_options();
|
||||||
HelpFormatter formatter = new HelpFormatter();
|
HelpFormatter formatter = new HelpFormatter();
|
||||||
formatter.setWidth(120);
|
formatter.setWidth(120);
|
||||||
|
|
||||||
@ -596,7 +639,14 @@ public class Main {
|
|||||||
@Override
|
@Override
|
||||||
public void publish(LogRecord record) {
|
public void publish(LogRecord record) {
|
||||||
if (getFormatter() == null) {
|
if (getFormatter() == null) {
|
||||||
setFormatter(new SimpleFormatter());
|
setFormatter(new Formatter() {
|
||||||
|
@Override
|
||||||
|
public String format(LogRecord record) {
|
||||||
|
return record.getLevel().toString().charAt(0) + ": "
|
||||||
|
+ record.getMessage()
|
||||||
|
+ System.getProperty("line.separator");
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -616,6 +666,7 @@ public class Main {
|
|||||||
reportError(null, exception, ErrorManager.FORMAT_FAILURE);
|
reportError(null, exception, ErrorManager.FORMAT_FAILURE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() throws SecurityException {}
|
public void close() throws SecurityException {}
|
||||||
@Override
|
@Override
|
||||||
@ -627,15 +678,6 @@ public class Main {
|
|||||||
if (verbosity == Verbosity.VERBOSE) {
|
if (verbosity == Verbosity.VERBOSE) {
|
||||||
handler.setLevel(Level.ALL);
|
handler.setLevel(Level.ALL);
|
||||||
logger.setLevel(Level.ALL);
|
logger.setLevel(Level.ALL);
|
||||||
} else {
|
|
||||||
handler.setFormatter(new Formatter() {
|
|
||||||
@Override
|
|
||||||
public String format(LogRecord record) {
|
|
||||||
return record.getLevel().toString().charAt(0) + ": "
|
|
||||||
+ record.getMessage()
|
|
||||||
+ System.getProperty("line.separator");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,13 +1,3 @@
|
|||||||
val baksmaliVersion: String by rootProject.extra
|
|
||||||
val smaliVersion: String by rootProject.extra
|
|
||||||
val xmlpullVersion: String by rootProject.extra
|
|
||||||
val guavaVersion: String by rootProject.extra
|
|
||||||
val commonsLangVersion: String by rootProject.extra
|
|
||||||
val commonsIoVersion: String by rootProject.extra
|
|
||||||
val commonsTextVersion: String by rootProject.extra
|
|
||||||
val junitVersion: String by rootProject.extra
|
|
||||||
val xmlunitVersion: String by rootProject.extra
|
|
||||||
|
|
||||||
val gitRevision: String by rootProject.extra
|
val gitRevision: String by rootProject.extra
|
||||||
val apktoolVersion: String by rootProject.extra
|
val apktoolVersion: String by rootProject.extra
|
||||||
|
|
||||||
@ -53,16 +43,16 @@ dependencies {
|
|||||||
api(project(":brut.j.util"))
|
api(project(":brut.j.util"))
|
||||||
api(project(":brut.j.common"))
|
api(project(":brut.j.common"))
|
||||||
|
|
||||||
implementation("com.android.tools.smali:smali-baksmali:$baksmaliVersion")
|
implementation(libs.baksmali)
|
||||||
implementation("com.android.tools.smali:smali:$smaliVersion")
|
implementation(libs.smali)
|
||||||
implementation("xpp3:xpp3:$xmlpullVersion")
|
implementation(libs.xmlpull)
|
||||||
implementation("com.google.guava:guava:$guavaVersion")
|
implementation(libs.guava)
|
||||||
implementation("org.apache.commons:commons-lang3:$commonsLangVersion")
|
implementation(libs.commons.lang3)
|
||||||
implementation("commons-io:commons-io:$commonsIoVersion")
|
implementation(libs.commons.io)
|
||||||
implementation("org.apache.commons:commons-text:$commonsTextVersion")
|
implementation(libs.commons.text)
|
||||||
|
|
||||||
testImplementation("junit:junit:$junitVersion")
|
testImplementation(libs.junit)
|
||||||
testImplementation("org.xmlunit:xmlunit-legacy:$xmlunitVersion")
|
testImplementation(libs.xmlunit)
|
||||||
|
|
||||||
compileOnly(files(androidJarPath))
|
compileOnly(files(androidJarPath))
|
||||||
}
|
}
|
||||||
|
@ -92,6 +92,7 @@ public class ApkDecoder {
|
|||||||
} catch (BrutException ex) {
|
} catch (BrutException ex) {
|
||||||
throw new AndrolibException(ex);
|
throw new AndrolibException(ex);
|
||||||
}
|
}
|
||||||
|
//noinspection ResultOfMethodCallIgnored
|
||||||
outDir.mkdirs();
|
outDir.mkdirs();
|
||||||
|
|
||||||
LOGGER.info("Using Apktool " + ApktoolProperties.getVersion() + " on " + mApkInfo.apkFileName);
|
LOGGER.info("Using Apktool " + ApktoolProperties.getVersion() + " on " + mApkInfo.apkFileName);
|
||||||
|
@ -23,6 +23,7 @@ import java.io.File;
|
|||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
public class Config {
|
public class Config {
|
||||||
|
private static Config instance = null;
|
||||||
private final static Logger LOGGER = Logger.getLogger(Config.class.getName());
|
private final static Logger LOGGER = Logger.getLogger(Config.class.getName());
|
||||||
|
|
||||||
public final static short DECODE_SOURCES_NONE = 0x0000;
|
public final static short DECODE_SOURCES_NONE = 0x0000;
|
||||||
@ -38,6 +39,10 @@ public class Config {
|
|||||||
public final static short DECODE_ASSETS_NONE = 0x0000;
|
public final static short DECODE_ASSETS_NONE = 0x0000;
|
||||||
public final static short DECODE_ASSETS_FULL = 0x0001;
|
public final static short DECODE_ASSETS_FULL = 0x0001;
|
||||||
|
|
||||||
|
public final static short DECODE_RES_RESOLVE_REMOVE = 0x0000;
|
||||||
|
public final static short DECODE_RES_RESOLVE_DUMMY = 0x0001;
|
||||||
|
public final static short DECODE_RES_RESOLVE_RETAIN = 0x0002;
|
||||||
|
|
||||||
// Build options
|
// Build options
|
||||||
public boolean forceBuildAll = false;
|
public boolean forceBuildAll = false;
|
||||||
public boolean forceDeleteFramework = false;
|
public boolean forceDeleteFramework = false;
|
||||||
@ -46,7 +51,7 @@ public class Config {
|
|||||||
public boolean verbose = false;
|
public boolean verbose = false;
|
||||||
public boolean copyOriginalFiles = false;
|
public boolean copyOriginalFiles = false;
|
||||||
public boolean updateFiles = false;
|
public boolean updateFiles = false;
|
||||||
public boolean useAapt2 = false;
|
public boolean useAapt2 = true;
|
||||||
public boolean noCrunch = false;
|
public boolean noCrunch = false;
|
||||||
public int forceApi = 0;
|
public int forceApi = 0;
|
||||||
|
|
||||||
@ -55,6 +60,7 @@ public class Config {
|
|||||||
public short decodeResources = DECODE_RESOURCES_FULL;
|
public short decodeResources = DECODE_RESOURCES_FULL;
|
||||||
public short forceDecodeManifest = FORCE_DECODE_MANIFEST_NONE;
|
public short forceDecodeManifest = FORCE_DECODE_MANIFEST_NONE;
|
||||||
public short decodeAssets = DECODE_ASSETS_FULL;
|
public short decodeAssets = DECODE_ASSETS_FULL;
|
||||||
|
public short decodeResolveMode = DECODE_RES_RESOLVE_REMOVE;
|
||||||
public int apiLevel = 0;
|
public int apiLevel = 0;
|
||||||
public boolean analysisMode = false;
|
public boolean analysisMode = false;
|
||||||
public boolean forceDelete = false;
|
public boolean forceDelete = false;
|
||||||
@ -72,8 +78,23 @@ public class Config {
|
|||||||
return this.useAapt2 || this.aaptVersion == 2;
|
return this.useAapt2 || this.aaptVersion == 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Config() {
|
public boolean isDecodeResolveModeUsingDummies() {
|
||||||
|
return decodeResolveMode == DECODE_RES_RESOLVE_DUMMY;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDecodeResolveModeRemoving() {
|
||||||
|
return decodeResolveMode == DECODE_RES_RESOLVE_REMOVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Config() {
|
||||||
|
instance = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Config getInstance() {
|
||||||
|
if (instance == null) {
|
||||||
|
instance = new Config();
|
||||||
|
}
|
||||||
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setDefaultFrameworkDirectory() {
|
private void setDefaultFrameworkDirectory() {
|
||||||
@ -105,6 +126,13 @@ public class Config {
|
|||||||
decodeSources = mode;
|
decodeSources = mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setDecodeResolveMode(short mode) throws AndrolibException {
|
||||||
|
if (mode != DECODE_RES_RESOLVE_REMOVE && mode != DECODE_RES_RESOLVE_DUMMY && mode != DECODE_RES_RESOLVE_RETAIN) {
|
||||||
|
throw new AndrolibException("Invalid decode resources mode");
|
||||||
|
}
|
||||||
|
decodeResolveMode = mode;
|
||||||
|
}
|
||||||
|
|
||||||
public void setDecodeResources(short mode) throws AndrolibException {
|
public void setDecodeResources(short mode) throws AndrolibException {
|
||||||
if (mode != DECODE_RESOURCES_NONE && mode != DECODE_RESOURCES_FULL) {
|
if (mode != DECODE_RESOURCES_NONE && mode != DECODE_RESOURCES_FULL) {
|
||||||
throw new AndrolibException("Invalid decode resources mode");
|
throw new AndrolibException("Invalid decode resources mode");
|
||||||
|
@ -191,6 +191,7 @@ public class ApkInfo implements YamlSerializable {
|
|||||||
return ResConfigFlags.SDK_TIRAMISU;
|
return ResConfigFlags.SDK_TIRAMISU;
|
||||||
case "UPSIDEDOWNCAKE":
|
case "UPSIDEDOWNCAKE":
|
||||||
case "UPSIDE_DOWN_CAKE":
|
case "UPSIDE_DOWN_CAKE":
|
||||||
|
return ResConfigFlags.SDK_UPSIDEDOWN_CAKE;
|
||||||
case "VANILLAICECREAM":
|
case "VANILLAICECREAM":
|
||||||
case "VANILLA_ICE_CREAM":
|
case "VANILLA_ICE_CREAM":
|
||||||
return ResConfigFlags.SDK_DEVELOPMENT;
|
return ResConfigFlags.SDK_DEVELOPMENT;
|
||||||
|
@ -32,6 +32,7 @@ public class ResConfigFlags {
|
|||||||
public final byte keyboard;
|
public final byte keyboard;
|
||||||
public final byte navigation;
|
public final byte navigation;
|
||||||
public final byte inputFlags;
|
public final byte inputFlags;
|
||||||
|
public final byte grammaticalInflection;
|
||||||
|
|
||||||
public final short screenWidth;
|
public final short screenWidth;
|
||||||
public final short screenHeight;
|
public final short screenHeight;
|
||||||
@ -70,6 +71,7 @@ public class ResConfigFlags {
|
|||||||
keyboard = KEYBOARD_ANY;
|
keyboard = KEYBOARD_ANY;
|
||||||
navigation = NAVIGATION_ANY;
|
navigation = NAVIGATION_ANY;
|
||||||
inputFlags = KEYSHIDDEN_ANY | NAVHIDDEN_ANY;
|
inputFlags = KEYSHIDDEN_ANY | NAVHIDDEN_ANY;
|
||||||
|
grammaticalInflection = GRAMMATICAL_GENDER_ANY;
|
||||||
screenWidth = 0;
|
screenWidth = 0;
|
||||||
screenHeight = 0;
|
screenHeight = 0;
|
||||||
sdkVersion = 0;
|
sdkVersion = 0;
|
||||||
@ -91,7 +93,7 @@ public class ResConfigFlags {
|
|||||||
public ResConfigFlags(short mcc, short mnc, char[] language,
|
public ResConfigFlags(short mcc, short mnc, char[] language,
|
||||||
char[] region, byte orientation,
|
char[] region, byte orientation,
|
||||||
byte touchscreen, int density, byte keyboard, byte navigation,
|
byte touchscreen, int density, byte keyboard, byte navigation,
|
||||||
byte inputFlags, short screenWidth, short screenHeight,
|
byte inputFlags, byte grammaticalInflection, short screenWidth, short screenHeight,
|
||||||
short sdkVersion, byte screenLayout, byte uiMode,
|
short sdkVersion, byte screenLayout, byte uiMode,
|
||||||
short smallestScreenWidthDp, short screenWidthDp,
|
short smallestScreenWidthDp, short screenWidthDp,
|
||||||
short screenHeightDp, char[] localeScript, char[] localeVariant,
|
short screenHeightDp, char[] localeScript, char[] localeVariant,
|
||||||
@ -149,6 +151,7 @@ public class ResConfigFlags {
|
|||||||
this.keyboard = keyboard;
|
this.keyboard = keyboard;
|
||||||
this.navigation = navigation;
|
this.navigation = navigation;
|
||||||
this.inputFlags = inputFlags;
|
this.inputFlags = inputFlags;
|
||||||
|
this.grammaticalInflection = grammaticalInflection;
|
||||||
this.screenWidth = screenWidth;
|
this.screenWidth = screenWidth;
|
||||||
this.screenHeight = screenHeight;
|
this.screenHeight = screenHeight;
|
||||||
this.sdkVersion = sdkVersion;
|
this.sdkVersion = sdkVersion;
|
||||||
@ -198,6 +201,18 @@ public class ResConfigFlags {
|
|||||||
}
|
}
|
||||||
ret.append(getLocaleString());
|
ret.append(getLocaleString());
|
||||||
|
|
||||||
|
switch (grammaticalInflection) {
|
||||||
|
case GRAMMATICAL_GENDER_NEUTER:
|
||||||
|
ret.append("-neuter");
|
||||||
|
break;
|
||||||
|
case GRAMMATICAL_GENDER_FEMININE:
|
||||||
|
ret.append("-feminine");
|
||||||
|
break;
|
||||||
|
case GRAMMATICAL_GENDER_MASCULINE:
|
||||||
|
ret.append("-masculine");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
switch (screenLayout & MASK_LAYOUTDIR) {
|
switch (screenLayout & MASK_LAYOUTDIR) {
|
||||||
case SCREENLAYOUT_LAYOUTDIR_RTL:
|
case SCREENLAYOUT_LAYOUTDIR_RTL:
|
||||||
ret.append("-ldrtl");
|
ret.append("-ldrtl");
|
||||||
@ -421,6 +436,9 @@ public class ResConfigFlags {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private short getNaturalSdkVersionRequirement() {
|
private short getNaturalSdkVersionRequirement() {
|
||||||
|
if (grammaticalInflection != 0) {
|
||||||
|
return SDK_UPSIDEDOWN_CAKE;
|
||||||
|
}
|
||||||
if ((uiMode & MASK_UI_MODE_TYPE) == UI_MODE_TYPE_VR_HEADSET || (colorMode & COLOR_WIDE_MASK) != 0 || ((colorMode & COLOR_HDR_MASK) != 0)) {
|
if ((uiMode & MASK_UI_MODE_TYPE) == UI_MODE_TYPE_VR_HEADSET || (colorMode & COLOR_WIDE_MASK) != 0 || ((colorMode & COLOR_HDR_MASK) != 0)) {
|
||||||
return SDK_OREO;
|
return SDK_OREO;
|
||||||
}
|
}
|
||||||
@ -550,6 +568,7 @@ public class ResConfigFlags {
|
|||||||
public final static byte SDK_S = 31;
|
public final static byte SDK_S = 31;
|
||||||
public final static byte SDK_S_V2 = 32;
|
public final static byte SDK_S_V2 = 32;
|
||||||
public final static byte SDK_TIRAMISU = 33;
|
public final static byte SDK_TIRAMISU = 33;
|
||||||
|
public final static byte SDK_UPSIDEDOWN_CAKE = 34;
|
||||||
|
|
||||||
// AOSP has this as 10,000 for dev purposes.
|
// AOSP has this as 10,000 for dev purposes.
|
||||||
// platform_frameworks_base/commit/c7a1109a1fe0771d4c9b572dcf178e2779fc4f2d
|
// platform_frameworks_base/commit/c7a1109a1fe0771d4c9b572dcf178e2779fc4f2d
|
||||||
@ -590,6 +609,11 @@ public class ResConfigFlags {
|
|||||||
public final static short SCREENLAYOUT_ROUND_NO = 0x1;
|
public final static short SCREENLAYOUT_ROUND_NO = 0x1;
|
||||||
public final static short SCREENLAYOUT_ROUND_YES = 0x2;
|
public final static short SCREENLAYOUT_ROUND_YES = 0x2;
|
||||||
|
|
||||||
|
public final static byte GRAMMATICAL_GENDER_ANY = 0;
|
||||||
|
public final static byte GRAMMATICAL_GENDER_NEUTER = 1;
|
||||||
|
public final static byte GRAMMATICAL_GENDER_FEMININE = 2;
|
||||||
|
public final static byte GRAMMATICAL_GENDER_MASCULINE = 3;
|
||||||
|
|
||||||
public final static byte KEYBOARD_ANY = 0;
|
public final static byte KEYBOARD_ANY = 0;
|
||||||
public final static byte KEYBOARD_NOKEYS = 1;
|
public final static byte KEYBOARD_NOKEYS = 1;
|
||||||
public final static byte KEYBOARD_QWERTY = 2;
|
public final static byte KEYBOARD_QWERTY = 2;
|
||||||
|
@ -105,6 +105,10 @@ public class ResResSpec {
|
|||||||
return mType;
|
return mType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isDummyResSpec() {
|
||||||
|
return getName().startsWith("APKTOOL_DUMMY_");
|
||||||
|
}
|
||||||
|
|
||||||
public void addResource(ResResource res) throws AndrolibException {
|
public void addResource(ResResource res) throws AndrolibException {
|
||||||
addResource(res, false);
|
addResource(res, false);
|
||||||
}
|
}
|
||||||
|
@ -70,6 +70,10 @@ public class ResTable {
|
|||||||
return mConfig.analysisMode;
|
return mConfig.analysisMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Config getConfig() {
|
||||||
|
return mConfig;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isMainPkgLoaded() {
|
public boolean isMainPkgLoaded() {
|
||||||
return mMainPkgLoaded;
|
return mMainPkgLoaded;
|
||||||
}
|
}
|
||||||
|
@ -23,9 +23,11 @@ import java.util.*;
|
|||||||
public final class ResTypeSpec {
|
public final class ResTypeSpec {
|
||||||
|
|
||||||
public static final String RES_TYPE_NAME_ARRAY = "array";
|
public static final String RES_TYPE_NAME_ARRAY = "array";
|
||||||
public static final String RES_TYPE_NAME_PLURALS = "plurals";
|
|
||||||
public static final String RES_TYPE_NAME_STYLES = "style";
|
|
||||||
public static final String RES_TYPE_NAME_ATTR = "attr";
|
public static final String RES_TYPE_NAME_ATTR = "attr";
|
||||||
|
public static final String RES_TYPE_NAME_ATTR_PRIVATE = "^attr-private";
|
||||||
|
public static final String RES_TYPE_NAME_PLURALS = "plurals";
|
||||||
|
public static final String RES_TYPE_NAME_STRING = "string";
|
||||||
|
public static final String RES_TYPE_NAME_STYLES = "style";
|
||||||
|
|
||||||
private final String mName;
|
private final String mName;
|
||||||
private final Map<String, ResResSpec> mResSpecs = new LinkedHashMap<>();
|
private final Map<String, ResResSpec> mResSpecs = new LinkedHashMap<>();
|
||||||
@ -46,7 +48,7 @@ public final class ResTypeSpec {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean isString() {
|
public boolean isString() {
|
||||||
return mName.equalsIgnoreCase("string");
|
return mName.equalsIgnoreCase(RES_TYPE_NAME_STRING);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResResSpec getResSpec(String name) throws AndrolibException {
|
public ResResSpec getResSpec(String name) throws AndrolibException {
|
||||||
|
@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2010 Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||||
|
* Copyright (C) 2010 Connor Tumbleson <connor.tumbleson@gmail.com>
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package brut.androlib.res.data.arsc;
|
||||||
|
|
||||||
|
import brut.androlib.exceptions.AndrolibException;
|
||||||
|
import brut.androlib.res.data.value.ResReferenceValue;
|
||||||
|
|
||||||
|
public class FlagItem {
|
||||||
|
public final ResReferenceValue ref;
|
||||||
|
public final int flag;
|
||||||
|
public String value;
|
||||||
|
|
||||||
|
public FlagItem(ResReferenceValue ref, int flag) {
|
||||||
|
this.ref = ref;
|
||||||
|
this.flag = flag;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getValue() throws AndrolibException {
|
||||||
|
if (value == null) {
|
||||||
|
if (ref.referentIsNull()) {
|
||||||
|
return String.format("APKTOOL_MISSING_0x%08x", ref.getRawIntValue());
|
||||||
|
}
|
||||||
|
value = ref.getReferent().getName();
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
@ -91,6 +91,4 @@ public class ResArrayValue extends ResBagValue implements ResValuesXmlSerializab
|
|||||||
|
|
||||||
private final ResScalarValue[] mItems;
|
private final ResScalarValue[] mItems;
|
||||||
private final String[] AllowedArrayTypes = {"string", "integer"};
|
private final String[] AllowedArrayTypes = {"string", "integer"};
|
||||||
|
|
||||||
public static final int BAG_KEY_ARRAY_START = 0x02000000;
|
|
||||||
}
|
}
|
||||||
|
@ -26,8 +26,7 @@ import org.xmlpull.v1.XmlSerializer;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
public class ResAttr extends ResBagValue implements ResValuesXmlSerializable {
|
public class ResAttr extends ResBagValue implements ResValuesXmlSerializable {
|
||||||
ResAttr(ResReferenceValue parentVal, int type, Integer min, Integer max,
|
ResAttr(ResReferenceValue parentVal, int type, Integer min, Integer max, Boolean l10n) {
|
||||||
Boolean l10n) {
|
|
||||||
super(parentVal);
|
super(parentVal);
|
||||||
mType = type;
|
mType = type;
|
||||||
mMin = min;
|
mMin = min;
|
||||||
@ -64,38 +63,39 @@ public class ResAttr extends ResBagValue implements ResValuesXmlSerializable {
|
|||||||
public static ResAttr factory(ResReferenceValue parent,
|
public static ResAttr factory(ResReferenceValue parent,
|
||||||
Duo<Integer, ResScalarValue>[] items, ResValueFactory factory,
|
Duo<Integer, ResScalarValue>[] items, ResValueFactory factory,
|
||||||
ResPackage pkg) throws AndrolibException {
|
ResPackage pkg) throws AndrolibException {
|
||||||
|
|
||||||
int type = ((ResIntValue) items[0].m2).getValue();
|
|
||||||
int scalarType = type & 0xffff;
|
|
||||||
Integer min = null, max = null;
|
Integer min = null, max = null;
|
||||||
Boolean l10n = null;
|
Boolean l10n = null;
|
||||||
int i;
|
int i;
|
||||||
for (i = 1; i < items.length; i++) {
|
for (i = 1; i < items.length; i++) {
|
||||||
switch (items[i].m1) {
|
switch (items[i].m1) {
|
||||||
case BAG_KEY_ATTR_MIN:
|
case BAG_KEY_ATTR_MIN:
|
||||||
min = ((ResIntValue) items[i].m2).getValue();
|
min = (items[i].m2).getRawIntValue();
|
||||||
continue;
|
continue;
|
||||||
case BAG_KEY_ATTR_MAX:
|
case BAG_KEY_ATTR_MAX:
|
||||||
max = ((ResIntValue) items[i].m2).getValue();
|
max = (items[i].m2).getRawIntValue();
|
||||||
continue;
|
continue;
|
||||||
case BAG_KEY_ATTR_L10N:
|
case BAG_KEY_ATTR_L10N:
|
||||||
l10n = ((ResIntValue) items[i].m2).getValue() != 0;
|
l10n = (items[i].m2).getRawIntValue() != 0;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// #2806 - Make sure we handle int-based values and not just ResIntValue
|
||||||
|
int rawValue = items[0].m2.getRawIntValue();
|
||||||
|
int scalarType = rawValue & 0xffff;
|
||||||
|
|
||||||
if (i == items.length) {
|
if (i == items.length) {
|
||||||
return new ResAttr(parent, scalarType, min, max, l10n);
|
return new ResAttr(parent, scalarType, min, max, l10n);
|
||||||
}
|
}
|
||||||
Duo<ResReferenceValue, ResIntValue>[] attrItems = new Duo[items.length - i];
|
Duo<ResReferenceValue, ResScalarValue>[] attrItems = new Duo[items.length - i];
|
||||||
int j = 0;
|
int j = 0;
|
||||||
for (; i < items.length; i++) {
|
for (; i < items.length; i++) {
|
||||||
int resId = items[i].m1;
|
int resId = items[i].m1;
|
||||||
pkg.addSynthesizedRes(resId);
|
pkg.addSynthesizedRes(resId);
|
||||||
attrItems[j++] = new Duo<>(factory.newReference(resId, null), (ResIntValue) items[i].m2);
|
attrItems[j++] = new Duo<>(factory.newReference(resId, null), items[i].m2);
|
||||||
}
|
}
|
||||||
switch (type & 0xff0000) {
|
switch (rawValue & 0xff0000) {
|
||||||
case TYPE_ENUM:
|
case TYPE_ENUM:
|
||||||
return new ResEnumAttr(parent, scalarType, min, max, l10n, attrItems);
|
return new ResEnumAttr(parent, scalarType, min, max, l10n, attrItems);
|
||||||
case TYPE_FLAGS:
|
case TYPE_FLAGS:
|
||||||
@ -145,7 +145,6 @@ public class ResAttr extends ResBagValue implements ResValuesXmlSerializable {
|
|||||||
private final Integer mMax;
|
private final Integer mMax;
|
||||||
private final Boolean mL10n;
|
private final Boolean mL10n;
|
||||||
|
|
||||||
public static final int BAG_KEY_ATTR_TYPE = 0x01000000;
|
|
||||||
private static final int BAG_KEY_ATTR_MIN = 0x01000001;
|
private static final int BAG_KEY_ATTR_MIN = 0x01000001;
|
||||||
private static final int BAG_KEY_ATTR_MAX = 0x01000002;
|
private static final int BAG_KEY_ATTR_MAX = 0x01000002;
|
||||||
private static final int BAG_KEY_ATTR_L10N = 0x01000003;
|
private static final int BAG_KEY_ATTR_L10N = 0x01000003;
|
||||||
|
@ -36,18 +36,15 @@ public class ResBagValue extends ResValue implements ResValuesXmlSerializable {
|
|||||||
ResResource res) throws IOException, AndrolibException {
|
ResResource res) throws IOException, AndrolibException {
|
||||||
String type = res.getResSpec().getType().getName();
|
String type = res.getResSpec().getType().getName();
|
||||||
if ("style".equals(type)) {
|
if ("style".equals(type)) {
|
||||||
new ResStyleValue(mParent, new Duo[0], null)
|
new ResStyleValue(mParent, new Duo[0], null).serializeToResValuesXml(serializer, res);
|
||||||
.serializeToResValuesXml(serializer, res);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ("array".equals(type)) {
|
if ("array".equals(type)) {
|
||||||
new ResArrayValue(mParent, new Duo[0]).serializeToResValuesXml(
|
new ResArrayValue(mParent, new Duo[0]).serializeToResValuesXml(serializer, res);
|
||||||
serializer, res);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ("plurals".equals(type)) {
|
if ("plurals".equals(type)) {
|
||||||
new ResPluralsValue(mParent, new Duo[0]).serializeToResValuesXml(
|
new ResPluralsValue(mParent, new Duo[0]).serializeToResValuesXml(serializer, res);
|
||||||
serializer, res);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,10 +25,11 @@ import org.xmlpull.v1.XmlSerializer;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
public class ResEnumAttr extends ResAttr {
|
public class ResEnumAttr extends ResAttr {
|
||||||
ResEnumAttr(ResReferenceValue parent, int type, Integer min, Integer max,
|
ResEnumAttr(ResReferenceValue parent, int type, Integer min, Integer max,
|
||||||
Boolean l10n, Duo<ResReferenceValue, ResIntValue>[] items) {
|
Boolean l10n, Duo<ResReferenceValue, ResScalarValue>[] items) {
|
||||||
super(parent, type, min, max, l10n);
|
super(parent, type, min, max, l10n);
|
||||||
mItems = items;
|
mItems = items;
|
||||||
}
|
}
|
||||||
@ -46,15 +47,21 @@ public class ResEnumAttr extends ResAttr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void serializeBody(XmlSerializer serializer, ResResource res)
|
protected void serializeBody(XmlSerializer serializer, ResResource res) throws AndrolibException, IOException {
|
||||||
throws AndrolibException, IOException {
|
for (Duo<ResReferenceValue, ResScalarValue> duo : mItems) {
|
||||||
for (Duo<ResReferenceValue, ResIntValue> duo : mItems) {
|
int intVal = duo.m2.getRawIntValue();
|
||||||
int intVal = duo.m2.getValue();
|
|
||||||
|
// #2836 - Support skipping items if the resource cannot be identified.
|
||||||
ResResSpec m1Referent = duo.m1.getReferent();
|
ResResSpec m1Referent = duo.m1.getReferent();
|
||||||
|
if (m1Referent == null && shouldRemoveUnknownRes()) {
|
||||||
|
LOGGER.fine(String.format("null enum reference: m1=0x%08x(%s), m2=0x%08x(%s)",
|
||||||
|
duo.m1.getRawIntValue(), duo.m1.getType(), duo.m2.getRawIntValue(), duo.m2.getType()));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
serializer.startTag(null, "enum");
|
serializer.startTag(null, "enum");
|
||||||
serializer.attribute(null, "name",
|
serializer.attribute(null, "name",
|
||||||
m1Referent != null ? m1Referent.getName() : "@null"
|
m1Referent != null ? m1Referent.getName() : String.format("APKTOOL_MISSING_0x%08x", duo.m1.getRawIntValue())
|
||||||
);
|
);
|
||||||
serializer.attribute(null, "value", String.valueOf(intVal));
|
serializer.attribute(null, "value", String.valueOf(intVal));
|
||||||
serializer.endTag(null, "enum");
|
serializer.endTag(null, "enum");
|
||||||
@ -65,8 +72,8 @@ public class ResEnumAttr extends ResAttr {
|
|||||||
String value2 = mItemsCache.get(value);
|
String value2 = mItemsCache.get(value);
|
||||||
if (value2 == null) {
|
if (value2 == null) {
|
||||||
ResReferenceValue ref = null;
|
ResReferenceValue ref = null;
|
||||||
for (Duo<ResReferenceValue, ResIntValue> duo : mItems) {
|
for (Duo<ResReferenceValue, ResScalarValue> duo : mItems) {
|
||||||
if (duo.m2.getValue() == value) {
|
if (duo.m2.getRawIntValue() == value) {
|
||||||
ref = duo.m1;
|
ref = duo.m1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -79,6 +86,8 @@ public class ResEnumAttr extends ResAttr {
|
|||||||
return value2;
|
return value2;
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Duo<ResReferenceValue, ResIntValue>[] mItems;
|
private final Duo<ResReferenceValue, ResScalarValue>[] mItems;
|
||||||
private final Map<Integer, String> mItemsCache = new HashMap<>();
|
private final Map<Integer, String> mItemsCache = new HashMap<>();
|
||||||
|
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(ResEnumAttr.class.getName());
|
||||||
}
|
}
|
||||||
|
@ -17,21 +17,24 @@
|
|||||||
package brut.androlib.res.data.value;
|
package brut.androlib.res.data.value;
|
||||||
|
|
||||||
import brut.androlib.exceptions.AndrolibException;
|
import brut.androlib.exceptions.AndrolibException;
|
||||||
|
import brut.androlib.res.data.ResResSpec;
|
||||||
import brut.androlib.res.data.ResResource;
|
import brut.androlib.res.data.ResResource;
|
||||||
|
import brut.androlib.res.data.arsc.FlagItem;
|
||||||
import brut.util.Duo;
|
import brut.util.Duo;
|
||||||
import org.xmlpull.v1.XmlSerializer;
|
import org.xmlpull.v1.XmlSerializer;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
public class ResFlagsAttr extends ResAttr {
|
public class ResFlagsAttr extends ResAttr {
|
||||||
ResFlagsAttr(ResReferenceValue parent, int type, Integer min, Integer max,
|
ResFlagsAttr(ResReferenceValue parent, int type, Integer min, Integer max,
|
||||||
Boolean l10n, Duo<ResReferenceValue, ResIntValue>[] items) {
|
Boolean l10n, Duo<ResReferenceValue, ResScalarValue>[] items) {
|
||||||
super(parent, type, min, max, l10n);
|
super(parent, type, min, max, l10n);
|
||||||
|
|
||||||
mItems = new FlagItem[items.length];
|
mItems = new FlagItem[items.length];
|
||||||
for (int i = 0; i < items.length; i++) {
|
for (int i = 0; i < items.length; i++) {
|
||||||
mItems[i] = new FlagItem(items[i].m1, items[i].m2.getValue());
|
mItems[i] = new FlagItem(items[i].m1, items[i].m2.getRawIntValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,13 +73,19 @@ public class ResFlagsAttr extends ResAttr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void serializeBody(XmlSerializer serializer, ResResource res)
|
protected void serializeBody(XmlSerializer serializer, ResResource res) throws AndrolibException, IOException {
|
||||||
throws AndrolibException, IOException {
|
|
||||||
for (FlagItem item : mItems) {
|
for (FlagItem item : mItems) {
|
||||||
|
ResResSpec referent = item.ref.getReferent();
|
||||||
|
|
||||||
|
// #2836 - Support skipping items if the resource cannot be identified.
|
||||||
|
if (referent == null && shouldRemoveUnknownRes()) {
|
||||||
|
LOGGER.fine(String.format("null flag reference: 0x%08x(%s)", item.ref.getValue(), item.ref.getType()));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
serializer.startTag(null, "flag");
|
serializer.startTag(null, "flag");
|
||||||
serializer.attribute(null, "name", item.getValue());
|
serializer.attribute(null, "name", item.getValue());
|
||||||
serializer.attribute(null, "value",
|
serializer.attribute(null, "value", String.format("0x%08x", item.flag));
|
||||||
String.format("0x%08x", item.flag));
|
|
||||||
serializer.endTag(null, "flag");
|
serializer.endTag(null, "flag");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -130,24 +139,5 @@ public class ResFlagsAttr extends ResAttr {
|
|||||||
private FlagItem[] mZeroFlags;
|
private FlagItem[] mZeroFlags;
|
||||||
private FlagItem[] mFlags;
|
private FlagItem[] mFlags;
|
||||||
|
|
||||||
private static class FlagItem {
|
private static final Logger LOGGER = Logger.getLogger(ResFlagsAttr.class.getName());
|
||||||
public final ResReferenceValue ref;
|
|
||||||
public final int flag;
|
|
||||||
public String value;
|
|
||||||
|
|
||||||
public FlagItem(ResReferenceValue ref, int flag) {
|
|
||||||
this.ref = ref;
|
|
||||||
this.flag = flag;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getValue() throws AndrolibException {
|
|
||||||
if (value == null) {
|
|
||||||
if (ref.referentIsNull()) {
|
|
||||||
return "@null";
|
|
||||||
}
|
|
||||||
value = ref.getReferent().getName();
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -25,10 +25,8 @@ import org.xmlpull.v1.XmlSerializer;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
public class ResPluralsValue extends ResBagValue implements
|
public class ResPluralsValue extends ResBagValue implements ResValuesXmlSerializable {
|
||||||
ResValuesXmlSerializable {
|
ResPluralsValue(ResReferenceValue parent, Duo<Integer, ResScalarValue>[] items) {
|
||||||
ResPluralsValue(ResReferenceValue parent,
|
|
||||||
Duo<Integer, ResScalarValue>[] items) {
|
|
||||||
super(parent);
|
super(parent);
|
||||||
|
|
||||||
mItems = new ResScalarValue[6];
|
mItems = new ResScalarValue[6];
|
||||||
@ -59,6 +57,5 @@ public class ResPluralsValue extends ResBagValue implements
|
|||||||
private final ResScalarValue[] mItems;
|
private final ResScalarValue[] mItems;
|
||||||
|
|
||||||
public static final int BAG_KEY_PLURALS_START = 0x01000004;
|
public static final int BAG_KEY_PLURALS_START = 0x01000004;
|
||||||
public static final int BAG_KEY_PLURALS_END = 0x01000009;
|
|
||||||
private static final String[] QUANTITY_MAP = new String[] { "other", "zero", "one", "two", "few", "many" };
|
private static final String[] QUANTITY_MAP = new String[] { "other", "zero", "one", "two", "few", "many" };
|
||||||
}
|
}
|
||||||
|
@ -81,6 +81,11 @@ public abstract class ResScalarValue extends ResIntBasedValue implements
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Dummy attributes should be <item> with type attribute
|
||||||
|
if (res.getResSpec().isDummyResSpec()) {
|
||||||
|
item = true;
|
||||||
|
}
|
||||||
|
|
||||||
// Android does not allow values (false) for ids.xml anymore
|
// Android does not allow values (false) for ids.xml anymore
|
||||||
// https://issuetracker.google.com/issues/80475496
|
// https://issuetracker.google.com/issues/80475496
|
||||||
// But it decodes as a ResBoolean, which makes no sense. So force it to empty
|
// But it decodes as a ResBoolean, which makes no sense. So force it to empty
|
||||||
|
@ -25,7 +25,6 @@ import java.io.IOException;
|
|||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
public class ResStringValue extends ResScalarValue {
|
public class ResStringValue extends ResScalarValue {
|
||||||
|
|
||||||
public ResStringValue(String value, int rawValue) {
|
public ResStringValue(String value, int rawValue) {
|
||||||
this(value, rawValue, "string");
|
this(value, rawValue, "string");
|
||||||
}
|
}
|
||||||
|
@ -26,10 +26,8 @@ import org.xmlpull.v1.XmlSerializer;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
public class ResStyleValue extends ResBagValue implements
|
public class ResStyleValue extends ResBagValue implements ResValuesXmlSerializable {
|
||||||
ResValuesXmlSerializable {
|
ResStyleValue(ResReferenceValue parent, Duo<Integer, ResScalarValue>[] items, ResValueFactory factory) {
|
||||||
ResStyleValue(ResReferenceValue parent,
|
|
||||||
Duo<Integer, ResScalarValue>[] items, ResValueFactory factory) {
|
|
||||||
super(parent);
|
super(parent);
|
||||||
|
|
||||||
mItems = new Duo[items.length];
|
mItems = new Duo[items.length];
|
||||||
@ -53,7 +51,7 @@ public class ResStyleValue extends ResBagValue implements
|
|||||||
ResResSpec spec = mItem.m1.getReferent();
|
ResResSpec spec = mItem.m1.getReferent();
|
||||||
|
|
||||||
if (spec == null) {
|
if (spec == null) {
|
||||||
LOGGER.fine(String.format("null reference: m1=0x%08x(%s), m2=0x%08x(%s)",
|
LOGGER.fine(String.format("null style reference: m1=0x%08x(%s), m2=0x%08x(%s)",
|
||||||
mItem.m1.getRawIntValue(), mItem.m1.getType(), mItem.m2.getRawIntValue(), mItem.m2.getType()));
|
mItem.m1.getRawIntValue(), mItem.m1.getType(), mItem.m2.getRawIntValue(), mItem.m2.getType()));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,10 @@
|
|||||||
*/
|
*/
|
||||||
package brut.androlib.res.data.value;
|
package brut.androlib.res.data.value;
|
||||||
|
|
||||||
public class ResValue {
|
import brut.androlib.Config;
|
||||||
|
|
||||||
|
public class ResValue {
|
||||||
|
public boolean shouldRemoveUnknownRes() {
|
||||||
|
return Config.getInstance().isDecodeResolveModeRemoving();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -80,40 +80,30 @@ public class ResValueFactory {
|
|||||||
return new ResStringValue(value, rawValue);
|
return new ResStringValue(value, rawValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResBagValue bagFactory(int parent, Duo<Integer, ResScalarValue>[] items, ResTypeSpec resTypeSpec) throws AndrolibException {
|
public ResBagValue bagFactory(int parent, Duo<Integer, ResScalarValue>[] items, ResTypeSpec resTypeSpec)
|
||||||
|
throws AndrolibException {
|
||||||
ResReferenceValue parentVal = newReference(parent, null);
|
ResReferenceValue parentVal = newReference(parent, null);
|
||||||
|
|
||||||
if (items.length == 0) {
|
if (items.length == 0) {
|
||||||
return new ResBagValue(parentVal);
|
return new ResBagValue(parentVal);
|
||||||
}
|
}
|
||||||
int key = items[0].m1;
|
|
||||||
if (key == ResAttr.BAG_KEY_ATTR_TYPE) {
|
|
||||||
return ResAttr.factory(parentVal, items, this, mPackage);
|
|
||||||
}
|
|
||||||
|
|
||||||
String resTypeName = resTypeSpec.getName();
|
String resTypeName = resTypeSpec.getName();
|
||||||
|
|
||||||
// Android O Preview added an unknown enum for c. This is hardcoded as 0 for now.
|
switch (resTypeName) {
|
||||||
if (ResTypeSpec.RES_TYPE_NAME_ARRAY.equals(resTypeName)
|
case ResTypeSpec.RES_TYPE_NAME_ATTR:
|
||||||
|| key == ResArrayValue.BAG_KEY_ARRAY_START || key == 0) {
|
case ResTypeSpec.RES_TYPE_NAME_ATTR_PRIVATE:
|
||||||
|
return ResAttr.factory(parentVal, items, this, mPackage);
|
||||||
|
case ResTypeSpec.RES_TYPE_NAME_ARRAY:
|
||||||
return new ResArrayValue(parentVal, items);
|
return new ResArrayValue(parentVal, items);
|
||||||
}
|
case ResTypeSpec.RES_TYPE_NAME_PLURALS:
|
||||||
|
|
||||||
if (ResTypeSpec.RES_TYPE_NAME_PLURALS.equals(resTypeName) ||
|
|
||||||
(key >= ResPluralsValue.BAG_KEY_PLURALS_START && key <= ResPluralsValue.BAG_KEY_PLURALS_END)) {
|
|
||||||
return new ResPluralsValue(parentVal, items);
|
return new ResPluralsValue(parentVal, items);
|
||||||
}
|
default:
|
||||||
|
|
||||||
if (ResTypeSpec.RES_TYPE_NAME_ATTR.equals(resTypeName)) {
|
|
||||||
return new ResAttr(parentVal, 0, null, null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (resTypeName.startsWith(ResTypeSpec.RES_TYPE_NAME_STYLES)) {
|
if (resTypeName.startsWith(ResTypeSpec.RES_TYPE_NAME_STYLES)) {
|
||||||
return new ResStyleValue(parentVal, items, this);
|
return new ResStyleValue(parentVal, items, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new AndrolibException("unsupported res type name for bags. Found: " + resTypeName);
|
throw new AndrolibException("unsupported res type name for bags. Found: " + resTypeName);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public ResReferenceValue newReference(int resID, String rawValue) {
|
public ResReferenceValue newReference(int resID, String rawValue) {
|
||||||
return newReference(resID, rawValue, false);
|
return newReference(resID, rawValue, false);
|
||||||
|
@ -59,11 +59,11 @@ public class ARSCDecoder {
|
|||||||
mIn = new ExtCountingDataInput(new LittleEndianDataInputStream(arscStream));
|
mIn = new ExtCountingDataInput(new LittleEndianDataInputStream(arscStream));
|
||||||
mResTable = resTable;
|
mResTable = resTable;
|
||||||
mKeepBroken = keepBroken;
|
mKeepBroken = keepBroken;
|
||||||
|
mMissingResSpecMap = new LinkedHashMap<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
private ResPackage[] readResourceTable() throws IOException, AndrolibException {
|
private ResPackage[] readResourceTable() throws IOException, AndrolibException {
|
||||||
Set<ResPackage> pkgs = new LinkedHashSet<>();
|
Set<ResPackage> pkgs = new LinkedHashSet<>();
|
||||||
|
|
||||||
ResTypeSpec typeSpec;
|
ResTypeSpec typeSpec;
|
||||||
int chunkNumber = 1;
|
int chunkNumber = 1;
|
||||||
|
|
||||||
@ -118,6 +118,10 @@ public class ARSCDecoder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mResTable.getConfig().isDecodeResolveModeUsingDummies() && mPkg != null && mPkg.getResSpecCount() > 0) {
|
||||||
|
addMissingResSpecs();
|
||||||
|
}
|
||||||
|
|
||||||
return pkgs.toArray(new ResPackage[0]);
|
return pkgs.toArray(new ResPackage[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -207,7 +211,7 @@ public class ARSCDecoder {
|
|||||||
mHeader.checkForUnreadHeader(mIn);
|
mHeader.checkForUnreadHeader(mIn);
|
||||||
|
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
LOGGER.fine(String.format("Skipping staged alias stagedId (%h) finalId: %h", mIn.readInt(), mIn.readInt()));
|
LOGGER.fine(String.format("Staged alias: 0x%08x -> 0x%08x", mIn.readInt(), mIn.readInt()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -256,10 +260,16 @@ public class ARSCDecoder {
|
|||||||
private ResType readTableType() throws IOException, AndrolibException {
|
private ResType readTableType() throws IOException, AndrolibException {
|
||||||
checkChunkType(ARSCHeader.XML_TYPE_TYPE);
|
checkChunkType(ARSCHeader.XML_TYPE_TYPE);
|
||||||
int typeId = mIn.readUnsignedByte() - mTypeIdOffset;
|
int typeId = mIn.readUnsignedByte() - mTypeIdOffset;
|
||||||
|
|
||||||
|
// #3311 - Some older applications have no TYPE_SPEC chunks, but still define TYPE chunks.
|
||||||
if (mResTypeSpecs.containsKey(typeId)) {
|
if (mResTypeSpecs.containsKey(typeId)) {
|
||||||
mResId = (0xff000000 & mResId) | mResTypeSpecs.get(typeId).getId() << 16;
|
|
||||||
mTypeSpec = mResTypeSpecs.get(typeId);
|
mTypeSpec = mResTypeSpecs.get(typeId);
|
||||||
|
} else {
|
||||||
|
mTypeSpec = new ResTypeSpec(mTypeNames.getString(typeId - 1), typeId);
|
||||||
|
addTypeSpec(mTypeSpec);
|
||||||
|
mPkg.addType(mTypeSpec);
|
||||||
}
|
}
|
||||||
|
mResId = (0xff000000 & mResId) | mTypeSpec.getId() << 16;
|
||||||
|
|
||||||
int typeFlags = mIn.readByte();
|
int typeFlags = mIn.readByte();
|
||||||
mIn.skipBytes(2); // reserved
|
mIn.skipBytes(2); // reserved
|
||||||
@ -270,16 +280,21 @@ public class ARSCDecoder {
|
|||||||
|
|
||||||
mHeader.checkForUnreadHeader(mIn);
|
mHeader.checkForUnreadHeader(mIn);
|
||||||
|
|
||||||
|
boolean isOffset16 = (typeFlags & TABLE_TYPE_FLAG_OFFSET16) != 0;
|
||||||
|
boolean isSparse = (typeFlags & TABLE_TYPE_FLAG_SPARSE) != 0;
|
||||||
|
|
||||||
// Be sure we don't poison mResTable by marking the application as sparse
|
// Be sure we don't poison mResTable by marking the application as sparse
|
||||||
// Only flag the ResTable as sparse if the main package is not loaded.
|
// Only flag the ResTable as sparse if the main package is not loaded.
|
||||||
if ((typeFlags & 0x01) != 0 && !mResTable.isMainPkgLoaded()) {
|
if (isSparse && !mResTable.isMainPkgLoaded()) {
|
||||||
mResTable.setSparseResources(true);
|
mResTable.setSparseResources(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
HashMap<Integer, Integer> entryOffsetMap = new LinkedHashMap<>();
|
HashMap<Integer, Integer> entryOffsetMap = new LinkedHashMap<>();
|
||||||
for (int i = 0; i < entryCount; i++) {
|
for (int i = 0; i < entryCount; i++) {
|
||||||
if ((typeFlags & 0x01) != 0) {
|
if (isSparse) {
|
||||||
entryOffsetMap.put(mIn.readUnsignedShort(), mIn.readUnsignedShort());
|
entryOffsetMap.put(mIn.readUnsignedShort(), mIn.readUnsignedShort());
|
||||||
|
} else if (isOffset16) {
|
||||||
|
entryOffsetMap.put(i, mIn.readUnsignedShort());
|
||||||
} else {
|
} else {
|
||||||
entryOffsetMap.put(i, mIn.readInt());
|
entryOffsetMap.put(i, mIn.readInt());
|
||||||
}
|
}
|
||||||
@ -295,11 +310,13 @@ public class ARSCDecoder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mType = flags.isInvalid && !mKeepBroken ? null : mPkg.getOrCreateConfig(flags);
|
mType = flags.isInvalid && !mKeepBroken ? null : mPkg.getOrCreateConfig(flags);
|
||||||
|
int noEntry = isOffset16 ? NO_ENTRY_OFFSET16 : NO_ENTRY;
|
||||||
|
|
||||||
for (int i : entryOffsetMap.keySet()) {
|
for (int i : entryOffsetMap.keySet()) {
|
||||||
mResId = (mResId & 0xffff0000) | i;
|
mResId = (mResId & 0xffff0000) | i;
|
||||||
int offset = entryOffsetMap.get(i);
|
int offset = entryOffsetMap.get(i);
|
||||||
if (offset == NO_ENTRY) {
|
if (offset == noEntry) {
|
||||||
|
mMissingResSpecMap.put(mResId, typeId);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -315,6 +332,8 @@ public class ARSCDecoder {
|
|||||||
EntryData entryData = readEntryData();
|
EntryData entryData = readEntryData();
|
||||||
if (entryData != null) {
|
if (entryData != null) {
|
||||||
readEntry(entryData);
|
readEntry(entryData);
|
||||||
|
} else {
|
||||||
|
mMissingResSpecMap.put(mResId, typeId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -329,17 +348,35 @@ public class ARSCDecoder {
|
|||||||
|
|
||||||
private EntryData readEntryData() throws IOException, AndrolibException {
|
private EntryData readEntryData() throws IOException, AndrolibException {
|
||||||
short size = mIn.readShort();
|
short size = mIn.readShort();
|
||||||
if (size < 0) {
|
short flags = mIn.readShort();
|
||||||
throw new AndrolibException("Entry size is under 0 bytes.");
|
|
||||||
|
boolean isComplex = (flags & ENTRY_FLAG_COMPLEX) != 0;
|
||||||
|
boolean isCompact = (flags & ENTRY_FLAG_COMPACT) != 0;
|
||||||
|
|
||||||
|
if (size < 0 && !isCompact) {
|
||||||
|
throw new AndrolibException("Entry size is under 0 bytes and not compactly packed.");
|
||||||
}
|
}
|
||||||
|
|
||||||
short flags = mIn.readShort();
|
|
||||||
int specNamesId = mIn.readInt();
|
int specNamesId = mIn.readInt();
|
||||||
if (specNamesId == NO_ENTRY) {
|
if (specNamesId == NO_ENTRY && !isCompact) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
ResValue value = (flags & ENTRY_FLAG_COMPLEX) == 0 ? readValue() : readComplexEntry();
|
// #3366 - In a compactly packed entry, the key index is the size & type is higher 8 bits on flags.
|
||||||
|
// We assume a size of 8 bytes for compact entries and the specNamesId is the data itself encoded.
|
||||||
|
ResValue value;
|
||||||
|
if (isCompact) {
|
||||||
|
byte type = (byte) ((flags >> 8) & 0xFF);
|
||||||
|
value = readCompactValue(type, specNamesId);
|
||||||
|
|
||||||
|
// To keep code below happy - we know if compact that the size has the key index encoded.
|
||||||
|
specNamesId = size;
|
||||||
|
} else if (isComplex) {
|
||||||
|
value = readComplexEntry();
|
||||||
|
} else {
|
||||||
|
value = readValue();
|
||||||
|
}
|
||||||
|
|
||||||
// #2824 - In some applications the res entries are duplicated with the 2nd being malformed.
|
// #2824 - In some applications the res entries are duplicated with the 2nd being malformed.
|
||||||
// AOSP skips this, so we will do the same.
|
// AOSP skips this, so we will do the same.
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
@ -402,6 +439,12 @@ public class ARSCDecoder {
|
|||||||
resId = mIn.readInt();
|
resId = mIn.readInt();
|
||||||
resValue = readValue();
|
resValue = readValue();
|
||||||
|
|
||||||
|
// #2824 - In some applications the res entries are duplicated with the 2nd being malformed.
|
||||||
|
// AOSP skips this, so we will do the same.
|
||||||
|
if (resValue == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (!(resValue instanceof ResScalarValue)) {
|
if (!(resValue instanceof ResScalarValue)) {
|
||||||
resValue = new ResStringValue(resValue.toString(), resValue.getRawIntValue());
|
resValue = new ResStringValue(resValue.toString(), resValue.getRawIntValue());
|
||||||
}
|
}
|
||||||
@ -411,6 +454,12 @@ public class ARSCDecoder {
|
|||||||
return factory.bagFactory(parent, items, mTypeSpec);
|
return factory.bagFactory(parent, items, mTypeSpec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ResIntBasedValue readCompactValue(byte type, int data) throws AndrolibException {
|
||||||
|
return type == TypedValue.TYPE_STRING
|
||||||
|
? mPkg.getValueFactory().factory(mTableStrings.getHTML(data), data)
|
||||||
|
: mPkg.getValueFactory().factory(type, data, null);
|
||||||
|
}
|
||||||
|
|
||||||
private ResIntBasedValue readValue() throws IOException, AndrolibException {
|
private ResIntBasedValue readValue() throws IOException, AndrolibException {
|
||||||
int size = mIn.readShort();
|
int size = mIn.readShort();
|
||||||
if (size < 8) {
|
if (size < 8) {
|
||||||
@ -464,11 +513,12 @@ public class ARSCDecoder {
|
|||||||
byte keyboard = 0;
|
byte keyboard = 0;
|
||||||
byte navigation = 0;
|
byte navigation = 0;
|
||||||
byte inputFlags = 0;
|
byte inputFlags = 0;
|
||||||
|
byte grammaticalInflection = 0;
|
||||||
if (size >= 20) {
|
if (size >= 20) {
|
||||||
keyboard = mIn.readByte();
|
keyboard = mIn.readByte();
|
||||||
navigation = mIn.readByte();
|
navigation = mIn.readByte();
|
||||||
inputFlags = mIn.readByte();
|
inputFlags = mIn.readByte();
|
||||||
mIn.skipBytes(1); // inputPad0
|
grammaticalInflection = mIn.readByte();
|
||||||
read = 20;
|
read = 20;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -526,6 +576,7 @@ public class ARSCDecoder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int exceedingKnownSize = size - KNOWN_CONFIG_BYTES;
|
int exceedingKnownSize = size - KNOWN_CONFIG_BYTES;
|
||||||
|
|
||||||
if (exceedingKnownSize > 0) {
|
if (exceedingKnownSize > 0) {
|
||||||
byte[] buf = new byte[exceedingKnownSize];
|
byte[] buf = new byte[exceedingKnownSize];
|
||||||
read += exceedingKnownSize;
|
read += exceedingKnownSize;
|
||||||
@ -550,7 +601,7 @@ public class ARSCDecoder {
|
|||||||
|
|
||||||
return new ResConfigFlags(mcc, mnc, language, country,
|
return new ResConfigFlags(mcc, mnc, language, country,
|
||||||
orientation, touchscreen, density, keyboard, navigation,
|
orientation, touchscreen, density, keyboard, navigation,
|
||||||
inputFlags, screenWidth, screenHeight, sdkVersion,
|
inputFlags, grammaticalInflection, screenWidth, screenHeight, sdkVersion,
|
||||||
screenLayout, uiMode, smallestScreenWidthDp, screenWidthDp,
|
screenLayout, uiMode, smallestScreenWidthDp, screenWidthDp,
|
||||||
screenHeightDp, localeScript, localeVariant, screenLayout2,
|
screenHeightDp, localeScript, localeVariant, screenLayout2,
|
||||||
colorMode, localeNumberingSystem, isInvalid, size);
|
colorMode, localeNumberingSystem, isInvalid, size);
|
||||||
@ -589,6 +640,30 @@ public class ARSCDecoder {
|
|||||||
mResTypeSpecs.put(resTypeSpec.getId(), resTypeSpec);
|
mResTypeSpecs.put(resTypeSpec.getId(), resTypeSpec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void addMissingResSpecs() throws AndrolibException {
|
||||||
|
for (int resId : mMissingResSpecMap.keySet()) {
|
||||||
|
int typeId = mMissingResSpecMap.get(resId);
|
||||||
|
String resName = "APKTOOL_DUMMY_" + Integer.toHexString(resId);
|
||||||
|
ResID id = new ResID(resId);
|
||||||
|
ResResSpec spec = new ResResSpec(id, resName, mPkg, mResTypeSpecs.get(typeId));
|
||||||
|
|
||||||
|
// If we already have this resID don't add it again.
|
||||||
|
if (! mPkg.hasResSpec(id)) {
|
||||||
|
mPkg.addResSpec(spec);
|
||||||
|
spec.getType().addResSpec(spec);
|
||||||
|
ResType resType = mPkg.getOrCreateConfig(new ResConfigFlags());
|
||||||
|
|
||||||
|
// We are going to make dummy attributes a null reference (@null) now instead of a boolean false.
|
||||||
|
// This is because aapt2 is stricter when it comes to what we can put in an application.
|
||||||
|
ResValue value = new ResReferenceValue(mPkg, 0, "");
|
||||||
|
|
||||||
|
ResResource res = new ResResource(resType, spec, value);
|
||||||
|
resType.addResource(res);
|
||||||
|
spec.addResource(res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private ARSCHeader nextChunk() throws IOException {
|
private ARSCHeader nextChunk() throws IOException {
|
||||||
return mHeader = ARSCHeader.read(mIn);
|
return mHeader = ARSCHeader.read(mIn);
|
||||||
}
|
}
|
||||||
@ -614,15 +689,21 @@ public class ARSCDecoder {
|
|||||||
private ResType mType;
|
private ResType mType;
|
||||||
private int mResId;
|
private int mResId;
|
||||||
private int mTypeIdOffset = 0;
|
private int mTypeIdOffset = 0;
|
||||||
|
private final HashMap<Integer, Integer> mMissingResSpecMap;
|
||||||
private final HashMap<Integer, ResTypeSpec> mResTypeSpecs = new HashMap<>();
|
private final HashMap<Integer, ResTypeSpec> mResTypeSpecs = new HashMap<>();
|
||||||
|
|
||||||
private final static short ENTRY_FLAG_COMPLEX = 0x0001;
|
private final static short ENTRY_FLAG_COMPLEX = 0x0001;
|
||||||
private final static short ENTRY_FLAG_PUBLIC = 0x0002;
|
private final static short ENTRY_FLAG_PUBLIC = 0x0002;
|
||||||
private final static short ENTRY_FLAG_WEAK = 0x0004;
|
private final static short ENTRY_FLAG_WEAK = 0x0004;
|
||||||
|
private final static short ENTRY_FLAG_COMPACT = 0x0008;
|
||||||
|
|
||||||
|
private final static short TABLE_TYPE_FLAG_SPARSE = 0x01;
|
||||||
|
private final static short TABLE_TYPE_FLAG_OFFSET16 = 0x02;
|
||||||
|
|
||||||
private static final int KNOWN_CONFIG_BYTES = 64;
|
private static final int KNOWN_CONFIG_BYTES = 64;
|
||||||
|
|
||||||
private static final int NO_ENTRY = 0xFFFFFFFF;
|
private static final int NO_ENTRY = 0xFFFFFFFF;
|
||||||
|
private static final int NO_ENTRY_OFFSET16 = 0xFFFF;
|
||||||
|
|
||||||
private static final Logger LOGGER = Logger.getLogger(ARSCDecoder.class.getName());
|
private static final Logger LOGGER = Logger.getLogger(ARSCDecoder.class.getName());
|
||||||
}
|
}
|
||||||
|
@ -365,7 +365,7 @@ public final class ResXmlPatcher {
|
|||||||
* @throws SAXException
|
* @throws SAXException
|
||||||
* @throws ParserConfigurationException
|
* @throws ParserConfigurationException
|
||||||
*/
|
*/
|
||||||
private static Document loadDocument(File file)
|
public static Document loadDocument(File file)
|
||||||
throws IOException, SAXException, ParserConfigurationException {
|
throws IOException, SAXException, ParserConfigurationException {
|
||||||
|
|
||||||
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
|
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
|
||||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -22,6 +22,7 @@ import brut.directory.ExtFile;
|
|||||||
import brut.directory.FileDirectory;
|
import brut.directory.FileDirectory;
|
||||||
import org.custommonkey.xmlunit.*;
|
import org.custommonkey.xmlunit.*;
|
||||||
import org.w3c.dom.Document;
|
import org.w3c.dom.Document;
|
||||||
|
import org.w3c.dom.Element;
|
||||||
import org.xml.sax.SAXException;
|
import org.xml.sax.SAXException;
|
||||||
|
|
||||||
import javax.xml.parsers.DocumentBuilder;
|
import javax.xml.parsers.DocumentBuilder;
|
||||||
@ -155,6 +156,17 @@ public class BaseTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected static int getStringEntryCount(Document doc, String key) {
|
||||||
|
int count = 0;
|
||||||
|
Element resources = doc.getDocumentElement();
|
||||||
|
for (int i = 0; i < resources.getChildNodes().getLength(); i++) {
|
||||||
|
if (resources.getChildNodes().item(i).getNodeName().equals(key)) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
protected static ExtFile sTmpDir;
|
protected static ExtFile sTmpDir;
|
||||||
protected static ExtFile sTestOrigDir;
|
protected static ExtFile sTestOrigDir;
|
||||||
protected static ExtFile sTestNewDir;
|
protected static ExtFile sTestNewDir;
|
||||||
|
@ -18,15 +18,19 @@ package brut.androlib;
|
|||||||
|
|
||||||
import brut.androlib.exceptions.AndrolibException;
|
import brut.androlib.exceptions.AndrolibException;
|
||||||
import brut.androlib.res.Framework;
|
import brut.androlib.res.Framework;
|
||||||
|
import brut.androlib.res.xml.ResXmlPatcher;
|
||||||
import brut.common.BrutException;
|
import brut.common.BrutException;
|
||||||
import brut.directory.DirUtil;
|
import brut.directory.DirUtil;
|
||||||
import brut.directory.Directory;
|
import brut.directory.Directory;
|
||||||
import brut.directory.FileDirectory;
|
import brut.directory.FileDirectory;
|
||||||
import brut.util.OS;
|
import brut.util.OS;
|
||||||
|
import org.w3c.dom.Document;
|
||||||
|
import org.xml.sax.SAXException;
|
||||||
import org.xmlpull.v1.XmlPullParser;
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
import org.xmlpull.v1.XmlPullParserException;
|
import org.xmlpull.v1.XmlPullParserException;
|
||||||
import org.xmlpull.v1.XmlPullParserFactory;
|
import org.xmlpull.v1.XmlPullParserFactory;
|
||||||
|
|
||||||
|
import javax.xml.parsers.ParserConfigurationException;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.net.URLDecoder;
|
import java.net.URLDecoder;
|
||||||
@ -77,6 +81,14 @@ public abstract class TestUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Document getDocumentFromFile(File file) throws BrutException {
|
||||||
|
try {
|
||||||
|
return ResXmlPatcher.loadDocument(file);
|
||||||
|
} catch (ParserConfigurationException | SAXException | IOException ex) {
|
||||||
|
throw new BrutException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void copyResourceDir(Class<?> class_, String dirPath, File out) throws BrutException {
|
public static void copyResourceDir(Class<?> class_, String dirPath, File out) throws BrutException {
|
||||||
if (!out.exists()) {
|
if (!out.exists()) {
|
||||||
out.mkdirs();
|
out.mkdirs();
|
||||||
|
@ -47,6 +47,7 @@ public class AndroidOreoNotSparseTest extends BaseTest {
|
|||||||
|
|
||||||
LOGGER.info("Building not_sparse.apk...");
|
LOGGER.info("Building not_sparse.apk...");
|
||||||
Config config = Config.getDefaultConfig();
|
Config config = Config.getDefaultConfig();
|
||||||
|
config.useAapt2 = false;
|
||||||
new ApkBuilder(config, sTestNewDir).build(testApk);
|
new ApkBuilder(config, sTestNewDir).build(testApk);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,6 +47,7 @@ public class AndroidOreoSparseTest extends BaseTest {
|
|||||||
|
|
||||||
LOGGER.info("Building sparse.apk...");
|
LOGGER.info("Building sparse.apk...");
|
||||||
Config config = Config.getDefaultConfig();
|
Config config = Config.getDefaultConfig();
|
||||||
|
config.useAapt2 = false;
|
||||||
new ApkBuilder(config, sTestNewDir).build(testApk);
|
new ApkBuilder(config, sTestNewDir).build(testApk);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,10 +16,7 @@
|
|||||||
*/
|
*/
|
||||||
package brut.androlib.aapt1;
|
package brut.androlib.aapt1;
|
||||||
|
|
||||||
import brut.androlib.ApkBuilder;
|
import brut.androlib.*;
|
||||||
import brut.androlib.ApkDecoder;
|
|
||||||
import brut.androlib.BaseTest;
|
|
||||||
import brut.androlib.TestUtils;
|
|
||||||
import brut.directory.ExtFile;
|
import brut.directory.ExtFile;
|
||||||
import brut.common.BrutException;
|
import brut.common.BrutException;
|
||||||
import brut.util.OS;
|
import brut.util.OS;
|
||||||
@ -44,7 +41,9 @@ public class BuildAndDecodeJarTest extends BaseTest {
|
|||||||
|
|
||||||
LOGGER.info("Building testjar.jar...");
|
LOGGER.info("Building testjar.jar...");
|
||||||
File testJar = new File(sTmpDir, "testjar.jar");
|
File testJar = new File(sTmpDir, "testjar.jar");
|
||||||
new ApkBuilder(sTestOrigDir).build(testJar);
|
Config config = Config.getDefaultConfig();
|
||||||
|
config.useAapt2 = false;
|
||||||
|
new ApkBuilder(config, sTestOrigDir).build(testJar);
|
||||||
|
|
||||||
LOGGER.info("Decoding testjar.jar...");
|
LOGGER.info("Decoding testjar.jar...");
|
||||||
ApkDecoder apkDecoder = new ApkDecoder(testJar);
|
ApkDecoder apkDecoder = new ApkDecoder(testJar);
|
||||||
|
@ -16,10 +16,7 @@
|
|||||||
*/
|
*/
|
||||||
package brut.androlib.aapt1;
|
package brut.androlib.aapt1;
|
||||||
|
|
||||||
import brut.androlib.ApkBuilder;
|
import brut.androlib.*;
|
||||||
import brut.androlib.ApkDecoder;
|
|
||||||
import brut.androlib.BaseTest;
|
|
||||||
import brut.androlib.TestUtils;
|
|
||||||
import brut.androlib.apk.ApkInfo;
|
import brut.androlib.apk.ApkInfo;
|
||||||
import brut.common.BrutException;
|
import brut.common.BrutException;
|
||||||
import brut.directory.ExtFile;
|
import brut.directory.ExtFile;
|
||||||
@ -52,7 +49,9 @@ public class BuildAndDecodeTest extends BaseTest {
|
|||||||
|
|
||||||
LOGGER.info("Building testapp.apk...");
|
LOGGER.info("Building testapp.apk...");
|
||||||
File testApk = new File(sTmpDir, "testapp.apk");
|
File testApk = new File(sTmpDir, "testapp.apk");
|
||||||
new ApkBuilder(sTestOrigDir).build(testApk);
|
Config config = Config.getDefaultConfig();
|
||||||
|
config.useAapt2 = false;
|
||||||
|
new ApkBuilder(config, sTestOrigDir).build(testApk);
|
||||||
|
|
||||||
LOGGER.info("Decoding testapp.apk...");
|
LOGGER.info("Decoding testapp.apk...");
|
||||||
ApkDecoder apkDecoder = new ApkDecoder(testApk);
|
ApkDecoder apkDecoder = new ApkDecoder(testApk);
|
||||||
|
@ -48,6 +48,7 @@ public class DebugTagRetainedTest extends BaseTest {
|
|||||||
|
|
||||||
LOGGER.info("Building issue1235.apk...");
|
LOGGER.info("Building issue1235.apk...");
|
||||||
Config config = Config.getDefaultConfig();
|
Config config = Config.getDefaultConfig();
|
||||||
|
config.useAapt2 = false;
|
||||||
config.debugMode = true;
|
config.debugMode = true;
|
||||||
|
|
||||||
File testApk = new File(sTmpDir, "issue1235.apk");
|
File testApk = new File(sTmpDir, "issue1235.apk");
|
||||||
|
@ -16,10 +16,7 @@
|
|||||||
*/
|
*/
|
||||||
package brut.androlib.aapt1;
|
package brut.androlib.aapt1;
|
||||||
|
|
||||||
import brut.androlib.ApkBuilder;
|
import brut.androlib.*;
|
||||||
import brut.androlib.ApkDecoder;
|
|
||||||
import brut.androlib.BaseTest;
|
|
||||||
import brut.androlib.TestUtils;
|
|
||||||
import brut.common.BrutException;
|
import brut.common.BrutException;
|
||||||
import brut.directory.ExtFile;
|
import brut.directory.ExtFile;
|
||||||
import brut.util.OS;
|
import brut.util.OS;
|
||||||
@ -46,7 +43,9 @@ public class DefaultBaksmaliVariableTest extends BaseTest {
|
|||||||
|
|
||||||
LOGGER.info("Building issue1481.jar...");
|
LOGGER.info("Building issue1481.jar...");
|
||||||
File testJar = new File(sTmpDir, "issue1481.jar");
|
File testJar = new File(sTmpDir, "issue1481.jar");
|
||||||
new ApkBuilder(sTestOrigDir).build(testJar);
|
Config config = Config.getDefaultConfig();
|
||||||
|
config.useAapt2 = false;
|
||||||
|
new ApkBuilder(config, sTestOrigDir).build(testJar);
|
||||||
|
|
||||||
LOGGER.info("Decoding issue1481.jar...");
|
LOGGER.info("Decoding issue1481.jar...");
|
||||||
ApkDecoder apkDecoder = new ApkDecoder(testJar);
|
ApkDecoder apkDecoder = new ApkDecoder(testJar);
|
||||||
|
@ -50,6 +50,7 @@ public class EmptyResourcesArscTest {
|
|||||||
|
|
||||||
LOGGER.info("Building issue1730.apk...");
|
LOGGER.info("Building issue1730.apk...");
|
||||||
Config config = Config.getDefaultConfig();
|
Config config = Config.getDefaultConfig();
|
||||||
|
config.useAapt2 = false;
|
||||||
new ApkBuilder(config, sTestNewDir).build(testApk);
|
new ApkBuilder(config, sTestNewDir).build(testApk);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,12 +14,9 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package brut.androlib.decode;
|
package brut.androlib.aapt1;
|
||||||
|
|
||||||
import brut.androlib.ApkBuilder;
|
import brut.androlib.*;
|
||||||
import brut.androlib.ApkDecoder;
|
|
||||||
import brut.androlib.BaseTest;
|
|
||||||
import brut.androlib.TestUtils;
|
|
||||||
import brut.directory.ExtFile;
|
import brut.directory.ExtFile;
|
||||||
import brut.common.BrutException;
|
import brut.common.BrutException;
|
||||||
import brut.util.OS;
|
import brut.util.OS;
|
||||||
@ -43,7 +40,9 @@ public class ExternalEntityTest extends BaseTest {
|
|||||||
|
|
||||||
LOGGER.info("Building doctype.apk...");
|
LOGGER.info("Building doctype.apk...");
|
||||||
File testApk = new File(sTestOrigDir, "doctype.apk");
|
File testApk = new File(sTestOrigDir, "doctype.apk");
|
||||||
new ApkBuilder(sTestOrigDir).build(testApk);
|
Config config = Config.getDefaultConfig();
|
||||||
|
config.useAapt2 = false;
|
||||||
|
new ApkBuilder(config, sTestOrigDir).build(testApk);
|
||||||
|
|
||||||
LOGGER.info("Decoding doctype.apk...");
|
LOGGER.info("Decoding doctype.apk...");
|
||||||
ApkDecoder apkDecoder = new ApkDecoder(testApk);
|
ApkDecoder apkDecoder = new ApkDecoder(testApk);
|
@ -16,10 +16,7 @@
|
|||||||
*/
|
*/
|
||||||
package brut.androlib.aapt1;
|
package brut.androlib.aapt1;
|
||||||
|
|
||||||
import brut.androlib.ApkBuilder;
|
import brut.androlib.*;
|
||||||
import brut.androlib.ApkDecoder;
|
|
||||||
import brut.androlib.BaseTest;
|
|
||||||
import brut.androlib.TestUtils;
|
|
||||||
import brut.directory.ExtFile;
|
import brut.directory.ExtFile;
|
||||||
import brut.common.BrutException;
|
import brut.common.BrutException;
|
||||||
import brut.util.OS;
|
import brut.util.OS;
|
||||||
@ -54,8 +51,10 @@ public class LargeIntsInManifestTest extends BaseTest {
|
|||||||
apkDecoder.decode(outDir);
|
apkDecoder.decode(outDir);
|
||||||
|
|
||||||
// build issue767
|
// build issue767
|
||||||
|
Config config = Config.getDefaultConfig();
|
||||||
|
config.useAapt2 = false;
|
||||||
ExtFile testApk = new ExtFile(sTmpDir, apk + ".out");
|
ExtFile testApk = new ExtFile(sTmpDir, apk + ".out");
|
||||||
new ApkBuilder(testApk).build(null);
|
new ApkBuilder(config, testApk).build(null);
|
||||||
String newApk = apk + ".out" + File.separator + "dist" + File.separator + apk;
|
String newApk = apk + ".out" + File.separator + "dist" + File.separator + apk;
|
||||||
|
|
||||||
// decode issue767 again
|
// decode issue767 again
|
||||||
|
@ -16,10 +16,7 @@
|
|||||||
*/
|
*/
|
||||||
package brut.androlib.aapt1;
|
package brut.androlib.aapt1;
|
||||||
|
|
||||||
import brut.androlib.ApkBuilder;
|
import brut.androlib.*;
|
||||||
import brut.androlib.ApkDecoder;
|
|
||||||
import brut.androlib.BaseTest;
|
|
||||||
import brut.androlib.TestUtils;
|
|
||||||
import brut.directory.ExtFile;
|
import brut.directory.ExtFile;
|
||||||
import brut.common.BrutException;
|
import brut.common.BrutException;
|
||||||
import brut.util.OS;
|
import brut.util.OS;
|
||||||
@ -62,7 +59,9 @@ public class ProviderAttributeTest extends BaseTest {
|
|||||||
|
|
||||||
// build issue636
|
// build issue636
|
||||||
ExtFile testApk = new ExtFile(sTmpDir, apk + ".out");
|
ExtFile testApk = new ExtFile(sTmpDir, apk + ".out");
|
||||||
new ApkBuilder(testApk).build(null);
|
Config config = Config.getDefaultConfig();
|
||||||
|
config.useAapt2 = false;
|
||||||
|
new ApkBuilder(config, testApk).build(null);
|
||||||
String newApk = apk + ".out" + File.separator + "dist" + File.separator + apk;
|
String newApk = apk + ".out" + File.separator + "dist" + File.separator + apk;
|
||||||
assertTrue(fileExists(newApk));
|
assertTrue(fileExists(newApk));
|
||||||
|
|
||||||
|
@ -81,6 +81,7 @@ public class SharedLibraryTest extends BaseTest {
|
|||||||
Config config = Config.getDefaultConfig();
|
Config config = Config.getDefaultConfig();
|
||||||
config.frameworkDirectory = sTmpDir.getAbsolutePath();
|
config.frameworkDirectory = sTmpDir.getAbsolutePath();
|
||||||
config.frameworkTag = "shared";
|
config.frameworkTag = "shared";
|
||||||
|
config.useAapt2 = false;
|
||||||
|
|
||||||
// install library/framework
|
// install library/framework
|
||||||
new Framework(config).installFramework(new File(sTmpDir + File.separator + library));
|
new Framework(config).installFramework(new File(sTmpDir + File.separator + library));
|
||||||
|
@ -42,6 +42,7 @@ public class UnknownCompressionTest extends BaseTest {
|
|||||||
String apk = "deflated_unknowns.apk";
|
String apk = "deflated_unknowns.apk";
|
||||||
Config config = Config.getDefaultConfig();
|
Config config = Config.getDefaultConfig();
|
||||||
config.frameworkDirectory = sTmpDir.getAbsolutePath();
|
config.frameworkDirectory = sTmpDir.getAbsolutePath();
|
||||||
|
config.useAapt2 = false;
|
||||||
|
|
||||||
sTestOrigDir = new ExtFile(sTmpDir, apk);
|
sTestOrigDir = new ExtFile(sTmpDir, apk);
|
||||||
|
|
||||||
|
@ -86,6 +86,12 @@ public class BuildAndDecodeTest extends BaseTest {
|
|||||||
compareValuesFiles("values-b+iw+660/strings.xml");
|
compareValuesFiles("values-b+iw+660/strings.xml");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void valuesGrammaticalGenderTest() throws BrutException {
|
||||||
|
compareValuesFiles("values-neuter/strings.xml");
|
||||||
|
compareValuesFiles("values-feminine/strings.xml");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void valuesBcp47LanguageScriptRegionVariantTest() throws BrutException {
|
public void valuesBcp47LanguageScriptRegionVariantTest() throws BrutException {
|
||||||
compareValuesFiles("values-b+ast+Latn+IT+AREVELA/strings.xml");
|
compareValuesFiles("values-b+ast+Latn+IT+AREVELA/strings.xml");
|
||||||
|
@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2010 Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||||
|
* Copyright (C) 2010 Connor Tumbleson <connor.tumbleson@gmail.com>
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package brut.androlib.decode;
|
||||||
|
|
||||||
|
import brut.androlib.*;
|
||||||
|
import brut.directory.ExtFile;
|
||||||
|
import brut.common.BrutException;
|
||||||
|
import brut.util.OS;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.junit.*;
|
||||||
|
import org.w3c.dom.Document;
|
||||||
|
import org.xml.sax.SAXException;
|
||||||
|
|
||||||
|
import javax.xml.parsers.ParserConfigurationException;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
public class CompactResourceTest extends BaseTest {
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void beforeClass() throws Exception {
|
||||||
|
TestUtils.cleanFrameworkFile();
|
||||||
|
sTmpDir = new ExtFile(OS.createTempDirectory());
|
||||||
|
TestUtils.copyResourceDir(CompactResourceTest.class, "decode/issue3366/", sTmpDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
public static void afterClass() throws BrutException {
|
||||||
|
OS.rmdir(sTmpDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void checkIfDecodeSucceeds() throws BrutException, IOException, ParserConfigurationException, SAXException {
|
||||||
|
String apk = "issue3366.apk";
|
||||||
|
File testApk = new File(sTmpDir, apk);
|
||||||
|
|
||||||
|
// decode issue3366.apk
|
||||||
|
ApkDecoder apkDecoder = new ApkDecoder(testApk);
|
||||||
|
sTestOrigDir = new ExtFile(sTmpDir + File.separator + apk + ".out");
|
||||||
|
|
||||||
|
File outDir = new File(sTmpDir + File.separator + apk + ".out");
|
||||||
|
apkDecoder.decode(outDir);
|
||||||
|
|
||||||
|
Document doc = loadDocument(new File(sTestOrigDir + "/res/values/strings.xml"));
|
||||||
|
assertEquals(1002, getStringEntryCount(doc, "string"));
|
||||||
|
|
||||||
|
Config config = Config.getDefaultConfig();
|
||||||
|
LOGGER.info("Building duplicatedex.apk...");
|
||||||
|
new ApkBuilder(config, sTestOrigDir).build(testApk);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,140 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2010 Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||||
|
* Copyright (C) 2010 Connor Tumbleson <connor.tumbleson@gmail.com>
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package brut.androlib.decode;
|
||||||
|
|
||||||
|
import brut.androlib.ApkDecoder;
|
||||||
|
import brut.androlib.BaseTest;
|
||||||
|
import brut.androlib.Config;
|
||||||
|
import brut.androlib.TestUtils;
|
||||||
|
import brut.directory.ExtFile;
|
||||||
|
import brut.common.BrutException;
|
||||||
|
import brut.util.OS;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.junit.*;
|
||||||
|
import org.w3c.dom.Document;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
public class ResourceModeTest extends BaseTest {
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void beforeClass() throws Exception {
|
||||||
|
TestUtils.cleanFrameworkFile();
|
||||||
|
sTmpDir = new ExtFile(OS.createTempDirectory());
|
||||||
|
TestUtils.copyResourceDir(ResourceModeTest.class, "decode/issue2836/", sTmpDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
public static void afterClass() throws BrutException {
|
||||||
|
OS.rmdir(sTmpDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void checkDecodingModeAsRemove() throws BrutException, IOException {
|
||||||
|
String apk = "issue2836.apk";
|
||||||
|
|
||||||
|
Config config = Config.getDefaultConfig();
|
||||||
|
config.setDecodeResolveMode(Config.DECODE_RES_RESOLVE_REMOVE);
|
||||||
|
|
||||||
|
// decode issue2836.apk
|
||||||
|
ApkDecoder apkDecoder = new ApkDecoder(config, new File(sTmpDir + File.separator + apk));
|
||||||
|
sTestOrigDir = new ExtFile(sTmpDir + File.separator + apk + "remove.out");
|
||||||
|
|
||||||
|
File outDir = new File(sTmpDir + File.separator + apk + "remove.out");
|
||||||
|
apkDecoder.decode(outDir);
|
||||||
|
|
||||||
|
File stringsXml = new File(sTestOrigDir,"res/values/strings.xml");
|
||||||
|
assertTrue(stringsXml.isFile());
|
||||||
|
|
||||||
|
File attrXml = new File(sTestOrigDir,"res/values/attrs.xml");
|
||||||
|
Document attrDocument = TestUtils.getDocumentFromFile(attrXml);
|
||||||
|
assertEquals(3, attrDocument.getElementsByTagName("enum").getLength());
|
||||||
|
|
||||||
|
File colorXml = new File(sTestOrigDir,"res/values/colors.xml");
|
||||||
|
Document colorDocument = TestUtils.getDocumentFromFile(colorXml);
|
||||||
|
assertEquals(8, colorDocument.getElementsByTagName("color").getLength());
|
||||||
|
assertEquals(0, colorDocument.getElementsByTagName("item").getLength());
|
||||||
|
|
||||||
|
File publicXml = new File(sTestOrigDir,"res/values/public.xml");
|
||||||
|
Document publicDocument = TestUtils.getDocumentFromFile(publicXml);
|
||||||
|
assertEquals(21, publicDocument.getElementsByTagName("public").getLength());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void checkDecodingModeAsDummies() throws BrutException, IOException {
|
||||||
|
String apk = "issue2836.apk";
|
||||||
|
|
||||||
|
Config config = Config.getDefaultConfig();
|
||||||
|
config.setDecodeResolveMode(Config.DECODE_RES_RESOLVE_DUMMY);
|
||||||
|
|
||||||
|
// decode issue2836.apk
|
||||||
|
ApkDecoder apkDecoder = new ApkDecoder(config, new File(sTmpDir + File.separator + apk));
|
||||||
|
sTestOrigDir = new ExtFile(sTmpDir + File.separator + apk + "dummies.out");
|
||||||
|
|
||||||
|
File outDir = new File(sTmpDir + File.separator + apk + "dummies.out");
|
||||||
|
apkDecoder.decode(outDir);
|
||||||
|
|
||||||
|
File stringsXml = new File(sTestOrigDir,"res/values/strings.xml");
|
||||||
|
assertTrue(stringsXml.isFile());
|
||||||
|
|
||||||
|
File attrXml = new File(sTestOrigDir,"res/values/attrs.xml");
|
||||||
|
Document attrDocument = TestUtils.getDocumentFromFile(attrXml);
|
||||||
|
assertEquals(4, attrDocument.getElementsByTagName("enum").getLength());
|
||||||
|
|
||||||
|
File colorXml = new File(sTestOrigDir,"res/values/colors.xml");
|
||||||
|
Document colorDocument = TestUtils.getDocumentFromFile(colorXml);
|
||||||
|
assertEquals(8, colorDocument.getElementsByTagName("color").getLength());
|
||||||
|
assertEquals(1, colorDocument.getElementsByTagName("item").getLength());
|
||||||
|
|
||||||
|
File publicXml = new File(sTestOrigDir,"res/values/public.xml");
|
||||||
|
Document publicDocument = TestUtils.getDocumentFromFile(publicXml);
|
||||||
|
assertEquals(22, publicDocument.getElementsByTagName("public").getLength());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void checkDecodingModeAsLeave() throws BrutException, IOException {
|
||||||
|
String apk = "issue2836.apk";
|
||||||
|
|
||||||
|
Config config = Config.getDefaultConfig();
|
||||||
|
config.setDecodeResolveMode(Config.DECODE_RES_RESOLVE_RETAIN);
|
||||||
|
|
||||||
|
// decode issue2836.apk
|
||||||
|
ApkDecoder apkDecoder = new ApkDecoder(config, new File(sTmpDir + File.separator + apk));
|
||||||
|
sTestOrigDir = new ExtFile(sTmpDir + File.separator + apk + "leave.out");
|
||||||
|
|
||||||
|
File outDir = new File(sTmpDir + File.separator + apk + "leave.out");
|
||||||
|
apkDecoder.decode(outDir);
|
||||||
|
|
||||||
|
File stringsXml = new File(sTestOrigDir,"res/values/strings.xml");
|
||||||
|
assertTrue(stringsXml.isFile());
|
||||||
|
|
||||||
|
File attrXml = new File(sTestOrigDir,"res/values/attrs.xml");
|
||||||
|
Document attrDocument = TestUtils.getDocumentFromFile(attrXml);
|
||||||
|
assertEquals(4, attrDocument.getElementsByTagName("enum").getLength());
|
||||||
|
|
||||||
|
File colorXml = new File(sTestOrigDir,"res/values/colors.xml");
|
||||||
|
Document colorDocument = TestUtils.getDocumentFromFile(colorXml);
|
||||||
|
assertEquals(8, colorDocument.getElementsByTagName("color").getLength());
|
||||||
|
assertEquals(0, colorDocument.getElementsByTagName("item").getLength());
|
||||||
|
|
||||||
|
File publicXml = new File(sTestOrigDir,"res/values/public.xml");
|
||||||
|
Document publicDocument = TestUtils.getDocumentFromFile(publicXml);
|
||||||
|
assertEquals(21, publicDocument.getElementsByTagName("public").getLength());
|
||||||
|
}
|
||||||
|
}
|
@ -35,4 +35,7 @@
|
|||||||
<item>res/</item>
|
<item>res/</item>
|
||||||
<item>view/</item>
|
<item>view/</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
<string-array name="issue_2806">
|
||||||
|
<item>MIICXAIBAAKBgQCjcGqTkOq0CR3rTx0ZSQSIdTrDrFAYl29611xN8aVgMQIWtDB/lD0W5TpKPuU9iaiG/sSn/VYt6EzN7Sr332jj7cyl2WrrHI6ujRswNy4HojMuqtfab5FFDpRmCuvl35fge18OvoQTJELhhJ1EvJ5KUeZiuJ3u3YyMnxxXzLuKbQIDAQABAoGAPrNDz7TKtaLBvaIuMaMXgBopHyQd3jFKbT/tg2Fu5kYm3PrnmCoQfZYXFKCoZUFIS/G1FBVWWGpD/MQ9tbYZkKpwuH+t2rGndMnLXiTC296/s9uix7gsjnT4Naci5N6EN9pVUBwQmGrYUTHFc58ThtelSiPARX7LSU2ibtJSv8ECQQDWBRrrAYmbCUN7ra0DFT6SppaDtvvuKtb+mUeKbg0B8U4y4wCIK5GH8EyQSwUWcXnNBO05rlUPbifsDLv/u82lAkEAw39sTJ0KmJJyaChqvqAJ8guulKlgucQJ0Et9ppZyet9iVwNKX/aW9UlwGBMQdafQ36nd1QMEA8AbAw4D+hw/KQJBANJbHDUGQtk2hrSmZNoV5HXB9Uiq7v4N71k5ER8XwgM5yVGs2tX8dMM3RhnBEtQXXs9LW1uJZSOQcv7JGXNnhN0CQBZenzrJAWxh3XtznHtBfsHWelyCYRIAj4rpCHCmaGUM6IjCVKFUawOYKp5mmAyObkUZf8ue87emJLEdynC1CLkCQHduNjP1hemAGWrd6v8BHhE3kKtcK6KHsPvJR5dOfzbdHAqVePERhISfN6cwZt5p8B3/JUwSR8el66DF7Jm57BM=</item>
|
||||||
|
</string-array>
|
||||||
</resources>
|
</resources>
|
||||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
@ -1,7 +1,5 @@
|
|||||||
val commonsIoVersion: String by rootProject.extra
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(project(":brut.j.common"))
|
implementation(project(":brut.j.common"))
|
||||||
implementation(project(":brut.j.util"))
|
implementation(project(":brut.j.util"))
|
||||||
implementation("commons-io:commons-io:$commonsIoVersion")
|
implementation(libs.commons.io)
|
||||||
}
|
}
|
||||||
|
@ -267,8 +267,7 @@ public abstract class AbstractDirectory implements Directory {
|
|||||||
protected abstract AbstractDirectory createDirLocal(String name) throws DirectoryException;
|
protected abstract AbstractDirectory createDirLocal(String name) throws DirectoryException;
|
||||||
protected abstract void removeFileLocal(String name);
|
protected abstract void removeFileLocal(String name);
|
||||||
|
|
||||||
|
private static class ParsedPath {
|
||||||
private class ParsedPath {
|
|
||||||
public final String dir;
|
public final String dir;
|
||||||
public final String subpath;
|
public final String subpath;
|
||||||
public ParsedPath(String dir, String subpath) {
|
public ParsedPath(String dir, String subpath) {
|
||||||
@ -277,7 +276,7 @@ public abstract class AbstractDirectory implements Directory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class SubPath {
|
private static class SubPath {
|
||||||
public final AbstractDirectory dir;
|
public final AbstractDirectory dir;
|
||||||
public final String path;
|
public final String path;
|
||||||
|
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
val commonsIoVersion: String by rootProject.extra
|
|
||||||
val guavaVersion: String by rootProject.extra
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(project(":brut.j.common"))
|
implementation(project(":brut.j.common"))
|
||||||
implementation("commons-io:commons-io:$commonsIoVersion")
|
implementation(libs.commons.io)
|
||||||
implementation("com.google.guava:guava:$guavaVersion")
|
implementation(libs.guava)
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,7 @@
|
|||||||
import java.io.ByteArrayOutputStream
|
import java.io.ByteArrayOutputStream
|
||||||
|
|
||||||
val baksmaliVersion by extra("3.0.3")
|
val version = "2.9.1"
|
||||||
val commonsCliVersion by extra("1.5.0")
|
val suffix = "SNAPSHOT"
|
||||||
val commonsIoVersion by extra("2.13.0")
|
|
||||||
val commonsLangVersion by extra("3.13.0")
|
|
||||||
val commonsTextVersion by extra("1.10.0")
|
|
||||||
val guavaVersion by extra("32.0.1-jre")
|
|
||||||
val junitVersion by extra("4.13.2")
|
|
||||||
val smaliVersion by extra("3.0.3")
|
|
||||||
val xmlpullVersion by extra("1.1.4c")
|
|
||||||
val xmlunitVersion by extra("2.9.1")
|
|
||||||
|
|
||||||
val version = "2.8.2"
|
|
||||||
val suffix = "6"
|
|
||||||
|
|
||||||
// Strings embedded into the build.
|
// Strings embedded into the build.
|
||||||
var gitRevision by extra("")
|
var gitRevision by extra("")
|
||||||
@ -66,17 +55,12 @@ if ("release" !in gradle.startParameter.taskNames) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("com.github.johnrengelman.shadow") version "8.1.1"
|
alias(libs.plugins.shadow)
|
||||||
`java-library`
|
`java-library`
|
||||||
`maven-publish`
|
`maven-publish`
|
||||||
signing
|
signing
|
||||||
}
|
}
|
||||||
|
|
||||||
java {
|
|
||||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
|
||||||
targetCompatibility = JavaVersion.VERSION_1_8
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.withType<JavaCompile> {
|
tasks.withType<JavaCompile> {
|
||||||
options.compilerArgs.add("-Xlint:-options")
|
options.compilerArgs.add("-Xlint:-options")
|
||||||
options.compilerArgs.add("--release 8")
|
options.compilerArgs.add("--release 8")
|
||||||
@ -95,6 +79,11 @@ subprojects {
|
|||||||
apply(plugin = "java")
|
apply(plugin = "java")
|
||||||
apply(plugin = "java-library")
|
apply(plugin = "java-library")
|
||||||
|
|
||||||
|
java {
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility = JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
|
||||||
val mavenProjects = arrayOf("apktool-lib", "apktool-cli", "brut.j.common", "brut.j.util", "brut.j.dir")
|
val mavenProjects = arrayOf("apktool-lib", "apktool-cli", "brut.j.common", "brut.j.util", "brut.j.dir")
|
||||||
|
|
||||||
if (project.name in mavenProjects) {
|
if (project.name in mavenProjects) {
|
||||||
|
62
docker/Dockerfile
Normal file
62
docker/Dockerfile
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
# BUILDER
|
||||||
|
# =====================================================
|
||||||
|
FROM ibm-semeru-runtimes:open-17-jdk-jammy AS builder
|
||||||
|
|
||||||
|
COPY . /build
|
||||||
|
WORKDIR /build
|
||||||
|
RUN \
|
||||||
|
# Apktool
|
||||||
|
./gradlew build shadowJar proguard
|
||||||
|
|
||||||
|
RUN \
|
||||||
|
# Relocate for easier copying
|
||||||
|
JAR=$(find /build/brut.apktool/apktool-cli/build/libs/apktool-*.jar |grep -v cli-all) &&\
|
||||||
|
mv ${JAR} /build/apktool.jar
|
||||||
|
|
||||||
|
# BASE
|
||||||
|
# =====================================================
|
||||||
|
FROM ibm-semeru-runtimes:open-17-jre-jammy AS base
|
||||||
|
|
||||||
|
# Latest version as of 2023.10.01
|
||||||
|
# Ref: https://developer.android.com/studio/index.html#command-line-tools-only
|
||||||
|
ARG COMMAND_LINE_TOOLS_VERSION=10406996
|
||||||
|
|
||||||
|
ARG ANDROID_SDK_ROOT=/opt/android-sdk
|
||||||
|
|
||||||
|
COPY --from=builder /build/apktool.jar /usr/local/bin/apktool.jar
|
||||||
|
COPY --from=builder /build/scripts/linux/apktool /usr/local/bin/apktool
|
||||||
|
|
||||||
|
RUN \
|
||||||
|
# Update
|
||||||
|
apt-get update &&\
|
||||||
|
\
|
||||||
|
# Apktool final setup
|
||||||
|
chmod +x /usr/local/bin/apktool /usr/local/bin/apktool.jar &&\
|
||||||
|
\
|
||||||
|
# Android SDK
|
||||||
|
apt-get install -y --no-install-recommends \
|
||||||
|
ca-certificates \
|
||||||
|
curl \
|
||||||
|
zipalign \
|
||||||
|
git \
|
||||||
|
openssl \
|
||||||
|
wget \
|
||||||
|
unzip \
|
||||||
|
sdkmanager &&\
|
||||||
|
curl -o /tmp/tools.zip https://dl.google.com/android/repository/commandlinetools-linux-${COMMAND_LINE_TOOLS_VERSION}_latest.zip &&\
|
||||||
|
mkdir -p ${ANDROID_SDK_ROOT}/cmdline-tools &&\
|
||||||
|
unzip -q /tmp/tools.zip -d ${ANDROID_SDK_ROOT}/cmdline-tools &&\
|
||||||
|
mv ${ANDROID_SDK_ROOT}/cmdline-tools/cmdline-tools ${ANDROID_SDK_ROOT}/cmdline-tools/latest &&\
|
||||||
|
rm -v /tmp/tools.zip &&\
|
||||||
|
mkdir -p /root/.android/ && touch /root/.android/repositories.cfg &&\
|
||||||
|
yes | sdkmanager --licenses &&\
|
||||||
|
export BUILD_TOOLS_VERSION=$(sdkmanager --list |grep build-tools |grep -v rc |awk '{print $1}' |sed 's/build-tools;//g' |sort |tail -n1) &&\
|
||||||
|
sdkmanager --install "build-tools;${BUILD_TOOLS_VERSION}" &&\
|
||||||
|
ln -s ${ANDROID_SDK_ROOT}/build-tools/${BUILD_TOOLS_VERSION} /opt/bin &&\
|
||||||
|
\
|
||||||
|
# Cleanup
|
||||||
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
ENV PATH ${PATH}:/opt/bin
|
||||||
|
|
||||||
|
CMD ["/usr/local/bin/apktool"]
|
27
docker/README.md
Normal file
27
docker/README.md
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# Apktool in Docker
|
||||||
|
We provide an easy way to leverage `apktool`, along with common Android tools such as `zipalign` and `apksigner`, all from within Docker.
|
||||||
|
|
||||||
|
## Building the Docker image
|
||||||
|
To use the image, pull or build with the included Dockerfile:
|
||||||
|
```bash
|
||||||
|
docker pull ghcr.io/ibotpeaches/apktool:latest
|
||||||
|
# OR
|
||||||
|
docker build -t apktool:local .
|
||||||
|
```
|
||||||
|
|
||||||
|
## Using the Docker image
|
||||||
|
The best way to use the image is to create aliases to run the internal commands. Replace `ghcr.io/ibotpeaches/apktool:latest` with `apktool:local` if you have built the image locally.
|
||||||
|
```bash
|
||||||
|
alias apktool="docker run --rm -ti --name=apktool -v \"${PWD}:${PWD}\" -w \"${PWD}\" ghcr.io/ibotpeaches/apktool:latest apktool"
|
||||||
|
alias zipalign="docker run --rm -ti --name=zipalign -v \"${PWD}:${PWD}\" -w \"${PWD}\" ghcr.io/ibotpeaches/apktool:latest zipalign"
|
||||||
|
alias apksigner="docker run --rm -ti --name=apksigner -v \"${PWD}:${PWD}\" -w \"${PWD}\" ghcr.io/ibotpeaches/apktool:latest apksigner"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Running the commands
|
||||||
|
You can then utilize these commands as you would if they were natively installed:
|
||||||
|
```bash
|
||||||
|
apktool d My.apk -o MyFolder
|
||||||
|
apktool b MyFolder -o MyNew.apk
|
||||||
|
zipalign -p -f 4 MyNew.apk MyNewAligned.apk
|
||||||
|
apksigner sign --ks My.keystore MyNewAligned.apk
|
||||||
|
```
|
29
gradle/libs.versions.toml
Normal file
29
gradle/libs.versions.toml
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
[versions]
|
||||||
|
baksmali = "3.0.3"
|
||||||
|
commons_io = "2.14.0"
|
||||||
|
commons_cli = "1.5.0"
|
||||||
|
commons_lang3 = "3.13.0"
|
||||||
|
commons_text = "1.10.0"
|
||||||
|
guava = "32.0.1-jre"
|
||||||
|
junit = "4.13.2"
|
||||||
|
proguard = "7.3.2"
|
||||||
|
shadow = "8.1.1"
|
||||||
|
smali = "3.0.3"
|
||||||
|
xmlpull = "1.1.4c"
|
||||||
|
xmlunit = "2.9.1"
|
||||||
|
|
||||||
|
[libraries]
|
||||||
|
baksmali = { module = "com.android.tools.smali:smali-baksmali", version.ref = "baksmali" }
|
||||||
|
commons_cli = { module = "commons-cli:commons-cli", version.ref = "commons_cli"}
|
||||||
|
commons_io = { module = "commons-io:commons-io", version.ref = "commons_io" }
|
||||||
|
commons_lang3 = { module = "org.apache.commons:commons-lang3", version.ref = "commons_lang3" }
|
||||||
|
commons_text = { module = "org.apache.commons:commons-text", version.ref = "commons_text" }
|
||||||
|
guava = { module = "com.google.guava:guava", version.ref = "guava" }
|
||||||
|
junit = { module = "junit:junit", version.ref = "junit" }
|
||||||
|
proguard = { module = "com.guardsquare:proguard-gradle", version.ref = "proguard" }
|
||||||
|
smali = { module = "com.android.tools.smali:smali", version.ref = "smali" }
|
||||||
|
xmlpull = { module = "xpp3:xpp3", version.ref = "xmlpull" }
|
||||||
|
xmlunit = { module = "org.xmlunit:xmlunit-legacy", version.ref = "xmlunit" }
|
||||||
|
|
||||||
|
[plugins]
|
||||||
|
shadow = { id = "com.github.johnrengelman.shadow", version.ref = "shadow" }
|
@ -39,4 +39,4 @@ if "%ATTR:~0,1%"=="-" if "%~x1"==".apk" (
|
|||||||
"%java_exe%" -jar -Xmx1024M -Duser.language=en -Dfile.encoding=UTF8 -Djdk.util.zip.disableZip64ExtraFieldValidation=true -Djdk.nio.zipfs.allowDotZipEntry=true "%~dp0%BASENAME%%max%.jar" %fastCommand% %*
|
"%java_exe%" -jar -Xmx1024M -Duser.language=en -Dfile.encoding=UTF8 -Djdk.util.zip.disableZip64ExtraFieldValidation=true -Djdk.nio.zipfs.allowDotZipEntry=true "%~dp0%BASENAME%%max%.jar" %fastCommand% %*
|
||||||
|
|
||||||
rem Pause when ran non interactively
|
rem Pause when ran non interactively
|
||||||
for /f "tokens=2" %%# in ("%cmdcmdline%") do if /i "%%#" equ "/c" pause
|
for %%i in (%cmdcmdline%) do if /i "%%~i"=="/c" pause & exit /b
|
||||||
|
@ -1,2 +1,8 @@
|
|||||||
rootProject.name = "apktool-cli"
|
rootProject.name = "apktool-cli"
|
||||||
include("brut.j.common", "brut.j.util", "brut.j.dir", "brut.apktool:apktool-lib", "brut.apktool:apktool-cli")
|
include("brut.j.common", "brut.j.util", "brut.j.dir", "brut.apktool:apktool-lib", "brut.apktool:apktool-cli")
|
||||||
|
|
||||||
|
dependencyResolutionManagement {
|
||||||
|
versionCatalogs {
|
||||||
|
create("libs") {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user