├── .gitignore ├── README.md ├── Xposed.pm ├── build.conf.sample ├── build.pl ├── busybox.pl ├── create_uninstaller.pl ├── local_manifests ├── remove_art.xml ├── xposed_sdk15.xml ├── xposed_sdk16.xml ├── xposed_sdk17.xml ├── xposed_sdk18.xml ├── xposed_sdk19.xml ├── xposed_sdk21.xml ├── xposed_sdk22.xml └── xposed_sdk23.xml ├── signapk.jar ├── signkey.pk8 ├── signkey.x509.pem └── zipstatic ├── _all └── META-INF │ └── com │ └── google │ └── android │ ├── flash-script.sh │ └── updater-script ├── _uninstaller └── META-INF │ └── com │ └── google │ └── android │ ├── flash-script.sh │ └── updater-script ├── arm └── META-INF │ └── com │ └── google │ └── android │ └── update-binary ├── arm64 ├── armv5 └── META-INF │ └── com │ └── google │ └── android │ └── update-binary └── x86 └── META-INF └── com └── google └── android ├── genymotion-ready └── update-binary /.gitignore: -------------------------------------------------------------------------------- 1 | # The configuration has to be created based on the environment 2 | /build.conf 3 | 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Xposed Tools 2 | 3 | These tools can be used to compile and package the Xposed framework. They are especially useful when it comes to releasing files for various platforms and Android versions. Note that Xposed by itself is quite complicated and not suitable for beginners. You'll need a certain level of experience with C++ and general software development in order to build and modify Xposed by yourself. 4 | 5 | ---------------------------------- 6 | ## General note on forks and custom builds 7 | Xposed is open-source and contributions are very welcome. The files in this repository hopefully make it easier to compile custom versions for testing and improvements. However, please be careful when you publish your own versions. Make clear that it's an unofficial version to avoid confusion, and also remember to give proper attribution. Your version probably still includes 99% or more of the original source code that has been written since 2012, so it wouldn't be fair to make it sound like you did all the work. Also, if you made some fixes or improvements, it's in everyone's interest that you create a pull request and contribute them back to the original project. 8 | 9 | ---------------------------------- 10 | ## Build script (build.pl) 11 | This script can perform the following steps: 12 | - Compile the native executables (`app_process`), libraries (`libxposed_*.so`) and the modified ART runtime. 13 | - Collect these files and put them into a common output directory. 14 | - Create the `xposed.prop` file that serves as label for the published package. 15 | - Create a flashable ZIP file to install the Xposed framework. 16 | 17 | It can also compile the Java part of the framework, called `XposedBridge.jar`. 18 | 19 | You can call `./build.pl --help` to get a list of allowed options. Usually, it's enough to specify the `-t` option, e.g `./build.pl -t arm,x86:21` to build ARM and x86 files for SDK 21 (Android 5.0). 20 | 21 | > You will need to have Perl installed to run this script. It also requires some Perl modules, some of which might not be pre-installed. Depending on your distribution, you can install them using your OS package manager (like apt-get, packages could be named `lib*-perl`) or via `cpan `. Please look up the details for your installation yourself. 22 | 23 | ---------------------------------- 24 | ## Build preparations 25 | ### AOSP source tree 26 | If you have never built the native parts of Xposed before, you obviously need to download the source code. The Xposed source is placed in the complete AOSP source tree, so it's not enough to download just the SDK/NDK. Instead, please follow the [official instructions](https://source.android.com/source/building.html) to download the full source of the Android version that you want to build for. If you have done everything right, the command `make -j4 app_process` should succeed. Please note that I can't support you with these steps. 27 | 28 | ### Xposed source code 29 | Once you have the AOSP source ready, you can integrate the Xposed sources into it. There are at least three ways to do so: 30 | #### Local manifest 31 | This is probably the easiest way to get started. Go to the root directory of the AOSP directory. Change into the `.repo` subdirectory and create a folder called `local_manifests`. Then create a symbolic link to one of the manifests that are included in this repository, e.g. `ln -s /path/to/this/repository/local_manifests/xposed_sdk21.xml .`. Afterwards, go back to the AOSP root directory and run `repo sync` again. This will also help to avoid some failures when compiling older Android versions on recent VMs. 32 | 33 | #### Manual cloning 34 | If you're afraid that `repo sync` might overwrite your changes or for some other reasons you don't want to use it, you can also clone the repositories manually. First, navigate to the `framework/base/cmds` directory and execute `git clone https://github.com/rovo89/Xposed.git xposed`. This repository contains the modified `app_process` executable and the `libxposed_*.so` files. 35 | 36 | For variants that include ART, you will also need to replace `art` folder. However, you have to be careful that the `repo sync` command doesn't interfere with the new directory. For this, follow the steps above to activate the `remove_art.xml` manifest and run `repo sync`. This should already remove the `art` directory in the AOSP root, but otherwise, just remove it manually. Then execute `git clone https://github.com/rovo89/android_art.git art` to download the modified files. 37 | 38 | #### Bind mounting 39 | In case you have many AOSP trees and want to keep the Xposed source in sync for all of them (i.e. make one change and apply it to all SDKs immediately), you can also look into bind mounts. It's basically the same as manual cloning, but you clone the files into a separate directory and then use bind mounts to map them into the AOSP tree. This is a pretty advanced technique, so I won't go into details here. 40 | 41 | ### XposedBridge source code (or prebuilt file) 42 | If you want to compile `XposedBridge.jar` yourself, clone https://github.com/rovo89/XposedBridge and set the `javadir` variable accordingly. Make sure that the Gradle build is working fine, the Android SDK and other dependencies need to be installed for this. Then you can call `./build.pl -a java` to build and copy the JAR. 43 | 44 | If you want to use a prebuilt file instead, copy it to a `java` subfolder in the `outdir` that you have configured in `build.conf`. For example, if `outdir` is set to `/android/out`, then the file should be stored in `/android/out/java/XposedBridge.jar`. 45 | 46 | ---------------------------------- 47 | ## Configuration (build.conf) 48 | The `build.pl` script requires some configuration. The settings are stored in INI file format in `build.conf`. 49 | As the configuration is specific to your local machine, it's not included in the GitHub repository. There is, however, a file called [`build.conf.sample`](build.conf.sample) with some examples. You can either copy it to `build.conf` or create your own file based on it. 50 | 51 | ##### [General] 52 | **outdir:** The output directory for compiled files. All Xposed-specific executables/libraries are copied here, and it's used to store log files and the flashable ZIP. This directory must exist. 53 | **javadir:** *(Optional)* The directory that XposedBridge has been checked out to (see above). 54 | 55 | ##### [Build] 56 | **version:** The version number that is stored in the `/system/xposed.prop` file. It's displayed in various places, e.g. while flashing the ZIP file or in the installer. It's also the API version for Xposed, so please make sure that you use the version number that your build is based on. You're free to add any custom suffix with your own version number. You can use the placeholder `%s` to insert the current date in `YYYYMMDD` format. 57 | **makeflags**: Additional parameters to pass to each `make` command. The default value is `-j4`, which enables parallel build with 4 jobs. 58 | 59 | ##### [GPG] 60 | **sign:** *(Optional)* Might be `all` to sign all build, or `release` to sign only release builds (`-r` parameter) using `gpg -ab `. 61 | **user:** *(Optional)* This is passed as `-u` parameter to the `gpg` command, see its man page for allowed formats. 62 | 63 | ##### [AospDir] 64 | The parameters in this section tell the build script where the AOSP source trees (see above) are stored for each Android version. The key is the SDK version, the value is the directory. 65 | 66 | ##### [BusyBox] 67 | In case you want to compile the specialized BusyBox fork from https://github.com/rovo89/android_external_busybox, you have to add a mapping of platform to an Android SDK version. In the source tree for this SDK version, you have to check out the project into the folder `external/busybox`. 68 | -------------------------------------------------------------------------------- /Xposed.pm: -------------------------------------------------------------------------------- 1 | package Xposed; 2 | 3 | use strict; 4 | use warnings; 5 | 6 | require Exporter; 7 | our @ISA = qw(Exporter); 8 | our @EXPORT = qw(print_status print_error); 9 | 10 | use Config::IniFiles; 11 | use File::Path qw(make_path); 12 | use File::ReadBackwards; 13 | use File::Tail; 14 | use FindBin qw($Bin); 15 | use POSIX qw(strftime); 16 | use Term::ANSIColor; 17 | 18 | our $cfg; 19 | my $MAX_SUPPORTED_SDK = 25; 20 | 21 | sub print_status($$) { 22 | my $text = shift; 23 | my $level = shift; 24 | my $color = ('black on_white', 'white on_blue')[$level]; 25 | print colored($text, $color), "\n"; 26 | } 27 | 28 | sub print_error($) { 29 | my $text = shift; 30 | print STDERR colored("ERROR: $text", 'red'), "\n"; 31 | } 32 | 33 | sub timestamp() { 34 | return strftime('%Y%m%d_%H%M%S', localtime()); 35 | } 36 | 37 | # Load and return a config file in .ini format 38 | sub load_config($) { 39 | my $cfgname = shift; 40 | 41 | # Make sure that the file is readable 42 | if (!-r $cfgname) { 43 | print_error("$cfgname doesn't exist or isn't readable"); 44 | return undef; 45 | } 46 | 47 | # Load the file 48 | my $cfg = Config::IniFiles->new( -file => $cfgname, -handle_trailing_comment => 1); 49 | if (!$cfg) { 50 | print_error("Could not read $cfgname:"); 51 | print STDERR " $_\n" foreach (@Config::IniFiles::errors); 52 | return undef; 53 | } 54 | 55 | # Trim trailing spaces of each value 56 | foreach my $section ($cfg->Sections()) { 57 | foreach my $key ($cfg->Parameters($section)) { 58 | my $value = $cfg->val($section, $key); 59 | if ($value =~ s/\s+$//) { 60 | $cfg->setval($section, $key, $value); 61 | } 62 | } 63 | } 64 | 65 | return $cfg; 66 | } 67 | 68 | # Makes sure that some important exist 69 | sub check_requirements() { 70 | my $outdir = $cfg->val('General', 'outdir'); 71 | if (!-d $outdir) { 72 | print_error('[General][outdir] must point to a directory'); 73 | return 0; 74 | } 75 | my $version = $cfg->val('Build', 'version'); 76 | if (!$version || $version !~ m/^\d+/) { 77 | print_error('[Build][version] must begin with an integer'); 78 | return 0; 79 | } 80 | if ($version =~ m/^\d+(?:\.\d+)*\s*$/ && !$cfg->val('Build', 'official')) { 81 | print_error('[Build][version] should contain your custom suffix'); 82 | return 0; 83 | } 84 | return 1; 85 | } 86 | 87 | # Start a separate process to display the last line of the log 88 | sub start_tail_process($) { 89 | my $logfile = shift; 90 | 91 | my $longest = 0; 92 | local $SIG{'TERM'} = sub { 93 | print "\r", ' ' x $longest, color('reset'), "\n" if $longest; 94 | exit 0; 95 | }; 96 | 97 | my $pid = fork(); 98 | return $pid if ($pid > 0); 99 | 100 | my $file = File::Tail->new(name => $logfile, ignore_nonexistant => 1, interval => 5, tail => 1); 101 | while (defined(my $line = $file->read())) { 102 | $line = substr($line, 0, 80); 103 | $line =~ s/\s+$//; 104 | my $len = length($line); 105 | if ($len < $longest) { 106 | $line .= ' ' x ($longest - $len); 107 | } else { 108 | $longest = $len; 109 | } 110 | print "\r", colored($line, 'yellow'); 111 | } 112 | exit 0; 113 | } 114 | 115 | # Expands the list of targets and replaces the "all" wildcard 116 | sub expand_targets($;$) { 117 | my $spec = shift; 118 | my $print = shift || 0; 119 | 120 | my @result; 121 | my %seen; 122 | foreach (split(m/[\/ ]+/, $spec)) { 123 | my ($pfspec, $sdkspec) = split(m/[: ]+/, $_, 2); 124 | my @pflist = ($pfspec ne 'all' && $pfspec ne 'all+') ? split(m/[, ]/, $pfspec) : ('arm', 'x86', 'arm64', 'armv5'); 125 | if ($pfspec eq 'all+') { 126 | push @pflist, 'host'; 127 | push @pflist, 'hostd'; 128 | $pfspec = 'all'; 129 | } 130 | my @sdklist = ($sdkspec ne 'all') ? split(m/[, ]/, $sdkspec) : $cfg->Parameters('AospDir'); 131 | foreach my $sdk (@sdklist) { 132 | foreach my $pf (@pflist) { 133 | next if !check_target_sdk_platform($pf, $sdk, $pfspec eq 'all' || $sdkspec eq 'all'); 134 | next if $seen{"$pf/$sdk"}++; 135 | push @result, { platform => $pf, sdk => $sdk }; 136 | print " SDK $sdk, platform $pf\n" if $print; 137 | } 138 | } 139 | } 140 | return @result; 141 | } 142 | 143 | # Check target SDK version and platform 144 | sub check_target_sdk_platform($$;$) { 145 | my $platform = shift; 146 | my $sdk = shift; 147 | my $wildcard = shift || 0; 148 | 149 | if ($sdk < 15 || $sdk == 20 || $sdk > $MAX_SUPPORTED_SDK) { 150 | print_error("Unsupported SDK version $sdk"); 151 | return 0; 152 | } 153 | 154 | if ($platform eq 'armv5') { 155 | if ($sdk > 17) { 156 | print_error('ARMv5 builds are only supported up to Android 4.2 (SDK 17)') unless $wildcard; 157 | return 0; 158 | } 159 | } elsif ($platform eq 'arm64') { 160 | if ($sdk < 21) { 161 | print_error('arm64 builds are not supported prior to Android 5.0 (SDK 21)') unless $wildcard; 162 | return 0; 163 | } 164 | } elsif ($platform eq 'host' || $platform eq 'hostd') { 165 | if ($sdk < 21) { 166 | print_error('host builds are not supported prior to Android 5.0 (SDK 21)') unless $wildcard; 167 | return 0; 168 | } 169 | } elsif ($platform ne 'arm' && $platform ne 'x86') { 170 | print_error("Unsupported target platform $platform"); 171 | return 0; 172 | } 173 | 174 | return 1; 175 | } 176 | 177 | # Returns the Xposed version 178 | sub get_version() { 179 | my $version = $Xposed::cfg->val('Build', 'version'); 180 | if ($version =~ m/%s/) { 181 | $version = sprintf($version, strftime('%Y%m%d', localtime())); 182 | } 183 | return $version; 184 | } 185 | 186 | # Returns the Xposed version number and the suffix to be used in file names 187 | sub get_version_for_filename(;$) { 188 | my $version = shift || get_version(); 189 | $version =~ m/^(\d+(?:\.\d+)*)(.*)/; 190 | my ($version_num, $suffix) = ($1, $2); 191 | if ($suffix) { 192 | $suffix =~ s/[\s\/|*"?<:>%()]+/-/g; 193 | $suffix =~ s/-{2,}/-/g; 194 | $suffix =~ s/^-|-$//g; 195 | $suffix = '-' . $suffix if $suffix; 196 | } 197 | return ($version_num, $suffix); 198 | } 199 | 200 | # Returns the root of the AOSP tree for the specified SDK 201 | sub get_rootdir($) { 202 | my $sdk = shift; 203 | 204 | my $dir = $cfg->val('AospDir', $sdk); 205 | if (!$dir) { 206 | print_error("No root directory has been configured for SDK $sdk"); 207 | return undef; 208 | } elsif ($dir !~ m/^/) { 209 | print_error("Root directory $dir must be an absolute path"); 210 | return undef; 211 | } elsif (!-d $dir) { 212 | print_error("$dir is not a directory"); 213 | return undef; 214 | } else { 215 | # Trim trailing slashes 216 | $dir =~ s|/+$||; 217 | return $dir; 218 | } 219 | } 220 | 221 | # Determines the root directory where compiled files are put 222 | sub get_outdir($) { 223 | my $platform = shift; 224 | 225 | if ($platform eq 'arm') { 226 | return 'out/target/product/generic'; 227 | } elsif ($platform eq 'armv5') { 228 | return 'out_armv5/target/product/generic'; 229 | } elsif ($platform eq 'x86' || $platform eq 'arm64') { 230 | return 'out/target/product/generic_' . $platform; 231 | } else { 232 | print_error("Could not determine output directory for $platform"); 233 | return undef; 234 | } 235 | } 236 | 237 | # Determines the directory where compiled files etc. are collected 238 | sub get_collection_dir($$) { 239 | my $platform = shift; 240 | my $sdk = shift; 241 | return sprintf('%s/sdk%d/%s', $cfg->val('General', 'outdir'), $sdk, $platform); 242 | } 243 | 244 | # Returns the directory to store symlinks to the ZIPs per versions 245 | sub get_version_dir(;$) { 246 | my ($version, $suffix) = get_version_for_filename(shift); 247 | return sprintf('%s/versions/v%s%s', $cfg->val('General', 'outdir'), $version, $suffix); 248 | } 249 | 250 | # Determines the mode that has to be passed to the "lunch" command 251 | sub get_lunch_mode($$) { 252 | my $platform = shift; 253 | my $sdk = shift; 254 | 255 | if ($platform eq 'arm' || $platform eq 'armv5' || $platform eq 'host' || $platform eq 'hostd') { 256 | return ($sdk <= 17) ? 'full-eng' : 'aosp_arm-eng'; 257 | } elsif ($platform eq 'x86') { 258 | return ($sdk <= 17) ? 'full_x86-eng' : 'aosp_x86-eng'; 259 | } elsif ($platform eq 'arm64' && $sdk >= 21) { 260 | return 'aosp_arm64-eng'; 261 | } else { 262 | print_error("Could not determine lunch mode for SDK $sdk, platform $platform"); 263 | return undef; 264 | } 265 | } 266 | 267 | # Get default make parameters 268 | sub get_make_parameters($$) { 269 | my $platform = shift; 270 | my $sdk = shift; 271 | 272 | my @params = split(m/\s+/, $cfg->val('Build', 'makeflags', '-j4')); 273 | 274 | # ARMv5 build need some special parameters 275 | if ($platform eq 'armv5') { 276 | push @params, 'OUT_DIR=out_armv5'; 277 | push @params, 'TARGET_ARCH_VARIANT=armv5te'; 278 | push @params, 'ARCH_ARM_HAVE_TLS_REGISTER=false'; 279 | push @params, 'TARGET_CPU_SMP=false'; 280 | } elsif ($sdk < 23) { 281 | push @params, 'TARGET_CPU_SMP=true'; 282 | } 283 | 284 | return @params; 285 | } 286 | 287 | sub compile($$$$$;$$$) { 288 | my $platform = shift; 289 | my $sdk = shift; 290 | my $params = shift; 291 | my $targets = shift; 292 | my $makefiles = shift; 293 | my $incremental = shift || 0; 294 | my $silent = shift && $sdk < 24 || 0; 295 | my $logprefix = shift || 'build'; 296 | 297 | # Initialize some general build parameters 298 | my $rootdir = get_rootdir($sdk) || return 0; 299 | my $lunch_mode = get_lunch_mode($platform, $sdk) || return 0; 300 | 301 | # Build the command string 302 | my $cdcmd = 'cd ' . $rootdir; 303 | my $envsetupcmd = '. build/envsetup.sh >/dev/null'; 304 | my $lunchcmd = 'lunch ' . $lunch_mode . ' >/dev/null'; 305 | my $makecmd = $incremental ? "ONE_SHOT_MAKEFILE='" . join(' ', @$makefiles) . "' make -C $rootdir -f build/core/main.mk " : 'make '; 306 | $makecmd .= join(' ', @$params, @$targets); 307 | my $cmd = join(' && ', $cdcmd, $envsetupcmd, $lunchcmd, $makecmd); 308 | print colored('Executing: ', 'magenta'), $cmd, "\n"; 309 | 310 | my ($logfile, $tailpid); 311 | if ($silent) { 312 | my $logdir = get_collection_dir($platform, $sdk) . '/logs'; 313 | make_path($logdir); 314 | $logfile = sprintf('%s/%s_%s.log', $logdir, $logprefix, timestamp()); 315 | print colored('Log: ', 'magenta'), $logfile, "\n"; 316 | $cmd = "{ $cmd ;} &> $logfile"; 317 | $tailpid = start_tail_process($logfile); 318 | } 319 | 320 | # Execute the command 321 | my $rc = system("bash -c \"$cmd\""); 322 | 323 | # Stop progress indicator process 324 | if ($tailpid) { 325 | kill('TERM', $tailpid); 326 | waitpid($tailpid, 0); 327 | } 328 | 329 | # Return the result 330 | if ($rc == 0) { 331 | print colored('Build was successful!', 'green'), "\n\n" if $sdk < 21; 332 | return 1; 333 | } else { 334 | print colored('Build failed!', 'red'), "\n" if $sdk < 21; 335 | if ($silent) { 336 | print "Last 10 lines from the log:\n"; 337 | my $tail = File::ReadBackwards->new($logfile); 338 | my @lines; 339 | for (1..10) { 340 | last if $tail->eof(); 341 | unshift @lines, $tail->readline(); 342 | } 343 | print " $_" foreach (@lines); 344 | } 345 | print "\n" if $silent || $sdk < 21; 346 | return 0; 347 | } 348 | } 349 | 350 | sub sign_zip($) { 351 | my $file = shift; 352 | my $signed = $file . '.signed'; 353 | my $cmd = "java -jar $Bin/signapk.jar -w $Bin/signkey.x509.pem $Bin/signkey.pk8 $file $signed"; 354 | system("bash -c \"$cmd\"") == 0 || return 0; 355 | rename($signed, $file); 356 | return 1; 357 | } 358 | 359 | sub should_gpg_sign(;$) { 360 | my $release_build = shift // 1; 361 | my $sign = $cfg->val('GPG', 'sign'); 362 | return $sign && ($sign eq 'all' || $release_build && $sign eq 'release'); 363 | } 364 | 365 | sub gpg_sign($) { 366 | my $file = shift; 367 | unlink($file . '.asc'); 368 | my $cmd = 'gpg -ab '; 369 | my $user = $cfg->val('GPG', 'user'); 370 | $cmd .= "-u '$user' " if $user; 371 | $cmd .= $file; 372 | return system($cmd) == 0; 373 | } 374 | 375 | 1; 376 | -------------------------------------------------------------------------------- /build.conf.sample: -------------------------------------------------------------------------------- 1 | [General] 2 | outdir = /android/out 3 | javadir = /android/XposedBridge 4 | 5 | [Build] 6 | # Please keep the base version number and add your custom suffix 7 | version = 65 (custom build by xyz / %s) 8 | # makeflags = -j4 9 | 10 | [GPG] 11 | sign = release 12 | user = 852109AA! 13 | 14 | # Root directories of the AOSP source tree per SDK version 15 | [AospDir] 16 | 19 = /android/aosp/440 17 | 21 = /android/aosp/500 18 | 19 | # SDKs to be used for compiling BusyBox 20 | # Needs https://github.com/rovo89/android_external_busybox 21 | [BusyBox] 22 | arm = 21 23 | x86 = 21 24 | armv5 = 17 25 | -------------------------------------------------------------------------------- /build.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use FindBin qw($Bin); 7 | use lib $Bin; 8 | 9 | use Xposed; 10 | 11 | use Archive::Zip qw(:ERROR_CODES :CONSTANTS); 12 | use Data::Dumper; 13 | use File::Basename; 14 | use File::Copy; 15 | use File::Path qw(make_path remove_tree); 16 | use Getopt::Std; 17 | use POSIX qw(strftime); 18 | use Tie::IxHash; 19 | use Term::ANSIColor; 20 | 21 | our $VERSION = '0.9'; 22 | 23 | my %opts; 24 | $| = 1; 25 | 26 | # Main routine 27 | sub main() { 28 | $Getopt::Std::STANDARD_HELP_VERSION = 1; 29 | getopts('a:firs:t:v', \%opts) || usage(2); 30 | $opts{'r'} //= 0; 31 | 32 | # Load the config file 33 | print_status("Loading config file $Bin/build.conf...", 0); 34 | $Xposed::cfg = Xposed::load_config("$Bin/build.conf") || exit 1; 35 | 36 | # Check some build requirements 37 | print_status('Checking requirements...', 0); 38 | Xposed::check_requirements() || exit 1; 39 | 40 | my $action = $opts{'a'} || 'build'; 41 | if ($action eq 'build') { 42 | my $jar = $Xposed::cfg->val('General', 'outdir') . '/java/XposedBridge.jar'; 43 | if (!-r $jar) { 44 | print_error("$jar doesn't exist or isn't readable"); 45 | exit 1; 46 | } 47 | 48 | # Determine build targets 49 | my $target_spec = $opts{'t'} || ''; 50 | print_status("Expanding targets from '$target_spec'...", 0); 51 | my @targets = Xposed::expand_targets($target_spec, 1); 52 | if (!@targets) { 53 | print_error('No valid targets specified'); 54 | usage(2); 55 | } 56 | print "\n"; 57 | 58 | # Check whether flashing is possible 59 | if ($opts{'f'} && $#targets != 0) { 60 | print_error('Flashing is only supported for a single target!'); 61 | exit 1; 62 | } 63 | 64 | # Build the specified targets 65 | foreach my $target (@targets) { 66 | all_in_one($target->{'platform'}, $target->{'sdk'}, !$opts{'v'}) || exit 1; 67 | } 68 | } elsif ($action eq 'java') { 69 | # Build XposedBridge.jar 70 | build_java() || exit 1; 71 | } elsif ($action eq 'prunelogs') { 72 | # Remove old logs 73 | prune_logs() || exit 1; 74 | } else { 75 | print_error("Unknown action specified: $action"); 76 | usage(2); 77 | } 78 | 79 | print_status('Done!', 0); 80 | } 81 | 82 | # Print usage and exit 83 | sub usage($) { 84 | my $exit = shift; 85 | print STDERR <][-t ] [-s ] [-r] 90 | -a Execute . The default is "build". 91 | -f Flash the files after building and performs a soft reboot. Requires step "zip". 92 | -t Build for targets specified in . 93 | -r Mark this as a release build. Currently only affects GPG signing. 94 | -s Limit build steps to . By default, all steps are performed. 95 | -i Incremental build. Compile faster by skipping dependencies (like mm/mmm). 96 | -v Verbose mode. Display the build log instead of redirecting it to a file. 97 | 98 | Possible actions are: 99 | build Builds the native executables and libraries. 100 | java Builds the Java part (XposedBridge). 101 | prunelogs Removes logs which are older than 24 hours. 102 | 103 | Format of is: :[/:/...] 104 | is a comma-separated list of: arm, x86, arm64 (and up to SDK 17, also armv5) 105 | is a comma-separated list of integers (e.g. 21 for Android 5.0) 106 | Both platform and SDK accept the wildcard "all". 107 | 108 | Values for are provided as a comma-separated list of: 109 | compile Compile executables and libraries. 110 | collect Collect compiled files and put them in the output directory. 111 | prop Create the xposed.prop file. 112 | zip Create the flashable ZIP file. 113 | gpg Sign the ZIP file with GPG (as configured). 114 | 115 | 116 | Examples: 117 | $0 -t arm:all/x86,arm64:21 118 | (build ARM files for all SDKs, plus x86 and arm64 files for SDK 21) 119 | 120 | USAGE 121 | exit $exit if $exit >= 0; 122 | } 123 | 124 | sub HELP_MESSAGE() { 125 | usage(-1); 126 | } 127 | 128 | sub VERSION_MESSAGE() { 129 | print "Xposed build script, version $VERSION\n"; 130 | } 131 | 132 | # Returns whether a certain build step should be performed 133 | sub should_perform_step($) { 134 | my $step = shift; 135 | return 1 if !$opts{'s'}; 136 | my @steps = split(m/[, ]+/, $opts{'s'}); 137 | foreach (@steps) { 138 | return 1 if $step eq $_; 139 | } 140 | return 0; 141 | } 142 | 143 | # Performs all build steps for one platform/SDK combination 144 | sub all_in_one($$;$) { 145 | my $platform = shift; 146 | my $sdk = shift; 147 | my $silent = shift || 0; 148 | 149 | print_status("Processing SDK $sdk, platform $platform...", 0); 150 | 151 | compile($platform, $sdk, $silent) || return 0; 152 | if ($platform ne 'host' && $platform ne 'hostd') { 153 | collect($platform, $sdk) || return 0; 154 | create_xposed_prop($platform, $sdk, !$silent) || return 0; 155 | create_zip($platform, $sdk) || return 0; 156 | gpg_sign($platform, $sdk) || return 0; 157 | } 158 | 159 | print "\n\n"; 160 | 161 | return 1; 162 | } 163 | 164 | # Compile Xposed (and possibly ART) for one SDK/platform 165 | sub compile($$;$) { 166 | my $platform = shift; 167 | my $sdk = shift; 168 | my $silent = shift || 0; 169 | 170 | should_perform_step('compile') || return 1; 171 | print_status("Compiling...", 1); 172 | 173 | my @params = Xposed::get_make_parameters($platform, $sdk); 174 | my @targets = qw(xposed); 175 | my @makefiles = qw(frameworks/base/cmds/xposed/Android.mk); 176 | 177 | # Version-specific targets 178 | if ($sdk < 21) { 179 | push @targets, qw/libxposed_dalvik/; 180 | } else { 181 | push @targets, qw/libxposed_art/; 182 | push @targets, qw/libart libart-compiler libart-disassembler libsigchain/; 183 | push @targets, qw/dex2oat oatdump patchoat/; 184 | push @makefiles, qw(art/Android.mk); 185 | } 186 | 187 | if ($platform eq 'host') { 188 | $ENV{'HOST_PREFER_32_BIT'} = 'true' if $sdk < 24; 189 | $ENV{'ART_BUILD_HOST_NDEBUG'} = 'true'; 190 | @targets = qw( 191 | out/host/linux-x86/bin/dex2oat 192 | out/host/linux-x86/bin/oatdump 193 | ); 194 | @makefiles = qw(art/Android.mk); 195 | } elsif ($platform eq 'hostd') { 196 | $ENV{'HOST_PREFER_32_BIT'} = 'true' if $sdk < 24; 197 | $ENV{'ART_BUILD_HOST_DEBUG'} = 'true'; 198 | @targets = qw( 199 | out/host/linux-x86/bin/dex2oatd 200 | out/host/linux-x86/bin/oatdumpd 201 | ); 202 | @makefiles = qw(art/Android.mk); 203 | } 204 | 205 | my $result = Xposed::compile($platform, $sdk, \@params, \@targets, \@makefiles, $opts{'i'}, $silent); 206 | 207 | delete($ENV{'ART_BUILD_HOST_NDEBUG'}); 208 | delete($ENV{'ART_BUILD_HOST_DEBUG'}); 209 | 210 | return $result; 211 | } 212 | 213 | # Collect final files into a single directory 214 | sub collect($$) { 215 | my $platform = shift; 216 | my $sdk = shift; 217 | 218 | should_perform_step('collect') || return 1; 219 | print_status("Collecting compiled files...", 1); 220 | 221 | my $coldir = Xposed::get_collection_dir($platform, $sdk); 222 | make_path($coldir); 223 | my $rootdir = Xposed::get_rootdir($sdk) || return 0; 224 | my $outdir = Xposed::get_outdir($platform) || return 0; 225 | 226 | # Clear collection directory 227 | remove_tree($coldir . '/files'); 228 | return 0 if -e $coldir . '/files'; 229 | 230 | # Copy files 231 | my $files = get_compiled_files($platform, $sdk); 232 | while( my ($file, $target) = each(%$files)) { 233 | $file = $rootdir . '/' . $outdir . $file; 234 | $target = $coldir . '/files' . $target; 235 | print "$file => $target\n"; 236 | make_path(dirname($target)); 237 | if (!copy($file, $target)) { 238 | print_error("Copy failed: $!"); 239 | return 0; 240 | } 241 | } 242 | 243 | return 1; 244 | } 245 | 246 | # Returns a hash paths of compiled files 247 | sub get_compiled_files($$) { 248 | my $platform = shift; 249 | my $sdk = shift; 250 | 251 | my %files; 252 | tie(%files, 'Tie::IxHash'); 253 | 254 | if ($sdk < 21) { 255 | $files{'/system/bin/app_process_xposed'} = '/system/bin/app_process_xposed'; 256 | $files{$_} = $_ foreach qw( 257 | /system/lib/libxposed_dalvik.so 258 | ); 259 | } else { 260 | $files{$_} = $_ foreach qw( 261 | /system/bin/app_process32_xposed 262 | /system/lib/libxposed_art.so 263 | 264 | /system/lib/libart.so 265 | /system/lib/libart-compiler.so 266 | /system/lib/libart-disassembler.so 267 | /system/lib/libsigchain.so 268 | 269 | /system/bin/dex2oat 270 | /system/bin/oatdump 271 | /system/bin/patchoat 272 | ); 273 | 274 | if ($platform eq 'arm64') { 275 | # libart-disassembler is required by oatdump only, which is a 64-bit executable 276 | delete $files{'/system/lib/libart-disassembler.so'}; 277 | 278 | $files{$_} = $_ foreach qw( 279 | /system/bin/app_process64_xposed 280 | /system/lib64/libxposed_art.so 281 | 282 | /system/lib64/libart.so 283 | /system/lib64/libart-disassembler.so 284 | /system/lib64/libsigchain.so 285 | ); 286 | 287 | if ($sdk >= 24) { 288 | $files{'/system/lib64/libart-compiler.so'} = '/system/lib64/libart-compiler.so'; 289 | } 290 | } 291 | } 292 | 293 | return \%files; 294 | } 295 | 296 | # Creates the /system/xposed.prop file 297 | sub create_xposed_prop($$;$) { 298 | my $platform = shift; 299 | my $sdk = shift; 300 | my $print = shift || 0; 301 | 302 | should_perform_step('prop') || return 1; 303 | print_status("Creating xposed.prop file...", 1); 304 | 305 | # Open the file 306 | my $coldir = Xposed::get_collection_dir($platform, $sdk); 307 | my $propfile = $coldir . '/files/system/xposed.prop'; 308 | print "$propfile\n"; 309 | make_path(dirname($propfile)); 310 | if (!open(PROPFILE, '>', $propfile)) { 311 | print_error("Could not write to $propfile: $!"); 312 | return 0; 313 | } 314 | 315 | # Prepare variables 316 | my $version = Xposed::get_version(); 317 | if ($platform eq 'armv5') { 318 | $platform = 'arm'; 319 | } 320 | 321 | # Calculate minimum / maximum compatible SDK versions 322 | my $minsdk = $sdk; 323 | my $maxsdk = $sdk; 324 | 325 | if ($sdk >= 15 && $sdk <= 19) { 326 | $minsdk = 15; 327 | $maxsdk = 19; 328 | } 329 | 330 | # Write to file 331 | my $content = <= 24) { 339 | $content .= "requires:fbe_aware=1\n" 340 | } 341 | 342 | print PROPFILE $content; 343 | print $content if $print; 344 | 345 | # Close the file 346 | close(PROPFILE); 347 | 348 | return 1; 349 | } 350 | 351 | # Create a flashable ZIP file with the compiled and some static files 352 | sub create_zip($$) { 353 | my $platform = shift; 354 | my $sdk = shift; 355 | 356 | should_perform_step('zip') || return 1; 357 | print_status("Creating flashable ZIP file...", 1); 358 | 359 | # Create a new ZIP file 360 | my $zip = Archive::Zip->new(); 361 | my $outdir = $Xposed::cfg->val('General', 'outdir'); 362 | my $coldir = Xposed::get_collection_dir($platform, $sdk); 363 | make_path($coldir); 364 | $zip->addTree($coldir . '/files/', '') == AZ_OK || return 0; 365 | $zip->addDirectory('system/framework/') || return 0; 366 | $zip->addFile("$outdir/java/XposedBridge.jar", 'system/framework/XposedBridge.jar') || return 0; 367 | # TODO: We probably need different files for older releases 368 | $zip->addTree($Bin . '/zipstatic/_all/', '') == AZ_OK || return 0; 369 | $zip->addTree($Bin . '/zipstatic/' . $platform . '/', '') == AZ_OK || return 0; 370 | 371 | # Set last modification time to "now" 372 | my $now = time(); 373 | foreach my $member($zip->members()) { 374 | $member->setLastModFileDateTimeFromUnix($now); 375 | } 376 | 377 | # Write the ZIP file to disk 378 | my ($version, $suffix) = Xposed::get_version_for_filename(); 379 | my $zipname = sprintf('xposed-v%s-sdk%d-%s%s.zip', $version, $sdk, $platform, $suffix); 380 | my $zippath = $coldir . '/' . $zipname; 381 | print "$zippath\n"; 382 | $zip->writeToFileNamed($zippath) == AZ_OK || return 0; 383 | 384 | Xposed::sign_zip($zippath); 385 | 386 | # Create a stable symlink to the latest version 387 | my $latestlink = $coldir . '/latest.zip'; 388 | unlink($latestlink); 389 | if (!symlink($zipname, $latestlink)) { 390 | print_error("Could not create link $latestlink -> $zipname: $!"); 391 | } 392 | 393 | # Create a symlink in a version-specific directory for easier collection 394 | my $versiondir = Xposed::get_version_dir(); 395 | my $versionlink = $versiondir . '/' . $zipname; 396 | make_path($versiondir); 397 | unlink($versionlink); 398 | if (!symlink($zippath, $versionlink)) { 399 | print_error("Could not create link $versionlink -> $zippath: $!"); 400 | } 401 | 402 | # Flash the file (if requested) 403 | if ($opts{'f'}) { 404 | print_status("Flashing ZIP file...", 1); 405 | my $shell = `adb shell id` =~ m/uid=0/ ? 'sh' : 'su'; 406 | system("adb push $zippath /data/local/tmp/xposed.zip") == 0 || return 0; 407 | system("adb push $Bin/zipstatic/$platform/META-INF/com/google/android/update-binary /data/local/tmp/update-binary") == 0 || return 0; 408 | system("adb shell 'chmod 700 /data/local/tmp/update-binary'") == 0 || return 0; 409 | system("adb shell $shell -c \\'NO_UIPRINT=1 /data/local/tmp/update-binary 2 1 /data/local/tmp/xposed.zip\\'") == 0 || return 0; 410 | system("adb shell 'rm /data/local/tmp/update-binary /data/local/tmp/xposed.zip'") == 0 || return 0; 411 | system("adb shell $shell -c stop") == 0 || return 0; 412 | sleep(2); 413 | system("adb shell $shell -c start") == 0 || return 0; 414 | } 415 | 416 | return 1; 417 | } 418 | 419 | # Sign the ZIP file with GPG. 420 | sub gpg_sign($$) { 421 | my $platform = shift; 422 | my $sdk = shift; 423 | 424 | should_perform_step('gpg') || return 1; 425 | Xposed::should_gpg_sign($opts{'r'}) || return 1; 426 | print_status("Signing ZIP file with GPG...", 1); 427 | 428 | # Calculate ZIP path. 429 | my $coldir = Xposed::get_collection_dir($platform, $sdk); 430 | my ($version, $suffix) = Xposed::get_version_for_filename(); 431 | my $zipname = sprintf('xposed-v%s-sdk%d-%s%s.zip', $version, $sdk, $platform, $suffix); 432 | my $zippath = $coldir . '/' . $zipname; 433 | print "$zippath.asc\n"; 434 | 435 | # Sign the file. 436 | return Xposed::gpg_sign($zippath); 437 | } 438 | 439 | # Build XposedBridge.jar 440 | sub build_java() { 441 | print_status('Building the Java part...', 0); 442 | my $javadir = $Xposed::cfg->val('General', 'javadir'); 443 | if (!-d $javadir) { 444 | print_error('[General][javadir] must point to a directory'); 445 | return 0; 446 | } 447 | 448 | print_status('Compiling...', 1); 449 | chdir($javadir); 450 | system('./gradlew app:assembleRelease lint') == 0 || return 0; 451 | print "\n"; 452 | 453 | print_status('Copying APK to XposedBridge.jar...', 1); 454 | my $base = $javadir . '/app/build/outputs/apk/app-release'; 455 | foreach my $suffix ('.apk', '-unaligned.apk', '-unsigned.apk') { 456 | my $file = $base . $suffix; 457 | if (-f $file) { 458 | my $target = $Xposed::cfg->val('General', 'outdir') . '/java/XposedBridge.jar'; 459 | print "$file => $target\n"; 460 | make_path(dirname($target)); 461 | if (!copy($file, $target)) { 462 | print_error("Copy failed: $!"); 463 | return 0; 464 | } 465 | print "\n"; 466 | return 1; 467 | } else { 468 | print "Skipping non-existent $file\n"; 469 | } 470 | } 471 | 472 | print_error('No suitable file found, please check the build results'); 473 | return 0; 474 | } 475 | 476 | # Remove old logs 477 | sub prune_logs(;$) { 478 | my $cutoff = shift || (time() - 86400); 479 | print_status('Cleaning log files...', 0); 480 | 481 | foreach my $logdir (glob($Xposed::cfg->val('General', 'outdir') . '/sdk*/*/logs/')) { 482 | print_status("Cleaning $logdir...", 1); 483 | 484 | my $removed = 0; 485 | opendir(DIR, $logdir) || next; 486 | foreach my $file (sort readdir(DIR)) { 487 | next if ($file !~ m/\.log$/); 488 | my $filepath = $logdir . '/' . $file; 489 | my $modtime = (stat($filepath))[9]; 490 | if ($modtime < $cutoff) { 491 | print "[REMOVE] $file\n"; 492 | unlink($filepath); 493 | $removed = 1; 494 | } else { 495 | print "[KEEP] $file\n"; 496 | } 497 | } 498 | closedir(DIR); 499 | 500 | print "\n" if $removed; 501 | } 502 | 503 | return 1; 504 | } 505 | 506 | main(); 507 | -------------------------------------------------------------------------------- /busybox.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use FindBin qw($Bin); 7 | use lib $Bin; 8 | 9 | use Xposed; 10 | 11 | use Data::Dumper; 12 | use File::Basename; 13 | use File::Copy; 14 | use File::Path qw(make_path); 15 | use Getopt::Std; 16 | 17 | our $VERSION = '1.0'; 18 | 19 | my %opts; 20 | $| = 1; 21 | 22 | # Main routine 23 | sub main() { 24 | $Getopt::Std::STANDARD_HELP_VERSION = 1; 25 | getopts('iuv', \%opts) || usage(2); 26 | 27 | # Load the config file 28 | print_status("Loading config file $Bin/build.conf...", 0); 29 | $Xposed::cfg = Xposed::load_config("$Bin/build.conf") || exit 1; 30 | 31 | # Check some build requirements 32 | print_status('Checking requirements...', 0); 33 | Xposed::check_requirements() || exit 1; 34 | 35 | # Build BusyBox for the configured platforms 36 | my @platforms = $Xposed::cfg->Parameters('BusyBox'); 37 | if (!@platforms) { 38 | print_error("No platforms found, please configure the [BusyBox] section!"); 39 | exit 1; 40 | } 41 | foreach my $platform (@platforms) { 42 | build($platform, $Xposed::cfg->val('BusyBox', $platform)) || exit 1; 43 | } 44 | 45 | print_status('Done!', 0); 46 | } 47 | 48 | # Print usage and exit 49 | sub usage($) { 50 | my $exit = shift; 51 | print STDERR <= 0; 61 | } 62 | 63 | sub HELP_MESSAGE() { 64 | usage(-1); 65 | } 66 | 67 | sub VERSION_MESSAGE() { 68 | print "BusyBox for Xposed build script, version $VERSION\n"; 69 | } 70 | 71 | # Compile BusyBox for one SDK/platform 72 | sub build($$;$) { 73 | my $platform = shift; 74 | my $sdk = shift; 75 | 76 | print_status("Building for $platform on SDK $sdk...", 0); 77 | Xposed::check_target_sdk_platform($platform, $sdk) || return 0; 78 | 79 | my $rootdir = Xposed::get_rootdir($sdk) || return 0; 80 | my $outdir = Xposed::get_outdir($platform) || return 0; 81 | 82 | # Ensure that the BusyBox config exists 83 | my $checkfile = "$rootdir/external/busybox/busybox-xposed.config"; 84 | if (!-f $checkfile) { 85 | print_error("$checkfile not found, make sure BusyBox is set up correctly!"); 86 | return 0; 87 | } 88 | 89 | my @params = Xposed::get_make_parameters($platform, $sdk); 90 | push @params, 'XPOSED_BUILD_STATIC=true'; 91 | my @targets = qw(xposed_busybox); 92 | my @makefiles = qw(external/busybox/Android.mk); 93 | 94 | print_status("Compiling...", 1); 95 | Xposed::compile($platform, $sdk, \@params, \@targets, \@makefiles, $opts{'i'}, !$opts{'v'}, 'busybox') || return 0; 96 | 97 | # Copy the files to the output directories 98 | print_status("Copying files...", 1); 99 | my $file = "$rootdir/$outdir/utilities/busybox-xposed-static"; 100 | system("strip $file") if $platform eq 'x86'; 101 | my @copy_targets = ($Xposed::cfg->val('General', 'outdir') . "/busybox/busybox-xposed-static-$platform"); 102 | push @copy_targets, "$Bin/zipstatic/$platform/META-INF/com/google/android/update-binary" if $opts{'u'}; 103 | foreach my $target (@copy_targets) { 104 | print "$file => $target\n"; 105 | make_path(dirname($target)); 106 | if (!copy($file, $target)) { 107 | print_error("Copy failed: $!"); 108 | return 0; 109 | } 110 | } 111 | 112 | print "\n\n"; 113 | 114 | return 1; 115 | } 116 | 117 | main(); 118 | -------------------------------------------------------------------------------- /create_uninstaller.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use FindBin qw($Bin); 7 | use lib $Bin; 8 | 9 | use Xposed; 10 | 11 | use Archive::Zip qw(:ERROR_CODES :CONSTANTS); 12 | use Data::Dumper; 13 | use File::Basename; 14 | use File::Copy; 15 | use File::Path qw(make_path remove_tree); 16 | use Getopt::Std; 17 | use POSIX qw(strftime); 18 | use Tie::IxHash; 19 | use Term::ANSIColor; 20 | 21 | our $VERSION = '1.0'; 22 | 23 | my %opts; 24 | $| = 1; 25 | 26 | # Main routine 27 | sub main() { 28 | $Getopt::Std::STANDARD_HELP_VERSION = 1; 29 | getopts('') || exit 2; 30 | 31 | # Load the config file 32 | print_status("Loading config file $Bin/build.conf...", 0); 33 | $Xposed::cfg = Xposed::load_config("$Bin/build.conf") || exit 1; 34 | 35 | # Check some build requirements 36 | print_status('Checking requirements...', 0); 37 | Xposed::check_requirements() || exit 1; 38 | 39 | print_status('Creating ZIP archives...', 0); 40 | foreach my $platform ('arm', 'x86', 'arm64', 'armv5') { 41 | create_zip($platform) if -d "$Bin/zipstatic/$platform" || exit 1; 42 | } 43 | 44 | print_status('Done!', 0); 45 | } 46 | 47 | sub create_zip() { 48 | my $platform = shift; 49 | 50 | # Create a new ZIP file 51 | my $zip = Archive::Zip->new(); 52 | $zip->addTree($Bin . '/zipstatic/_uninstaller/', '') == AZ_OK || return 0; 53 | $zip->addTree($Bin . '/zipstatic/' . $platform . '/', '') == AZ_OK || return 0; 54 | 55 | # Set last modification time to "now" 56 | my $now = time(); 57 | foreach my $member($zip->members()) { 58 | $member->setLastModFileDateTimeFromUnix($now); 59 | } 60 | 61 | # Write the ZIP file to disk 62 | my $outdir = $Xposed::cfg->val('General', 'outdir'); 63 | my $zipname = sprintf('%s/uninstaller/xposed-uninstaller-%s-%s.zip', $outdir, strftime('%Y%m%d', localtime()), $platform); 64 | make_path(dirname($zipname)); 65 | 66 | print "$zipname\n"; 67 | $zip->writeToFileNamed($zipname) == AZ_OK || return 0; 68 | 69 | Xposed::sign_zip($zipname); 70 | Xposed::gpg_sign($zipname) if Xposed::should_gpg_sign(); 71 | 72 | return 1; 73 | } 74 | 75 | sub HELP_MESSAGE() { 76 | } 77 | 78 | sub VERSION_MESSAGE() { 79 | print "Xposed uninstaller creation script, version $VERSION\n"; 80 | } 81 | 82 | main(); 83 | -------------------------------------------------------------------------------- /local_manifests/remove_art.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /local_manifests/xposed_sdk15.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /local_manifests/xposed_sdk16.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /local_manifests/xposed_sdk17.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /local_manifests/xposed_sdk18.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /local_manifests/xposed_sdk19.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /local_manifests/xposed_sdk21.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /local_manifests/xposed_sdk22.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /local_manifests/xposed_sdk23.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /signapk.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rovo89/XposedTools/78173acfbf7fe2ef46e4ee4d0dcbad15bb45b05f/signapk.jar -------------------------------------------------------------------------------- /signkey.pk8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rovo89/XposedTools/78173acfbf7fe2ef46e4ee4d0dcbad15bb45b05f/signkey.pk8 -------------------------------------------------------------------------------- /signkey.x509.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIEqDCCA5CgAwIBAgIJAJNurL4H8gHfMA0GCSqGSIb3DQEBBQUAMIGUMQswCQYD 3 | VQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4g 4 | VmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UE 5 | AxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAe 6 | Fw0wODAyMjkwMTMzNDZaFw0zNTA3MTcwMTMzNDZaMIGUMQswCQYDVQQGEwJVUzET 7 | MBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4G 8 | A1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9p 9 | ZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASAwDQYJKoZI 10 | hvcNAQEBBQADggENADCCAQgCggEBANaTGQTexgskse3HYuDZ2CU+Ps1s6x3i/waM 11 | qOi8qM1r03hupwqnbOYOuw+ZNVn/2T53qUPn6D1LZLjk/qLT5lbx4meoG7+yMLV4 12 | wgRDvkxyGLhG9SEVhvA4oU6Jwr44f46+z4/Kw9oe4zDJ6pPQp8PcSvNQIg1QCAcy 13 | 4ICXF+5qBTNZ5qaU7Cyz8oSgpGbIepTYOzEJOmc3Li9kEsBubULxWBjf/gOBzAzU 14 | RNps3cO4JFgZSAGzJWQTT7/emMkod0jb9WdqVA2BVMi7yge54kdVMxHEa5r3b97s 15 | zI5p58ii0I54JiCUP5lyfTwE/nKZHZnfm644oLIXf6MdW2r+6R8CAQOjgfwwgfkw 16 | HQYDVR0OBBYEFEhZAFY9JyxGrhGGBaR0GawJyowRMIHJBgNVHSMEgcEwgb6AFEhZ 17 | AFY9JyxGrhGGBaR0GawJyowRoYGapIGXMIGUMQswCQYDVQQGEwJVUzETMBEGA1UE 18 | CBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMH 19 | QW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAG 20 | CSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbYIJAJNurL4H8gHfMAwGA1Ud 21 | EwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAHqvlozrUMRBBVEY0NqrrwFbinZa 22 | J6cVosK0TyIUFf/azgMJWr+kLfcHCHJsIGnlw27drgQAvilFLAhLwn62oX6snb4Y 23 | LCBOsVMR9FXYJLZW2+TcIkCRLXWG/oiVHQGo/rWuWkJgU134NDEFJCJGjDbiLCpe 24 | +ZTWHdcwauTJ9pUbo8EvHRkU3cYfGmLaLfgn9gP+pWA7LFQNvXwBnDa6sppCccEX 25 | 31I828XzgXpJ4O+mDL1/dBd+ek8ZPUP0IgdyZm5MTYPhvVqGCHzzTy3sIeJFymwr 26 | sBbmg2OAUNLEMO6nwmocSdN2ClirfxqCzJOLSDE4QyS9BAH6EhY6UFcOaE0= 27 | -----END CERTIFICATE----- 28 | -------------------------------------------------------------------------------- /zipstatic/_all/META-INF/com/google/android/flash-script.sh: -------------------------------------------------------------------------------- 1 | ########################################################################################## 2 | # 3 | # Xposed framework installer zip. 4 | # 5 | # This script installs the Xposed framework files to the system partition. 6 | # The Xposed Installer app is needed as well to manage the installed modules. 7 | # 8 | ########################################################################################## 9 | 10 | grep_prop() { 11 | REGEX="s/^$1=//p" 12 | shift 13 | FILES=$@ 14 | if [ -z "$FILES" ]; then 15 | FILES='/system/build.prop' 16 | fi 17 | cat $FILES 2>/dev/null | sed -n $REGEX | head -n 1 18 | } 19 | 20 | android_version() { 21 | case $1 in 22 | 15) echo '4.0 / SDK'$1;; 23 | 16) echo '4.1 / SDK'$1;; 24 | 17) echo '4.2 / SDK'$1;; 25 | 18) echo '4.3 / SDK'$1;; 26 | 19) echo '4.4 / SDK'$1;; 27 | 21) echo '5.0 / SDK'$1;; 28 | 22) echo '5.1 / SDK'$1;; 29 | 23) echo '6.0 / SDK'$1;; 30 | 24) echo '7.0 / SDK'$1;; 31 | 25) echo '7.1 / SDK'$1;; 32 | 26) echo '8.0 / SDK'$1;; 33 | 27) echo '8.1 / SDK'$1;; 34 | *) echo 'SDK'$1;; 35 | esac 36 | } 37 | 38 | cp_perm() { 39 | cp -f $1 $2 || exit 1 40 | set_perm $2 $3 $4 $5 $6 41 | } 42 | 43 | set_perm() { 44 | chown $2:$3 $1 || exit 1 45 | chmod $4 $1 || exit 1 46 | if [ "$5" ]; then 47 | chcon $5 $1 2>/dev/null 48 | else 49 | chcon 'u:object_r:system_file:s0' $1 2>/dev/null 50 | fi 51 | } 52 | 53 | install_nobackup() { 54 | cp_perm ./$1 $1 $2 $3 $4 $5 55 | } 56 | 57 | install_and_link() { 58 | TARGET=$1 59 | XPOSED="${1}_xposed" 60 | BACKUP="${1}_original" 61 | if [ ! -f ./$XPOSED ]; then 62 | return 63 | fi 64 | cp_perm ./$XPOSED $XPOSED $2 $3 $4 $5 65 | if [ ! -f $BACKUP ]; then 66 | mv $TARGET $BACKUP || exit 1 67 | ln -s $XPOSED $TARGET || exit 1 68 | chcon -h 'u:object_r:system_file:s0' $TARGET 2>/dev/null 69 | fi 70 | } 71 | 72 | install_overwrite() { 73 | TARGET=$1 74 | if [ ! -f ./$TARGET ]; then 75 | return 76 | fi 77 | BACKUP="${1}.orig" 78 | NO_ORIG="${1}.no_orig" 79 | if [ ! -f $TARGET ]; then 80 | touch $NO_ORIG || exit 1 81 | set_perm $NO_ORIG 0 0 600 82 | elif [ -f $BACKUP ]; then 83 | rm -f $TARGET 84 | gzip $BACKUP || exit 1 85 | set_perm "${BACKUP}.gz" 0 0 600 86 | elif [ ! -f "${BACKUP}.gz" -a ! -f $NO_ORIG ]; then 87 | mv $TARGET $BACKUP || exit 1 88 | gzip $BACKUP || exit 1 89 | set_perm "${BACKUP}.gz" 0 0 600 90 | fi 91 | cp_perm ./$TARGET $TARGET $2 $3 $4 $5 92 | } 93 | 94 | ########################################################################################## 95 | 96 | echo "******************************" 97 | echo "Xposed framework installer zip" 98 | echo "******************************" 99 | 100 | if [ ! -f "system/xposed.prop" ]; then 101 | echo "! Failed: Extracted file system/xposed.prop not found!" 102 | exit 1 103 | fi 104 | 105 | echo "- Mounting /system and /vendor read-write" 106 | mount /system >/dev/null 2>&1 107 | mount /vendor >/dev/null 2>&1 108 | mount -o remount,rw /system 109 | mount -o remount,rw /vendor >/dev/null 2>&1 110 | if [ ! -f '/system/build.prop' ]; then 111 | echo "! Failed: /system could not be mounted!" 112 | exit 1 113 | fi 114 | 115 | echo "- Checking environment" 116 | API=$(grep_prop ro.build.version.sdk) 117 | APINAME=$(android_version $API) 118 | ABI=$(grep_prop ro.product.cpu.abi | cut -c-3) 119 | ABI2=$(grep_prop ro.product.cpu.abi2 | cut -c-3) 120 | ABILONG=$(grep_prop ro.product.cpu.abi) 121 | 122 | XVERSION=$(grep_prop version system/xposed.prop) 123 | XARCH=$(grep_prop arch system/xposed.prop) 124 | XMINSDK=$(grep_prop minsdk system/xposed.prop) 125 | XMAXSDK=$(grep_prop maxsdk system/xposed.prop) 126 | 127 | XEXPECTEDSDK=$(android_version $XMINSDK) 128 | if [ "$XMINSDK" != "$XMAXSDK" ]; then 129 | XEXPECTEDSDK=$XEXPECTEDSDK' - '$(android_version $XMAXSDK) 130 | fi 131 | 132 | ARCH=arm 133 | IS64BIT= 134 | if [ "$ABI" = "x86" ]; then ARCH=x86; fi; 135 | if [ "$ABI2" = "x86" ]; then ARCH=x86; fi; 136 | if [ "$API" -ge "21" ]; then 137 | if [ "$ABILONG" = "arm64-v8a" ]; then ARCH=arm64; IS64BIT=1; fi; 138 | if [ "$ABILONG" = "x86_64" ]; then ARCH=x64; IS64BIT=1; fi; 139 | fi 140 | 141 | # echo "DBG [$API] [$ABI] [$ABI2] [$ABILONG] [$ARCH] [$XARCH] [$XMINSDK] [$XMAXSDK] [$XVERSION]" 142 | 143 | echo " Xposed version: $XVERSION" 144 | 145 | XVALID= 146 | if [ "$ARCH" = "$XARCH" ]; then 147 | if [ "$API" -ge "$XMINSDK" ]; then 148 | if [ "$API" -le "$XMAXSDK" ]; then 149 | XVALID=1 150 | else 151 | echo "! Wrong Android version: $APINAME" 152 | echo "! This file is for: $XEXPECTEDSDK" 153 | fi 154 | else 155 | echo "! Wrong Android version: $APINAME" 156 | echo "! This file is for: $XEXPECTEDSDK" 157 | fi 158 | else 159 | echo "! Wrong platform: $ARCH" 160 | echo "! This file is for: $XARCH" 161 | fi 162 | 163 | if [ -z $XVALID ]; then 164 | echo "! Please download the correct package" 165 | echo "! for your platform/ROM!" 166 | exit 1 167 | fi 168 | 169 | echo "- Placing files" 170 | install_nobackup /system/xposed.prop 0 0 0644 171 | install_nobackup /system/framework/XposedBridge.jar 0 0 0644 172 | 173 | install_and_link /system/bin/app_process32 0 2000 0755 u:object_r:zygote_exec:s0 174 | install_overwrite /system/bin/dex2oat 0 2000 0755 u:object_r:dex2oat_exec:s0 175 | install_overwrite /system/bin/oatdump 0 2000 0755 176 | install_overwrite /system/bin/patchoat 0 2000 0755 u:object_r:dex2oat_exec:s0 177 | install_overwrite /system/lib/libart.so 0 0 0644 178 | install_overwrite /system/lib/libart-compiler.so 0 0 0644 179 | install_overwrite /system/lib/libart-disassembler.so 0 0 0644 180 | install_overwrite /system/lib/libsigchain.so 0 0 0644 181 | install_nobackup /system/lib/libxposed_art.so 0 0 0644 182 | if [ $IS64BIT ]; then 183 | install_and_link /system/bin/app_process64 0 2000 0755 u:object_r:zygote_exec:s0 184 | install_overwrite /system/lib64/libart.so 0 0 0644 185 | install_overwrite /system/lib64/libart-compiler.so 0 0 0644 186 | install_overwrite /system/lib64/libart-disassembler.so 0 0 0644 187 | install_overwrite /system/lib64/libsigchain.so 0 0 0644 188 | install_nobackup /system/lib64/libxposed_art.so 0 0 0644 189 | fi 190 | 191 | if [ "$API" -ge "22" ]; then 192 | find /system /vendor -type f -name '*.odex.gz' 2>/dev/null | while read f; do mv "$f" "$f.xposed"; done 193 | fi 194 | 195 | echo "- Done" 196 | exit 0 197 | -------------------------------------------------------------------------------- /zipstatic/_all/META-INF/com/google/android/updater-script: -------------------------------------------------------------------------------- 1 | # this is a dummy file, the magic is in update-binary and flash-script.sh -------------------------------------------------------------------------------- /zipstatic/_uninstaller/META-INF/com/google/android/flash-script.sh: -------------------------------------------------------------------------------- 1 | ########################################################################################## 2 | # 3 | # Xposed framework uninstaller zip. 4 | # 5 | # This script removes the Xposed framework files from the system partition. 6 | # It doesn't touch the Xposed Installer app. 7 | # 8 | ########################################################################################## 9 | 10 | grep_prop() { 11 | REGEX="s/^$1=//p" 12 | shift 13 | FILES=$@ 14 | if [ -z "$FILES" ]; then 15 | FILES='/system/build.prop' 16 | fi 17 | cat $FILES 2>/dev/null | sed -n $REGEX | head -n 1 18 | } 19 | 20 | mv_perm() { 21 | mv -f $1 $2 || exit 1 22 | set_perm $2 $3 $4 $5 $6 23 | } 24 | 25 | set_perm() { 26 | chown $2:$3 $1 || exit 1 27 | chmod $4 $1 || exit 1 28 | if [ "$5" ]; then 29 | chcon $5 $1 2>/dev/null 30 | else 31 | chcon 'u:object_r:system_file:s0' $1 2>/dev/null 32 | fi 33 | } 34 | 35 | restore_link() { 36 | TARGET=$1 37 | XPOSED="${1}_xposed" 38 | BACKUP="${1}_original" 39 | # Don't touch $TARGET if the link was created by something else (e.g. SuperSU) 40 | if [ -f $BACKUP -a -L $TARGET -a "$(readlink $TARGET)" = $XPOSED ]; then 41 | rm -f $TARGET 42 | mv_perm $BACKUP $TARGET $2 $3 $4 $5 43 | fi 44 | rm -f $XPOSED 45 | } 46 | 47 | restore_backup() { 48 | TARGET=$1 49 | BACKUP="${1}.orig" 50 | NO_ORIG="${1}.no_orig" 51 | if [ -f $BACKUP ]; then 52 | mv_perm $BACKUP $TARGET $2 $3 $4 $5 53 | rm -f $NO_ORIG 54 | elif [ -f "${BACKUP}.gz" ]; then 55 | rm -f $TARGET $NO_ORIG 56 | gunzip "${BACKUP}.gz" 57 | mv_perm $BACKUP $TARGET $2 $3 $4 $5 58 | elif [ -f $NO_ORIG ]; then 59 | rm -f $TARGET $NO_ORIG 60 | fi 61 | } 62 | 63 | ########################################################################################## 64 | 65 | echo "********************************" 66 | echo "Xposed framework uninstaller zip" 67 | echo "********************************" 68 | 69 | echo "- Mounting /system and /vendor read-write" 70 | mount /system >/dev/null 2>&1 71 | mount /vendor >/dev/null 2>&1 72 | mount -o remount,rw /system 73 | mount -o remount,rw /vendor >/dev/null 2>&1 74 | if [ ! -f '/system/build.prop' ]; then 75 | echo "! Failed: /system could not be mounted!" 76 | exit 1 77 | fi 78 | 79 | echo "- Checking environment" 80 | API=$(grep_prop ro.build.version.sdk) 81 | ABI=$(grep_prop ro.product.cpu.abi | cut -c-3) 82 | ABI2=$(grep_prop ro.product.cpu.abi2 | cut -c-3) 83 | ABILONG=$(grep_prop ro.product.cpu.abi) 84 | 85 | ARCH=arm 86 | IS64BIT= 87 | if [ "$ABI" = "x86" ]; then ARCH=x86; fi; 88 | if [ "$ABI2" = "x86" ]; then ARCH=x86; fi; 89 | if [ "$API" -ge "21" ]; then 90 | if [ "$ABILONG" = "arm64-v8a" ]; then ARCH=arm64; IS64BIT=1; fi; 91 | if [ "$ABILONG" = "x86_64" ]; then ARCH=x64; IS64BIT=1; fi; 92 | else 93 | echo "! This script doesn't work for SDK < 21 (yet)" 94 | exit 1 95 | fi 96 | 97 | # echo "DBG [$API] [$ABI] [$ABI2] [$ABILONG] [$ARCH]" 98 | 99 | echo "- Restoring/removing files" 100 | rm -f /system/xposed.prop 101 | rm -f /system/framework/XposedBridge.jar 102 | 103 | restore_link /system/bin/app_process32 0 2000 0755 u:object_r:zygote_exec:s0 104 | restore_backup /system/bin/dex2oat 0 2000 0755 u:object_r:dex2oat_exec:s0 105 | restore_backup /system/bin/oatdump 0 2000 0755 106 | restore_backup /system/bin/patchoat 0 2000 0755 u:object_r:dex2oat_exec:s0 107 | restore_backup /system/lib/libart.so 0 0 0644 108 | restore_backup /system/lib/libart-compiler.so 0 0 0644 109 | restore_backup /system/lib/libart-disassembler.so 0 0 0644 110 | restore_backup /system/lib/libsigchain.so 0 0 0644 111 | rm -f /system/lib/libxposed_art.so 112 | rm -f /system/lib/libxposed_art.so.no_orig 113 | if [ $IS64BIT ]; then 114 | restore_link /system/bin/app_process64 0 2000 0755 u:object_r:zygote_exec:s0 115 | restore_backup /system/lib64/libart.so 0 0 0644 116 | restore_backup /system/lib64/libart-compiler.so 0 0 0644 117 | restore_backup /system/lib64/libart-disassembler.so 0 0 0644 118 | restore_backup /system/lib64/libsigchain.so 0 0 0644 119 | rm -f /system/lib64/libxposed_art.so 120 | rm -f /system/lib64/libxposed_art.so.no_orig 121 | fi 122 | 123 | if [ "$API" -ge "22" ]; then 124 | find /system /vendor -type f -name '*.odex.gz.xposed' 2>/dev/null | while read f; do mv "$f" "${f%.xposed}"; done 125 | fi 126 | 127 | echo "- Done" 128 | echo 129 | echo "It's recommended that you wipe the" 130 | echo "Dalvik cache now." 131 | 132 | exit 0 133 | -------------------------------------------------------------------------------- /zipstatic/_uninstaller/META-INF/com/google/android/updater-script: -------------------------------------------------------------------------------- 1 | # this is a dummy file, the magic is in update-binary and flash-script.sh -------------------------------------------------------------------------------- /zipstatic/arm/META-INF/com/google/android/update-binary: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rovo89/XposedTools/78173acfbf7fe2ef46e4ee4d0dcbad15bb45b05f/zipstatic/arm/META-INF/com/google/android/update-binary -------------------------------------------------------------------------------- /zipstatic/arm64: -------------------------------------------------------------------------------- 1 | arm -------------------------------------------------------------------------------- /zipstatic/armv5/META-INF/com/google/android/update-binary: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rovo89/XposedTools/78173acfbf7fe2ef46e4ee4d0dcbad15bb45b05f/zipstatic/armv5/META-INF/com/google/android/update-binary -------------------------------------------------------------------------------- /zipstatic/x86/META-INF/com/google/android/genymotion-ready: -------------------------------------------------------------------------------- 1 | In order to flash this archive on the Genymotion emulator, please see: 2 | https://github.com/rovo89/GenyFlash 3 | -------------------------------------------------------------------------------- /zipstatic/x86/META-INF/com/google/android/update-binary: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rovo89/XposedTools/78173acfbf7fe2ef46e4ee4d0dcbad15bb45b05f/zipstatic/x86/META-INF/com/google/android/update-binary --------------------------------------------------------------------------------