├── .gitattributes
├── .github
├── dependabot.yml
└── workflows
│ └── build.yml
├── .gitignore
├── LICENSE
├── README.md
├── app-process
├── build.gradle
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── aidl
│ └── com
│ │ └── rosan
│ │ └── app_process
│ │ ├── INewProcess.aidl
│ │ └── IRemoteProcess.aidl
│ └── java
│ └── com
│ └── rosan
│ └── app_process
│ ├── AppProcess.java
│ ├── BinderWrapper.java
│ ├── NewProcess.java
│ ├── NewProcessImpl.java
│ ├── NewProcessReceiver.java
│ ├── RemoteProcess.java
│ └── RemoteProcessImpl.java
├── app
├── build.gradle
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── rosan
│ │ └── app_process
│ │ └── demo
│ │ └── MainActivity.java
│ └── res
│ └── layout
│ └── activity_main.xml
├── build.gradle
├── gradle.properties
├── gradle
├── libs.versions.toml
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── hidden-api
├── build.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ └── java
│ └── android
│ ├── app
│ ├── ActivityManagerNative.java
│ ├── ActivityThread.java
│ ├── ContentProviderHolder.java
│ ├── ContextImpl.java
│ ├── IActivityManager.java
│ └── LoadedApk.java
│ ├── content
│ └── IContentProvider.java
│ └── os
│ └── ServiceManager.java
└── settings.gradle
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
2 | *.bat text eol=crlf
3 | *.jar binary
4 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: gradle
4 | directory: "/"
5 | schedule:
6 | interval: daily
7 | time: "05:00"
8 | target-branch: main
9 | registries:
10 | - maven-google
11 | - maven-central
12 | - gradle-plugin
13 | groups:
14 | maven-dependencies:
15 | patterns:
16 | - "*"
17 |
18 | registries:
19 | maven-google:
20 | type: maven-repository
21 | url: "https://dl.google.com/dl/android/maven2/"
22 | maven-central:
23 | type: maven-repository
24 | url: "https://repo1.maven.org/maven2/"
25 | gradle-plugin:
26 | type: maven-repository
27 | url: "https://plugins.gradle.org/m2/"
28 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: Build
2 |
3 | on:
4 | push:
5 | paths-ignore:
6 | - '*.md'
7 | pull_request:
8 | workflow_dispatch:
9 |
10 | jobs:
11 | build:
12 | name: Build
13 | runs-on: ubuntu-24.04
14 |
15 | steps:
16 | - name: Checkout
17 | uses: actions/checkout@v4
18 |
19 | - name: Setup Java
20 | uses: actions/setup-java@v4
21 | with:
22 | distribution: 'temurin'
23 | java-version: 21
24 |
25 | - name: Setup Gradle
26 | uses: gradle/actions/setup-gradle@v4
27 |
28 | - name: Set environment
29 | run: |
30 | {
31 | echo "repo=$(echo ${GITHUB_REPOSITORY#$GITHUB_REPOSITORY_OWNER/})"
32 | echo "version=v$(grep artifactVersion build.gradle| awk -F\" '{print $2}')"
33 | echo "commit=$(echo ${{ github.sha }} | cut -c-7)"
34 | } >> $GITHUB_ENV
35 |
36 | - name: Build
37 | run: |
38 | ./gradlew --no-daemon --warning-mode=all assembleDebug
39 | cp app-process/build/outputs/aar/app-process-debug.aar AndroidAppProcess-${{ env.version }}@${{ env.commit }}.aar
40 | cp app/build/outputs/apk/debug/app-debug.apk AppProcessDemo@${{ env.commit }}.apk
41 |
42 | - name: Upload
43 | uses: actions/upload-artifact@v4
44 | with:
45 | name: "${{ env.repo }}-${{ env.version }}@${{ env.commit }}"
46 | path: |
47 | AndroidAppProcess-${{ env.version }}@${{ env.commit }}.aar
48 | AppProcessDemo@${{ env.commit }}.apk
49 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Gradle
2 | /.gradle/
3 | # Android SDK
4 | /local.properties
5 | # Gradle Build
6 | /*/build/
7 | # AGP Lint
8 | /build/
9 | # IntelliJ
10 | /.idea/
11 | # VS Code
12 | /.vscode/
13 | # Keystore
14 | /keystore/
15 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU LESSER GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 |
9 | This version of the GNU Lesser General Public License incorporates
10 | the terms and conditions of version 3 of the GNU General Public
11 | License, supplemented by the additional permissions listed below.
12 |
13 | 0. Additional Definitions.
14 |
15 | As used herein, "this License" refers to version 3 of the GNU Lesser
16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU
17 | General Public License.
18 |
19 | "The Library" refers to a covered work governed by this License,
20 | other than an Application or a Combined Work as defined below.
21 |
22 | An "Application" is any work that makes use of an interface provided
23 | by the Library, but which is not otherwise based on the Library.
24 | Defining a subclass of a class defined by the Library is deemed a mode
25 | of using an interface provided by the Library.
26 |
27 | A "Combined Work" is a work produced by combining or linking an
28 | Application with the Library. The particular version of the Library
29 | with which the Combined Work was made is also called the "Linked
30 | Version".
31 |
32 | The "Minimal Corresponding Source" for a Combined Work means the
33 | Corresponding Source for the Combined Work, excluding any source code
34 | for portions of the Combined Work that, considered in isolation, are
35 | based on the Application, and not on the Linked Version.
36 |
37 | The "Corresponding Application Code" for a Combined Work means the
38 | object code and/or source code for the Application, including any data
39 | and utility programs needed for reproducing the Combined Work from the
40 | Application, but excluding the System Libraries of the Combined Work.
41 |
42 | 1. Exception to Section 3 of the GNU GPL.
43 |
44 | You may convey a covered work under sections 3 and 4 of this License
45 | without being bound by section 3 of the GNU GPL.
46 |
47 | 2. Conveying Modified Versions.
48 |
49 | If you modify a copy of the Library, and, in your modifications, a
50 | facility refers to a function or data to be supplied by an Application
51 | that uses the facility (other than as an argument passed when the
52 | facility is invoked), then you may convey a copy of the modified
53 | version:
54 |
55 | a) under this License, provided that you make a good faith effort to
56 | ensure that, in the event an Application does not supply the
57 | function or data, the facility still operates, and performs
58 | whatever part of its purpose remains meaningful, or
59 |
60 | b) under the GNU GPL, with none of the additional permissions of
61 | this License applicable to that copy.
62 |
63 | 3. Object Code Incorporating Material from Library Header Files.
64 |
65 | The object code form of an Application may incorporate material from
66 | a header file that is part of the Library. You may convey such object
67 | code under terms of your choice, provided that, if the incorporated
68 | material is not limited to numerical parameters, data structure
69 | layouts and accessors, or small macros, inline functions and templates
70 | (ten or fewer lines in length), you do both of the following:
71 |
72 | a) Give prominent notice with each copy of the object code that the
73 | Library is used in it and that the Library and its use are
74 | covered by this License.
75 |
76 | b) Accompany the object code with a copy of the GNU GPL and this license
77 | document.
78 |
79 | 4. Combined Works.
80 |
81 | You may convey a Combined Work under terms of your choice that,
82 | taken together, effectively do not restrict modification of the
83 | portions of the Library contained in the Combined Work and reverse
84 | engineering for debugging such modifications, if you also do each of
85 | the following:
86 |
87 | a) Give prominent notice with each copy of the Combined Work that
88 | the Library is used in it and that the Library and its use are
89 | covered by this License.
90 |
91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license
92 | document.
93 |
94 | c) For a Combined Work that displays copyright notices during
95 | execution, include the copyright notice for the Library among
96 | these notices, as well as a reference directing the user to the
97 | copies of the GNU GPL and this license document.
98 |
99 | d) Do one of the following:
100 |
101 | 0) Convey the Minimal Corresponding Source under the terms of this
102 | License, and the Corresponding Application Code in a form
103 | suitable for, and under terms that permit, the user to
104 | recombine or relink the Application with a modified version of
105 | the Linked Version to produce a modified Combined Work, in the
106 | manner specified by section 6 of the GNU GPL for conveying
107 | Corresponding Source.
108 |
109 | 1) Use a suitable shared library mechanism for linking with the
110 | Library. A suitable mechanism is one that (a) uses at run time
111 | a copy of the Library already present on the user's computer
112 | system, and (b) will operate properly with a modified version
113 | of the Library that is interface-compatible with the Linked
114 | Version.
115 |
116 | e) Provide Installation Information, but only if you would otherwise
117 | be required to provide such information under section 6 of the
118 | GNU GPL, and only to the extent that such information is
119 | necessary to install and execute a modified version of the
120 | Combined Work produced by recombining or relinking the
121 | Application with a modified version of the Linked Version. (If
122 | you use option 4d0, the Installation Information must accompany
123 | the Minimal Corresponding Source and Corresponding Application
124 | Code. If you use option 4d1, you must provide the Installation
125 | Information in the manner specified by section 6 of the GNU GPL
126 | for conveying Corresponding Source.)
127 |
128 | 5. Combined Libraries.
129 |
130 | You may place library facilities that are a work based on the
131 | Library side by side in a single library together with other library
132 | facilities that are not Applications and are not covered by this
133 | License, and convey such a combined library under terms of your
134 | choice, if you do both of the following:
135 |
136 | a) Accompany the combined library with a copy of the same work based
137 | on the Library, uncombined with any other library facilities,
138 | conveyed under the terms of this License.
139 |
140 | b) Give prominent notice with the combined library that part of it
141 | is a work based on the Library, and explaining where to find the
142 | accompanying uncombined form of the same work.
143 |
144 | 6. Revised Versions of the GNU Lesser General Public License.
145 |
146 | The Free Software Foundation may publish revised and/or new versions
147 | of the GNU Lesser General Public License from time to time. Such new
148 | versions will be similar in spirit to the present version, but may
149 | differ in detail to address new problems or concerns.
150 |
151 | Each version is given a distinguishing version number. If the
152 | Library as you received it specifies that a certain numbered version
153 | of the GNU Lesser General Public License "or any later version"
154 | applies to it, you have the option of following the terms and
155 | conditions either of that published version or of any later version
156 | published by the Free Software Foundation. If the Library as you
157 | received it does not specify a version number of the GNU Lesser
158 | General Public License, you may choose any version of the GNU Lesser
159 | General Public License ever published by the Free Software Foundation.
160 |
161 | If the Library as you received it specifies that a proxy can decide
162 | whether future versions of the GNU Lesser General Public License shall
163 | apply, that proxy's public statement of acceptance of any version is
164 | permanent authorization for you to choose that version for the
165 | Library.
166 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Android AppProcess
2 |
3 | Encapsulation of Android app_process, you can use it just like Android Service.
4 |
5 | 
6 |
7 | ### How to use it
8 |
9 | ### Import library
10 |
11 | ```Groovy
12 | implementation 'io.github.iamr0s:AndroidAppProcess:'
13 | ```
14 |
15 | ### 1. New Process
16 |
17 | - Default
18 |
19 | ```java
20 | AppProcess process = new AppProcess.Default();
21 | process.init(context.getPackageName());
22 | ```
23 |
24 | - Root
25 |
26 | ```java
27 | AppProcess process = new AppProcess.Root();
28 | process.init(context.getPackageName());
29 | ```
30 |
31 | ### 2. Use it.
32 |
33 | - Remote Binder Transact
34 |
35 | ```java
36 | AppProcess process = new AppProcess.Root();
37 | process.init(context.getPackageName());
38 |
39 | IPackageManager manager = android.os.ServiceManager.getService("package");
40 | IBinder binderWrapper = process.binderWrapper(manager.asBinder());
41 | IPackageManager managerWrapper = IPackageManager.Stub.asInterface(binderWrapper);
42 |
43 | managerWrapper.uninstall(...) // will call it in root.
44 | ```
45 |
46 | - More
47 |
48 | > See the demo.
49 |
50 | ### 3. Close
51 |
52 | > You must close the AppProcess after use it.
53 |
54 | ```java
55 | process.close()
56 | ```
57 |
--------------------------------------------------------------------------------
/app-process/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias(libs.plugins.agp.lib)
3 | id "maven-publish"
4 | id "signing"
5 | }
6 |
7 | def ossrhUsername = ''
8 | def ossrhPassword = ''
9 | def extGradlePath = rootDir.path + "/keystore/r0s.gradle"
10 | if (file(extGradlePath).exists()) {
11 | apply from: extGradlePath
12 | }
13 |
14 | android {
15 | namespace = "com.rosan.app_process"
16 | compileSdk = 35
17 |
18 | defaultConfig {
19 | minSdk = 19
20 | }
21 |
22 | buildFeatures {
23 | buildConfig = true
24 | aidl = true
25 | }
26 |
27 | compileOptions {
28 | sourceCompatibility JavaVersion.VERSION_21
29 | targetCompatibility JavaVersion.VERSION_21
30 | }
31 |
32 | publishing {
33 | singleVariant("release") {
34 | withSourcesJar()
35 | withJavadocJar()
36 | }
37 | }
38 | }
39 |
40 | dependencies {
41 | compileOnly project(":hidden-api")
42 | compileOnly(libs.annotation)
43 |
44 | implementation(libs.commons.cli)
45 | implementation(libs.hiddenapibypass)
46 | }
47 |
48 | tasks.withType(JavaCompile).configureEach {
49 | options.compilerArgs << "-Xlint:deprecation" << "-Xlint:unchecked"
50 | }
51 |
52 | publishing {
53 | publications {
54 | mavenJava(MavenPublication) {
55 | group = rootProject.groupId
56 | artifactId = rootProject.artifactId
57 | version = rootProject.artifactVersion
58 | afterEvaluate {
59 | from components.findByName("release")
60 | }
61 | pom {
62 | name = rootProject.POM_NAME
63 | description = rootProject.POM_DESCRIPTION
64 | url = rootProject.POM_URL
65 | licenses {
66 | license {
67 | name = rootProject.POM_LICENCE_NAME
68 | url = rootProject.POM_LICENCE_URL
69 | }
70 | }
71 | developers {
72 | developer {
73 | name = rootProject.POM_DEVELOPER_NAME
74 | url = rootProject.POM_DEVELOPER_URL
75 | }
76 | }
77 | scm {
78 | connection = rootProject.POM_SCM_CONNECTION
79 | url = rootProject.POM_SCM_URL
80 | }
81 | }
82 | }
83 | }
84 | repositories {
85 | maven {
86 | name = "ossrh"
87 | url = "https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/"
88 | credentials {
89 | username = ossrhUsername
90 | password = ossrhPassword
91 | }
92 | }
93 | }
94 | }
95 |
96 | signing {
97 | sign publishing.publications.mavenJava
98 | }
99 |
--------------------------------------------------------------------------------
/app-process/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # Uncomment this to preserve the line number information for
9 | # debugging stack traces.
10 | -keepattributes SourceFile,LineNumberTable
11 |
12 | # If you keep the line number information, uncomment this to
13 | # hide the original source file name.
14 | -renamesourcefileattribute SourceFile
15 |
--------------------------------------------------------------------------------
/app-process/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app-process/src/main/aidl/com/rosan/app_process/INewProcess.aidl:
--------------------------------------------------------------------------------
1 | package com.rosan.app_process;
2 |
3 | import com.rosan.app_process.IRemoteProcess;
4 |
5 | interface INewProcess {
6 | void exit(int code) = 1;
7 |
8 | // remote binder transact: 2
9 |
10 | IRemoteProcess remoteProcess(in List cmdList, in Map env, in String directory) = 3;
11 | }
--------------------------------------------------------------------------------
/app-process/src/main/aidl/com/rosan/app_process/IRemoteProcess.aidl:
--------------------------------------------------------------------------------
1 | package com.rosan.app_process;
2 |
3 | interface IRemoteProcess {
4 | ParcelFileDescriptor getOutputStream();
5 |
6 | ParcelFileDescriptor getInputStream();
7 |
8 | ParcelFileDescriptor getErrorStream();
9 |
10 | int exitValue();
11 |
12 | void destroy();
13 |
14 | int waitFor();
15 | }
--------------------------------------------------------------------------------
/app-process/src/main/java/com/rosan/app_process/AppProcess.java:
--------------------------------------------------------------------------------
1 | package com.rosan.app_process;
2 |
3 | import android.app.ActivityThread;
4 | import android.content.ComponentName;
5 | import android.content.Context;
6 | import android.os.Binder;
7 | import android.os.IBinder;
8 | import android.os.Parcel;
9 | import android.os.RemoteException;
10 |
11 | import androidx.annotation.NonNull;
12 | import androidx.annotation.Nullable;
13 |
14 | import java.io.Closeable;
15 | import java.io.File;
16 | import java.io.IOException;
17 | import java.io.PrintWriter;
18 | import java.util.ArrayList;
19 | import java.util.Arrays;
20 | import java.util.HashMap;
21 | import java.util.List;
22 | import java.util.Map;
23 | import java.util.concurrent.Callable;
24 |
25 | public abstract class AppProcess implements Closeable {
26 | private Context mContext = null;
27 |
28 | private INewProcess mNewProcess = null;
29 |
30 | private final Map mChildProcess = new HashMap<>();
31 |
32 | public static ProcessParams generateProcessParams(@NonNull String classPath, @NonNull String entryClassName, @NonNull List args) {
33 | List cmdList = new ArrayList<>();
34 | cmdList.add("/system/bin/app_process");
35 | cmdList.add("-Djava.class.path=" + classPath);
36 | cmdList.add("/system/bin");
37 | cmdList.add(entryClassName);
38 | cmdList.addAll(args);
39 | return new ProcessParams(cmdList, null, null);
40 | }
41 |
42 | public static T binderWithCleanCallingIdentity(Callable action) throws Exception {
43 | final long callingIdentity = Binder.clearCallingIdentity();
44 | try {
45 | return action.call();
46 | } finally {
47 | Binder.restoreCallingIdentity(callingIdentity);
48 | }
49 | }
50 |
51 | public static IBinder binderWrapper(INewProcess newProcess, IBinder binder) {
52 | return new BinderWrapper(newProcess, binder);
53 | }
54 |
55 | public static boolean remoteTransact(INewProcess newProcess, IBinder binder, int code, Parcel data, Parcel reply, int flags) {
56 | IBinder processBinder = newProcess.asBinder();
57 | Parcel processData = Parcel.obtain();
58 | try {
59 | processData.writeInterfaceToken(processBinder.getInterfaceDescriptor());
60 | processData.writeStrongBinder(binder);
61 | processData.writeInt(code);
62 | processData.writeInt(flags);
63 | processData.appendFrom(data, 0, data.dataSize());
64 | return processBinder.transact(IBinder.FIRST_CALL_TRANSACTION + 2, processData, reply, 0);
65 | } catch (RemoteException e) {
66 | throw new RuntimeException(e);
67 | } finally {
68 | processData.recycle();
69 | }
70 | }
71 |
72 | public @NonNull Process start(@NonNull String classPath, @NonNull String entryClassName, @NonNull List args) throws IOException {
73 | return startProcess(generateProcessParams(classPath, entryClassName, args));
74 | }
75 |
76 | public @NonNull Process start(@NonNull String classPath, @NonNull String entryClassName, @NonNull String[] args) throws IOException {
77 | return start(classPath, entryClassName, Arrays.asList(args));
78 | }
79 |
80 | public @NonNull Process start(@NonNull String classPath, @NonNull Class entryClass, @NonNull List args) throws IOException {
81 | return start(classPath, entryClass.getName(), args);
82 | }
83 |
84 | public @NonNull Process start(@NonNull String classPath, @NonNull Class entryClass, @NonNull String[] args) throws IOException {
85 | return start(classPath, entryClass, Arrays.asList(args));
86 | }
87 |
88 | public boolean init() {
89 | return init(ActivityThread.currentActivityThread().getApplication());
90 | }
91 |
92 | public synchronized boolean init(@NonNull Context context) {
93 | if (initialized()) return true;
94 | mContext = context;
95 | IBinder binder = startProcess(new ComponentName(mContext.getPackageName(), NewProcessImpl.class.getName()));
96 | if (binder == null) return false;
97 | mNewProcess = INewProcess.Stub.asInterface(binder);
98 | try {
99 | binder.linkToDeath(() -> {
100 | if (mNewProcess == null || mNewProcess.asBinder() != binder) return;
101 | mNewProcess = null;
102 | }, 0);
103 | } catch (RemoteException e) {
104 | e.printStackTrace();
105 | }
106 | return initialized();
107 | }
108 |
109 | public boolean initialized() {
110 | return mContext != null && mNewProcess != null && mNewProcess.asBinder().isBinderAlive();
111 | }
112 |
113 | @Override
114 | public void close() {
115 | mContext = null;
116 | if (mNewProcess == null || !mNewProcess.asBinder().pingBinder()) return;
117 | try {
118 | mNewProcess.exit(0);
119 | } catch (RemoteException e) {
120 | e.printStackTrace();
121 | }
122 | }
123 |
124 | protected abstract @NonNull Process newProcess(@NonNull ProcessParams params) throws IOException;
125 |
126 | private @NonNull Process startProcess(@NonNull ProcessParams params) throws IOException {
127 | if (!initialized()) return newProcess(params);
128 | return remoteProcess(params.getCmdList(), params.getEnv(), params.getDirectory());
129 | }
130 |
131 | private @NonNull INewProcess requireNewProcess() {
132 | if (!initialized()) throw new IllegalStateException("please call init() first.");
133 | return mNewProcess;
134 | }
135 |
136 | public boolean remoteTransact(IBinder binder, int code, Parcel data, Parcel reply, int flags) {
137 | return remoteTransact(requireNewProcess(), binder, code, data, reply, flags);
138 | }
139 |
140 | public IBinder binderWrapper(IBinder binder) {
141 | return binderWrapper(requireNewProcess(), binder);
142 | }
143 |
144 | public Process remoteProcess(@NonNull List cmdList, @Nullable Map env, @Nullable String directory) {
145 | try {
146 | return new RemoteProcess(requireNewProcess().remoteProcess(cmdList, env, directory));
147 | } catch (RemoteException e) {
148 | throw new RuntimeException(e);
149 | }
150 | }
151 |
152 | private final Map locks = new HashMap<>();
153 |
154 | private synchronized Object buildLock(String token) {
155 | Object lock = locks.get(token);
156 | if (lock == null) lock = new Object();
157 | locks.put(token, lock);
158 | return lock;
159 | }
160 |
161 | public IBinder startProcess(@NonNull ComponentName componentName, boolean useCache) {
162 | if (!useCache) startProcessUnchecked(componentName);
163 | return startProcess(componentName);
164 | }
165 |
166 | public IBinder startProcess(@NonNull ComponentName componentName) {
167 | String token = componentName.flattenToString();
168 | synchronized (buildLock(token)) {
169 | IBinder existsBinder = mChildProcess.get(token);
170 | if (existsBinder != null) return existsBinder;
171 | final IBinder binder = startProcessUnchecked(componentName);
172 | if (binder == null) return null;
173 | mChildProcess.put(token, binder);
174 | try {
175 | binder.linkToDeath(() -> {
176 | IBinder curBinder = mChildProcess.get(token);
177 | if (curBinder == null || curBinder != binder) return;
178 | mChildProcess.remove(token);
179 | }, 0);
180 | } catch (RemoteException e) {
181 | e.printStackTrace();
182 | }
183 | return binder;
184 | }
185 | }
186 |
187 | private IBinder startProcessUnchecked(@NonNull ComponentName componentName) {
188 | return NewProcessReceiver.start(mContext, this, componentName);
189 | }
190 |
191 | public static class Default extends AppProcess {
192 | @NonNull
193 | @Override
194 | protected Process newProcess(@NonNull ProcessParams params) throws IOException {
195 | List cmdList = params.getCmdList();
196 | Map env = params.getEnv();
197 | String directory = params.getDirectory();
198 | ProcessBuilder builder = new ProcessBuilder().command(cmdList);
199 | if (directory != null) builder = builder.directory(new File(directory));
200 | if (env != null) builder.environment().putAll(env);
201 | return builder.start();
202 | }
203 | }
204 |
205 | public abstract static class Terminal extends Default {
206 | protected abstract @NonNull List newTerminal();
207 |
208 | @NonNull
209 | @Override
210 | protected Process newProcess(@NonNull ProcessParams params) throws IOException {
211 | ProcessParams newParams = new ProcessParams(params).setCmdList(newTerminal());
212 | Process process = super.newProcess(newParams);
213 | PrintWriter printWriter = new PrintWriter(process.getOutputStream(), true);
214 | int count = 0;
215 | StringBuilder buffer = new StringBuilder();
216 | for (String element : params.getCmdList()) {
217 | if (++count > 1) buffer.append(" ");
218 | buffer.append(element);
219 | }
220 | printWriter.println(buffer);
221 | printWriter.println("exit $?");
222 | return process;
223 | }
224 | }
225 |
226 | public static class Root extends Terminal {
227 |
228 | @NonNull
229 | @Override
230 | protected List newTerminal() {
231 | List terminal = new ArrayList<>();
232 | terminal.add("su");
233 | return terminal;
234 | }
235 | }
236 |
237 | public static class RootSystem extends Terminal {
238 | @NonNull
239 | @Override
240 | protected List newTerminal() {
241 | List terminal = new ArrayList<>();
242 | terminal.add("su");
243 | terminal.add("1000");
244 | return terminal;
245 | }
246 | }
247 |
248 | public static class ProcessParams {
249 | private @NonNull List mCmdList;
250 |
251 | private @Nullable Map mEnv;
252 |
253 | private @Nullable String mDirectory;
254 |
255 | public ProcessParams(@NonNull List cmdList, @Nullable Map env, @Nullable String directory) {
256 | this.mCmdList = cmdList;
257 | this.mEnv = env;
258 | this.mDirectory = directory;
259 | }
260 |
261 | public ProcessParams(@NonNull ProcessParams params) {
262 | this.mCmdList = new ArrayList<>(params.getCmdList());
263 | Map env = params.getEnv();
264 | this.mEnv = env != null ? new HashMap<>(env) : null;
265 | this.mDirectory = params.getDirectory();
266 | }
267 |
268 | @NonNull
269 | public List getCmdList() {
270 | return mCmdList;
271 | }
272 |
273 | @Nullable
274 | public Map getEnv() {
275 | return mEnv;
276 | }
277 |
278 | @Nullable
279 | public String getDirectory() {
280 | return mDirectory;
281 | }
282 |
283 | public ProcessParams setCmdList(@NonNull List mCmdList) {
284 | this.mCmdList = mCmdList;
285 | return this;
286 | }
287 |
288 | public ProcessParams setEnv(@Nullable Map mEnv) {
289 | this.mEnv = mEnv;
290 | return this;
291 | }
292 |
293 | public ProcessParams setDirectory(@Nullable String mDirectory) {
294 | this.mDirectory = mDirectory;
295 | return this;
296 | }
297 | }
298 | }
--------------------------------------------------------------------------------
/app-process/src/main/java/com/rosan/app_process/BinderWrapper.java:
--------------------------------------------------------------------------------
1 | package com.rosan.app_process;
2 |
3 | import android.os.IBinder;
4 | import android.os.IInterface;
5 | import android.os.Parcel;
6 | import android.os.RemoteException;
7 |
8 | import androidx.annotation.NonNull;
9 | import androidx.annotation.Nullable;
10 |
11 | import java.io.FileDescriptor;
12 |
13 | class BinderWrapper implements IBinder {
14 | private final INewProcess mNewProcess;
15 |
16 | private final IBinder mBinder;
17 |
18 | BinderWrapper(@NonNull INewProcess newProcess, @NonNull IBinder binder) {
19 | this.mNewProcess = newProcess;
20 | this.mBinder = binder;
21 | }
22 |
23 | @Nullable
24 | @Override
25 | public String getInterfaceDescriptor() throws RemoteException {
26 | return mBinder.getInterfaceDescriptor();
27 | }
28 |
29 | @Override
30 | public boolean pingBinder() {
31 | return mBinder.pingBinder();
32 | }
33 |
34 | @Override
35 | public boolean isBinderAlive() {
36 | return mBinder.isBinderAlive();
37 | }
38 |
39 | @Nullable
40 | @Override
41 | public IInterface queryLocalInterface(@NonNull String descriptor) {
42 | return null;
43 | }
44 |
45 | @Override
46 | public void dump(@NonNull FileDescriptor fd, @Nullable String[] args) throws RemoteException {
47 | mBinder.dump(fd, args);
48 | }
49 |
50 | @Override
51 | public void dumpAsync(@NonNull FileDescriptor fd, @Nullable String[] args) throws RemoteException {
52 | mBinder.dumpAsync(fd, args);
53 | }
54 |
55 | @Override
56 | public boolean transact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {
57 | return AppProcess.remoteTransact(mNewProcess, mBinder, code, data, reply, flags);
58 | }
59 |
60 | @Override
61 | public void linkToDeath(@NonNull DeathRecipient recipient, int flags) throws RemoteException {
62 | mBinder.linkToDeath(recipient, flags);
63 | }
64 |
65 | @Override
66 | public boolean unlinkToDeath(@NonNull DeathRecipient recipient, int flags) {
67 | return mBinder.unlinkToDeath(recipient, flags);
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/app-process/src/main/java/com/rosan/app_process/NewProcess.java:
--------------------------------------------------------------------------------
1 | package com.rosan.app_process;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.app.ActivityThread;
5 | import android.app.LoadedApk;
6 | import android.content.ComponentName;
7 | import android.content.Context;
8 | import android.content.ContextWrapper;
9 | import android.content.Intent;
10 | import android.content.pm.PackageManager;
11 | import android.os.Build;
12 | import android.os.Bundle;
13 | import android.os.IBinder;
14 | import android.os.IInterface;
15 | import android.os.Looper;
16 | import android.os.Process;
17 | import android.util.Log;
18 |
19 | import androidx.annotation.Keep;
20 | import androidx.annotation.NonNull;
21 |
22 | import org.apache.commons.cli.CommandLine;
23 | import org.apache.commons.cli.DefaultParser;
24 | import org.apache.commons.cli.Option;
25 | import org.apache.commons.cli.Options;
26 | import org.lsposed.hiddenapibypass.HiddenApiBypass;
27 |
28 | import java.lang.reflect.Constructor;
29 | import java.lang.reflect.InvocationTargetException;
30 | import java.lang.reflect.Method;
31 | import java.util.Arrays;
32 | import java.util.Collections;
33 | import java.util.List;
34 | import java.util.Objects;
35 |
36 | public class NewProcess {
37 | private static final String TAG = "NewProcess";
38 |
39 | private static ActivityThread mActivityThread = null;
40 |
41 | @Keep
42 | public static void main(String[] args) throws Throwable {
43 | try {
44 | innerMain(args);
45 | } catch (Throwable e) {
46 | e.printStackTrace();
47 | Log.e(TAG, "main", e);
48 | throw e;
49 | }
50 | }
51 |
52 | private static void innerMain(String[] args) throws Throwable {
53 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
54 | HiddenApiBypass.addHiddenApiExemptions("");
55 | }
56 | Options options = new Options().addOption(Option.builder().longOpt("package").hasArg().required().type(String.class).build()).addOption(Option.builder().longOpt("token").hasArg().required().type(String.class).build()).addOption(Option.builder().longOpt("component").hasArg().required().type(String.class).build());
57 | CommandLine cmdLine = new DefaultParser().parse(options, args);
58 | String packageName = cmdLine.getOptionValue("package");
59 | String token = cmdLine.getOptionValue("token");
60 | String component = cmdLine.getOptionValue("component");
61 | ComponentName componentName = ComponentName.unflattenFromString(component);
62 |
63 | Bundle bundle = new Bundle();
64 | IBinder binder = createBinder(componentName);
65 | bundle.putBinder(NewProcessReceiver.EXTRA_NEW_PROCESS, binder);
66 | bundle.putString(NewProcessReceiver.EXTRA_TOKEN, token);
67 | Intent intent = new Intent(NewProcessReceiver.ACTION_SEND_NEW_PROCESS)
68 | .setPackage(packageName)
69 | .putExtras(bundle);
70 | getSystemContext().sendBroadcast(intent);
71 | Looper.loop();
72 | }
73 |
74 | public static IBinder createBinder(ComponentName componentName) throws PackageManager.NameNotFoundException, NoSuchFieldException, NoSuchMethodException, ClassNotFoundException {
75 | Context packageContext = getSystemContext().createPackageContext(componentName.getPackageName(), Context.CONTEXT_IGNORE_SECURITY | Context.CONTEXT_INCLUDE_CODE);
76 | Class> clazz = packageContext.getClassLoader().loadClass(componentName.getClassName());
77 | Constructor> constructor = null;
78 | try {
79 | constructor = clazz.getDeclaredConstructor(Context.class);
80 | } catch (NoSuchMethodException ignored) {
81 | }
82 | Object result;
83 | try {
84 | result = constructor != null ? constructor.newInstance(getUIDContext()) : clazz.newInstance();
85 | } catch (IllegalAccessException | InstantiationException | InvocationTargetException e) {
86 | throw new RuntimeException(e);
87 | }
88 | return ((IInterface) result).asBinder();
89 | }
90 |
91 | public static List getPackagesForUid(Context context, int uid) {
92 | String[] packageNames = context.getPackageManager().getPackagesForUid(uid);
93 | if (packageNames == null) return Collections.emptyList();
94 | return Arrays.asList(packageNames);
95 | }
96 |
97 | private static @NonNull ActivityThread getActivityThread() {
98 | if (mActivityThread != null) return mActivityThread;
99 | if (Looper.getMainLooper() == null) Looper.prepareMainLooper();
100 | if (Looper.myLooper() == null) Looper.prepare();
101 |
102 | mActivityThread = ActivityThread.systemMain();
103 | Objects.requireNonNull(mActivityThread);
104 | return getActivityThread();
105 | }
106 |
107 | private static @NonNull Context getSystemContext() {
108 | return getActivityThread().getSystemContext();
109 | }
110 |
111 | public static Context getUIDContext() throws PackageManager.NameNotFoundException, NoSuchFieldException, InvocationTargetException, NoSuchMethodException, IllegalAccessException {
112 | Context context = getSystemContext();
113 |
114 | int uid = Process.myUid();
115 | List packageNames = getPackagesForUid(context, uid);
116 | if (packageNames.isEmpty()) return context;
117 | if (packageNames.contains(context.getPackageName()) && (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q || packageNames.contains(context.getOpPackageName())))
118 | return context;
119 |
120 | return createAppContext(context, packageNames.get(0));
121 | }
122 |
123 | @SuppressLint("PrivateApi")
124 | private static Context createAppContext(Context context, String packageName) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, PackageManager.NameNotFoundException, NoSuchFieldException {
125 | Context impl = context.createPackageContext(packageName, Context.CONTEXT_IGNORE_SECURITY | Context.CONTEXT_INCLUDE_CODE);
126 | while (impl instanceof ContextWrapper) {
127 | impl = ((ContextWrapper) impl).getBaseContext();
128 | }
129 |
130 | Method method = impl.getClass().getDeclaredMethod("createAppContext", ActivityThread.class, LoadedApk.class);
131 | method.setAccessible(true);
132 | return (Context) method.invoke(null, getActivityThread(), getActivityThread().peekPackageInfo(packageName, true));
133 | }
134 | }
--------------------------------------------------------------------------------
/app-process/src/main/java/com/rosan/app_process/NewProcessImpl.java:
--------------------------------------------------------------------------------
1 | package com.rosan.app_process;
2 |
3 | import android.os.Binder;
4 | import android.os.IBinder;
5 | import android.os.Parcel;
6 | import android.os.RemoteException;
7 |
8 | import androidx.annotation.Keep;
9 |
10 | import java.io.File;
11 | import java.io.IOException;
12 | import java.util.List;
13 | import java.util.Map;
14 |
15 | public class NewProcessImpl extends INewProcess.Stub {
16 | @Keep
17 | public NewProcessImpl() {
18 | super();
19 | }
20 |
21 | @Override
22 | public void exit(int code) {
23 | System.exit(code);
24 | }
25 |
26 | private boolean targetTransact(IBinder binder, int code, Parcel data, Parcel reply, int flags) throws RemoteException {
27 | try {
28 | return AppProcess.binderWithCleanCallingIdentity(() -> binder.transact(code, data, reply, flags));
29 | } catch (RemoteException e) {
30 | throw e;
31 | } catch (Exception e) {
32 | throw new RuntimeException(e);
33 | }
34 | }
35 |
36 | @Override
37 | public IRemoteProcess remoteProcess(List cmdList, Map env, String directory) {
38 | ProcessBuilder builder = new ProcessBuilder().command(cmdList);
39 | if (directory != null) builder = builder.directory(new File(directory));
40 | if (env != null) builder.environment().putAll(env);
41 | File file = null;
42 | try {
43 | return new RemoteProcessImpl(builder.start());
44 | } catch (IOException e) {
45 | throw new IllegalStateException(e);
46 | }
47 | }
48 |
49 | @Override
50 | public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
51 | if (code != Binder.FIRST_CALL_TRANSACTION + 2)
52 | return super.onTransact(code, data, reply, flags);
53 | Parcel targetData = Parcel.obtain();
54 | try {
55 | data.enforceInterface(this.asBinder().getInterfaceDescriptor());
56 | IBinder binder = data.readStrongBinder();
57 | int targetCode = data.readInt();
58 | int targetFlags = data.readInt();
59 | targetData.appendFrom(data, data.dataPosition(), data.dataAvail());
60 | return targetTransact(binder, targetCode, targetData, reply, targetFlags);
61 | } finally {
62 | targetData.recycle();
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/app-process/src/main/java/com/rosan/app_process/NewProcessReceiver.java:
--------------------------------------------------------------------------------
1 | package com.rosan.app_process;
2 |
3 | import android.content.BroadcastReceiver;
4 | import android.content.ComponentName;
5 | import android.content.Context;
6 | import android.content.Intent;
7 | import android.content.IntentFilter;
8 | import android.os.Build;
9 | import android.os.Bundle;
10 | import android.os.IBinder;
11 |
12 | import androidx.annotation.NonNull;
13 |
14 | import java.io.IOException;
15 | import java.util.Objects;
16 | import java.util.UUID;
17 | import java.util.concurrent.ExecutionException;
18 | import java.util.concurrent.ExecutorService;
19 | import java.util.concurrent.Executors;
20 | import java.util.concurrent.Future;
21 | import java.util.concurrent.LinkedBlockingQueue;
22 | import java.util.concurrent.TimeUnit;
23 | import java.util.concurrent.atomic.AtomicReference;
24 |
25 | abstract class NewProcessReceiver extends BroadcastReceiver {
26 | private static boolean waitFor(Process process, long timeout, TimeUnit unit) throws InterruptedException {
27 | long startTime = System.nanoTime();
28 | long rem = unit.toNanos(timeout);
29 |
30 | do {
31 | try {
32 | process.exitValue();
33 | return true;
34 | } catch (IllegalArgumentException ex) {
35 | if (rem > 0)
36 | Thread.sleep(
37 | Math.min(TimeUnit.NANOSECONDS.toMillis(rem) + 1, 100));
38 | }
39 | rem = unit.toNanos(timeout) - (System.nanoTime() - startTime);
40 | } while (rem > 0);
41 | return false;
42 | }
43 |
44 | public static IBinder start(Context context, AppProcess appProcess, ComponentName componentName) {
45 | String token = UUID.randomUUID().toString();
46 | IntentFilter filter = new IntentFilter();
47 | filter.addAction(ACTION_SEND_NEW_PROCESS);
48 | LinkedBlockingQueue> queue = new LinkedBlockingQueue<>();
49 | BroadcastReceiver receiver = new NewProcessReceiver() {
50 | @Override
51 | void onReceive(NewProcessResult result) {
52 | if (!result.getToken().equals(token)) return;
53 | queue.offer(new AtomicReference<>(result));
54 | }
55 | };
56 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
57 | context.registerReceiver(receiver, filter, Context.RECEIVER_NOT_EXPORTED);
58 | else context.registerReceiver(receiver, filter);
59 | ExecutorService executorService = Executors.newCachedThreadPool();
60 | Future> future = executorService.submit(queue::take);
61 | try {
62 | Process process = appProcess.start(context.getPackageCodePath(), NewProcess.class, new String[]{
63 | String.format("--package=%s", context.getPackageName()),
64 | String.format("--token=%s", token),
65 | String.format("--component=%s", componentName.flattenToString())
66 | });
67 | executorService.execute(() -> {
68 | try {
69 | waitFor(process, 15, TimeUnit.SECONDS);
70 | } catch (Throwable e) {
71 | e.printStackTrace();
72 | }
73 | queue.offer(new AtomicReference<>());
74 | });
75 | NewProcessResult result = future.get().get();
76 | IBinder binder = result != null ? result.getBinder() : null;
77 | if (binder == null) process.destroy();
78 | return binder;
79 | } catch (IOException | ExecutionException | InterruptedException e) {
80 | e.printStackTrace();
81 | return null;
82 | } finally {
83 | context.unregisterReceiver(receiver);
84 | executorService.shutdown();
85 | }
86 | }
87 |
88 | public static String ACTION_SEND_NEW_PROCESS = "com.rosan.app_process.send.new_process";
89 |
90 | public static String EXTRA_NEW_PROCESS = "new_process";
91 |
92 | public static String EXTRA_TOKEN = "token";
93 |
94 | @Override
95 | public void onReceive(Context context, Intent intent) {
96 | String action = intent.getAction();
97 | Bundle extras = intent.getExtras();
98 | if (extras == null) return;
99 | if (!Objects.equals(action, ACTION_SEND_NEW_PROCESS)) return;
100 | String token = extras.getString(EXTRA_TOKEN);
101 | if (token == null) return;
102 | IBinder binder = extras.getBinder(EXTRA_NEW_PROCESS);
103 | if (binder == null) return;
104 | onReceive(new NewProcessResult(token, binder));
105 | }
106 |
107 | abstract void onReceive(NewProcessResult result);
108 |
109 | static class NewProcessResult {
110 | private final @NonNull String mToken;
111 |
112 | private final @NonNull IBinder mBinder;
113 |
114 | NewProcessResult(@NonNull String token, @NonNull IBinder binder) {
115 | mToken = token;
116 | mBinder = binder;
117 | }
118 |
119 | @NonNull
120 | public String getToken() {
121 | return mToken;
122 | }
123 |
124 | @NonNull
125 | public IBinder getBinder() {
126 | return mBinder;
127 | }
128 | }
129 | }
--------------------------------------------------------------------------------
/app-process/src/main/java/com/rosan/app_process/RemoteProcess.java:
--------------------------------------------------------------------------------
1 | package com.rosan.app_process;
2 |
3 | import android.os.ParcelFileDescriptor;
4 | import android.os.RemoteException;
5 |
6 | import androidx.annotation.NonNull;
7 |
8 | import java.io.InputStream;
9 | import java.io.OutputStream;
10 |
11 | public class RemoteProcess extends Process {
12 | private final @NonNull IRemoteProcess mProcess;
13 |
14 | public RemoteProcess(@NonNull IRemoteProcess process) {
15 | mProcess = process;
16 | }
17 |
18 | @Override
19 | public OutputStream getOutputStream() {
20 | try {
21 | return new ParcelFileDescriptor.AutoCloseOutputStream(mProcess.getOutputStream());
22 | } catch (RemoteException e) {
23 | throw new RuntimeException(e);
24 | }
25 | }
26 |
27 | @Override
28 | public InputStream getInputStream() {
29 | try {
30 | return new ParcelFileDescriptor.AutoCloseInputStream(mProcess.getInputStream());
31 | } catch (RemoteException e) {
32 | throw new RuntimeException(e);
33 | }
34 | }
35 |
36 | @Override
37 | public InputStream getErrorStream() {
38 | try {
39 | return new ParcelFileDescriptor.AutoCloseInputStream(mProcess.getErrorStream());
40 | } catch (RemoteException e) {
41 | throw new RuntimeException(e);
42 | }
43 | }
44 |
45 | @Override
46 | public int waitFor() {
47 | try {
48 | return mProcess.waitFor();
49 | } catch (RemoteException e) {
50 | throw new RuntimeException(e);
51 | }
52 | }
53 |
54 | @Override
55 | public int exitValue() {
56 | try {
57 | return mProcess.exitValue();
58 | } catch (RemoteException e) {
59 | throw new RuntimeException(e);
60 | }
61 | }
62 |
63 | @Override
64 | public void destroy() {
65 | try {
66 | mProcess.destroy();
67 | } catch (RemoteException e) {
68 | throw new RuntimeException(e);
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/app-process/src/main/java/com/rosan/app_process/RemoteProcessImpl.java:
--------------------------------------------------------------------------------
1 | package com.rosan.app_process;
2 |
3 | import android.os.ParcelFileDescriptor;
4 | import android.os.RemoteException;
5 |
6 | import androidx.annotation.NonNull;
7 |
8 | import java.io.IOException;
9 | import java.io.InputStream;
10 | import java.io.OutputStream;
11 |
12 | public class RemoteProcessImpl extends IRemoteProcess.Stub {
13 | private final @NonNull Process mProcess;
14 |
15 | private ParcelFileDescriptor mInputStream;
16 |
17 | private ParcelFileDescriptor mOutputStream;
18 |
19 | private ParcelFileDescriptor mErrorStream;
20 |
21 | public RemoteProcessImpl(@NonNull Process process) {
22 | mProcess = process;
23 | }
24 |
25 | @Override
26 | public ParcelFileDescriptor getOutputStream() {
27 | if (mOutputStream != null) return mOutputStream;
28 | try {
29 | mOutputStream = parcelable(mProcess.getOutputStream());
30 | } catch (IOException e) {
31 | throw new IllegalStateException(e);
32 | }
33 | return mOutputStream;
34 | }
35 |
36 | @Override
37 | public ParcelFileDescriptor getInputStream() throws RemoteException {
38 | if (mInputStream != null) return mInputStream;
39 | try {
40 | mInputStream = parcelable(mProcess.getInputStream());
41 | } catch (IOException e) {
42 | throw new IllegalStateException(e);
43 | }
44 | return mInputStream;
45 | }
46 |
47 | @Override
48 | public ParcelFileDescriptor getErrorStream() {
49 | if (mErrorStream != null) return mErrorStream;
50 | try {
51 | mErrorStream = parcelable(mProcess.getErrorStream());
52 | } catch (IOException e) {
53 | throw new IllegalStateException(e);
54 | }
55 | return mErrorStream;
56 | }
57 |
58 | @Override
59 | public int exitValue() {
60 | return mProcess.exitValue();
61 | }
62 |
63 | @Override
64 | public void destroy() {
65 | mProcess.destroy();
66 | }
67 |
68 | @Override
69 | public int waitFor() {
70 | try {
71 | return mProcess.waitFor();
72 | } catch (InterruptedException e) {
73 | throw new IllegalStateException(e);
74 | }
75 | }
76 |
77 | public static ParcelFileDescriptor parcelable(InputStream inputStream) throws IOException {
78 | ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
79 | transferThread(inputStream, new ParcelFileDescriptor.AutoCloseOutputStream(pipe[1]));
80 | return pipe[0];
81 | }
82 |
83 | public static ParcelFileDescriptor parcelable(OutputStream outputStream) throws IOException {
84 | ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
85 | transferThread(new ParcelFileDescriptor.AutoCloseInputStream(pipe[0]), outputStream);
86 | return pipe[1];
87 | }
88 |
89 | public static void transferThread(InputStream inputStream, OutputStream outputStream) {
90 | new Thread(() -> {
91 | byte[] bytes = new byte[8192];
92 | int len = 0;
93 | try {
94 | while ((len = inputStream.read(bytes)) > 0) {
95 | outputStream.write(bytes, 0, len);
96 | outputStream.flush();
97 | }
98 | } catch (IOException e) {
99 | e.printStackTrace();
100 | } finally {
101 | try {
102 | inputStream.close();
103 | } catch (IOException e) {
104 | e.printStackTrace();
105 | }
106 | try {
107 | outputStream.close();
108 | } catch (IOException e) {
109 | e.printStackTrace();
110 | }
111 | }
112 | }).start();
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias(libs.plugins.agp.app)
3 | }
4 |
5 | android {
6 | namespace = "com.rosan.app_process.demo"
7 | compileSdk = 35
8 |
9 | defaultConfig {
10 | minSdk = 19
11 | targetSdk = 35
12 | versionCode = 1
13 | versionName = "1.0"
14 | multiDexEnabled = false
15 | proguardFiles += "proguard-rules.pro"
16 | }
17 |
18 | buildTypes {
19 | configureEach {
20 | signingConfig = signingConfigs.debug
21 | }
22 | release {
23 | minifyEnabled = true
24 | shrinkResources = true
25 | }
26 | }
27 |
28 | compileOptions {
29 | sourceCompatibility JavaVersion.VERSION_21
30 | targetCompatibility JavaVersion.VERSION_21
31 | }
32 | }
33 |
34 | dependencies {
35 | implementation project(":app-process")
36 | }
37 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # Uncomment this to preserve the line number information for
9 | # debugging stack traces.
10 | -keepattributes SourceFile,LineNumberTable
11 |
12 | # If you keep the line number information, uncomment this to
13 | # hide the original source file name.
14 | -renamesourcefileattribute SourceFile
15 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/java/com/rosan/app_process/demo/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.rosan.app_process.demo;
2 |
3 | import android.app.Activity;
4 | import android.os.Bundle;
5 |
6 | public class MainActivity extends Activity {
7 |
8 | @Override
9 | protected void onCreate(Bundle savedInstanceState) {
10 | super.onCreate(savedInstanceState);
11 | setContentView(R.layout.activity_main);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | plugins {
3 | alias(libs.plugins.agp.lib) apply false
4 | alias(libs.plugins.agp.app) apply false
5 | }
6 |
7 | ext {
8 | groupId = "io.github.iamr0s"
9 | artifactId = "AndroidAppProcess"
10 | artifactVersion = "1.3.1"
11 | POM_NAME = "Android app_process"
12 | POM_DESCRIPTION = "Run Android app_process just like Android Service."
13 | POM_URL = "https://github.com/iamr0s/AndroidAppProcess"
14 | POM_SCM_URL = "https://github.com/iamr0s/AndroidAppProcess"
15 | POM_SCM_CONNECTION = "scm:git@github.com:iamr0s/AndroidAppProcess.git"
16 | POM_SCM_DEV_CONNECTION = "scm:git@github.com:iamr0s/AndroidAppProcess.git"
17 |
18 | POM_LICENCE_NAME = "LGPL v3.0 License"
19 | POM_LICENCE_URL = "https://github.com/iamr0s/AndroidAppProcess/blob/main/LICENSE"
20 | POM_LICENCE_DIST = "repo"
21 |
22 | POM_DEVELOPER_ID = "iamr0s"
23 | POM_DEVELOPER_NAME = "iamr0s"
24 | POM_DEVELOPER_URL = "https://github.com/iamr0s"
25 | }
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m -Duser.language=en -Dconsole.encoding=UTF-8 -Dfile.encoding=UTF-8
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app's APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Enables namespacing of each library's R class so that its R class includes only the
19 | # resources declared in the library itself and none from the library's dependencies,
20 | # thereby reducing the size of the R class for that library
21 | android.nonTransitiveRClass=true
22 |
--------------------------------------------------------------------------------
/gradle/libs.versions.toml:
--------------------------------------------------------------------------------
1 | # Google Maven
2 | # https://maven.google.com/web/index.html?hl=zh-cn
3 |
4 | # Gradle Plugins
5 | # https://plugins.gradle.org/
6 |
7 | # Maven
8 | # https://mvnrepository.com/
9 |
10 | [versions]
11 | agp = "8.10.0"
12 |
13 | [plugins]
14 | agp-lib = { id = "com.android.library", version.ref = "agp" }
15 | agp-app = { id = "com.android.application", version.ref = "agp" }
16 |
17 | [libraries]
18 | annotation = { group = "androidx.annotation", name = "annotation", version = "1.9.1" }
19 | commons-cli = { group = "commons-cli", name = "commons-cli", version = "1.9.0" }
20 | hiddenapibypass = { group = "org.lsposed.hiddenapibypass", name = "hiddenapibypass", version = "6.1" }
21 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iamr0s/AndroidAppProcess/4f5d4e45469abac433ff87efbeddc9b22da64108/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | #
4 | # Copyright © 2015-2021 the original authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 | # SPDX-License-Identifier: Apache-2.0
19 | #
20 |
21 | ##############################################################################
22 | #
23 | # Gradle start up script for POSIX generated by Gradle.
24 | #
25 | # Important for running:
26 | #
27 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
28 | # noncompliant, but you have some other compliant shell such as ksh or
29 | # bash, then to run this script, type that shell name before the whole
30 | # command line, like:
31 | #
32 | # ksh Gradle
33 | #
34 | # Busybox and similar reduced shells will NOT work, because this script
35 | # requires all of these POSIX shell features:
36 | # * functions;
37 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
38 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»;
39 | # * compound commands having a testable exit status, especially «case»;
40 | # * various built-in commands including «command», «set», and «ulimit».
41 | #
42 | # Important for patching:
43 | #
44 | # (2) This script targets any POSIX shell, so it avoids extensions provided
45 | # by Bash, Ksh, etc; in particular arrays are avoided.
46 | #
47 | # The "traditional" practice of packing multiple parameters into a
48 | # space-separated string is a well documented source of bugs and security
49 | # problems, so this is (mostly) avoided, by progressively accumulating
50 | # options in "$@", and eventually passing that to Java.
51 | #
52 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
53 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
54 | # see the in-line comments for details.
55 | #
56 | # There are tweaks for specific operating systems such as AIX, CygWin,
57 | # Darwin, MinGW, and NonStop.
58 | #
59 | # (3) This script is generated from the Groovy template
60 | # https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
61 | # within the Gradle project.
62 | #
63 | # You can find Gradle at https://github.com/gradle/gradle/.
64 | #
65 | ##############################################################################
66 |
67 | # Attempt to set APP_HOME
68 |
69 | # Resolve links: $0 may be a link
70 | app_path=$0
71 |
72 | # Need this for daisy-chained symlinks.
73 | while
74 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
75 | [ -h "$app_path" ]
76 | do
77 | ls=$( ls -ld "$app_path" )
78 | link=${ls#*' -> '}
79 | case $link in #(
80 | /*) app_path=$link ;; #(
81 | *) app_path=$APP_HOME$link ;;
82 | esac
83 | done
84 |
85 | # This is normally unused
86 | # shellcheck disable=SC2034
87 | APP_BASE_NAME=${0##*/}
88 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
89 | APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
90 |
91 | # Use the maximum available, or set MAX_FD != -1 to use that value.
92 | MAX_FD=maximum
93 |
94 | warn () {
95 | echo "$*"
96 | } >&2
97 |
98 | die () {
99 | echo
100 | echo "$*"
101 | echo
102 | exit 1
103 | } >&2
104 |
105 | # OS specific support (must be 'true' or 'false').
106 | cygwin=false
107 | msys=false
108 | darwin=false
109 | nonstop=false
110 | case "$( uname )" in #(
111 | CYGWIN* ) cygwin=true ;; #(
112 | Darwin* ) darwin=true ;; #(
113 | MSYS* | MINGW* ) msys=true ;; #(
114 | NONSTOP* ) nonstop=true ;;
115 | esac
116 |
117 | CLASSPATH="\\\"\\\""
118 |
119 |
120 | # Determine the Java command to use to start the JVM.
121 | if [ -n "$JAVA_HOME" ] ; then
122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
123 | # IBM's JDK on AIX uses strange locations for the executables
124 | JAVACMD=$JAVA_HOME/jre/sh/java
125 | else
126 | JAVACMD=$JAVA_HOME/bin/java
127 | fi
128 | if [ ! -x "$JAVACMD" ] ; then
129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
130 |
131 | Please set the JAVA_HOME variable in your environment to match the
132 | location of your Java installation."
133 | fi
134 | else
135 | JAVACMD=java
136 | if ! command -v java >/dev/null 2>&1
137 | then
138 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
139 |
140 | Please set the JAVA_HOME variable in your environment to match the
141 | location of your Java installation."
142 | fi
143 | fi
144 |
145 | # Increase the maximum file descriptors if we can.
146 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
147 | case $MAX_FD in #(
148 | max*)
149 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
150 | # shellcheck disable=SC2039,SC3045
151 | MAX_FD=$( ulimit -H -n ) ||
152 | warn "Could not query maximum file descriptor limit"
153 | esac
154 | case $MAX_FD in #(
155 | '' | soft) :;; #(
156 | *)
157 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
158 | # shellcheck disable=SC2039,SC3045
159 | ulimit -n "$MAX_FD" ||
160 | warn "Could not set maximum file descriptor limit to $MAX_FD"
161 | esac
162 | fi
163 |
164 | # Collect all arguments for the java command, stacking in reverse order:
165 | # * args from the command line
166 | # * the main class name
167 | # * -classpath
168 | # * -D...appname settings
169 | # * --module-path (only if needed)
170 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
171 |
172 | # For Cygwin or MSYS, switch paths to Windows format before running java
173 | if "$cygwin" || "$msys" ; then
174 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
175 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
176 |
177 | JAVACMD=$( cygpath --unix "$JAVACMD" )
178 |
179 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
180 | for arg do
181 | if
182 | case $arg in #(
183 | -*) false ;; # don't mess with options #(
184 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
185 | [ -e "$t" ] ;; #(
186 | *) false ;;
187 | esac
188 | then
189 | arg=$( cygpath --path --ignore --mixed "$arg" )
190 | fi
191 | # Roll the args list around exactly as many times as the number of
192 | # args, so each arg winds up back in the position where it started, but
193 | # possibly modified.
194 | #
195 | # NB: a `for` loop captures its iteration list before it begins, so
196 | # changing the positional parameters here affects neither the number of
197 | # iterations, nor the values presented in `arg`.
198 | shift # remove old arg
199 | set -- "$@" "$arg" # push replacement arg
200 | done
201 | fi
202 |
203 |
204 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
205 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
206 |
207 | # Collect all arguments for the java command:
208 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
209 | # and any embedded shellness will be escaped.
210 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
211 | # treated as '${Hostname}' itself on the command line.
212 |
213 | set -- \
214 | "-Dorg.gradle.appname=$APP_BASE_NAME" \
215 | -classpath "$CLASSPATH" \
216 | -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
217 | "$@"
218 |
219 | # Stop when "xargs" is not available.
220 | if ! command -v xargs >/dev/null 2>&1
221 | then
222 | die "xargs is not available"
223 | fi
224 |
225 | # Use "xargs" to parse quoted args.
226 | #
227 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed.
228 | #
229 | # In Bash we could simply go:
230 | #
231 | # readarray ARGS < <( xargs -n1 <<<"$var" ) &&
232 | # set -- "${ARGS[@]}" "$@"
233 | #
234 | # but POSIX shell has neither arrays nor command substitution, so instead we
235 | # post-process each arg (as a line of input to sed) to backslash-escape any
236 | # character that might be a shell metacharacter, then use eval to reverse
237 | # that process (while maintaining the separation between arguments), and wrap
238 | # the whole thing up as a single "set" statement.
239 | #
240 | # This will of course break if any of these variables contains a newline or
241 | # an unmatched quote.
242 | #
243 |
244 | eval "set -- $(
245 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
246 | xargs -n1 |
247 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
248 | tr '\n' ' '
249 | )" '"$@"'
250 |
251 | exec "$JAVACMD" "$@"
252 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 | @rem SPDX-License-Identifier: Apache-2.0
17 | @rem
18 |
19 | @if "%DEBUG%"=="" @echo off
20 | @rem ##########################################################################
21 | @rem
22 | @rem Gradle startup script for Windows
23 | @rem
24 | @rem ##########################################################################
25 |
26 | @rem Set local scope for the variables with windows NT shell
27 | if "%OS%"=="Windows_NT" setlocal
28 |
29 | set DIRNAME=%~dp0
30 | if "%DIRNAME%"=="" set DIRNAME=.
31 | @rem This is normally unused
32 | set APP_BASE_NAME=%~n0
33 | set APP_HOME=%DIRNAME%
34 |
35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
37 |
38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
40 |
41 | @rem Find java.exe
42 | if defined JAVA_HOME goto findJavaFromJavaHome
43 |
44 | set JAVA_EXE=java.exe
45 | %JAVA_EXE% -version >NUL 2>&1
46 | if %ERRORLEVEL% equ 0 goto execute
47 |
48 | echo. 1>&2
49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
50 | echo. 1>&2
51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
52 | echo location of your Java installation. 1>&2
53 |
54 | goto fail
55 |
56 | :findJavaFromJavaHome
57 | set JAVA_HOME=%JAVA_HOME:"=%
58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
59 |
60 | if exist "%JAVA_EXE%" goto execute
61 |
62 | echo. 1>&2
63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
64 | echo. 1>&2
65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
66 | echo location of your Java installation. 1>&2
67 |
68 | goto fail
69 |
70 | :execute
71 | @rem Setup the command line
72 |
73 | set CLASSPATH=
74 |
75 |
76 | @rem Execute Gradle
77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
78 |
79 | :end
80 | @rem End local scope for the variables with windows NT shell
81 | if %ERRORLEVEL% equ 0 goto mainEnd
82 |
83 | :fail
84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
85 | rem the _cmd.exe /c_ return code!
86 | set EXIT_CODE=%ERRORLEVEL%
87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1
88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
89 | exit /b %EXIT_CODE%
90 |
91 | :mainEnd
92 | if "%OS%"=="Windows_NT" endlocal
93 |
94 | :omega
95 |
--------------------------------------------------------------------------------
/hidden-api/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias(libs.plugins.agp.lib)
3 | }
4 |
5 | android {
6 | namespace = "com.rosan.hidden_api"
7 | compileSdk = 35
8 |
9 | defaultConfig {
10 | minSdk = 19
11 | }
12 |
13 | compileOptions {
14 | sourceCompatibility JavaVersion.VERSION_21
15 | targetCompatibility JavaVersion.VERSION_21
16 | }
17 | }
18 |
19 | dependencies {
20 | compileOnly(libs.annotation)
21 | }
22 |
23 | tasks.withType(JavaCompile).configureEach {
24 | options.compilerArgs << "-Xlint:deprecation" << "-Xlint:unchecked"
25 | }
26 |
--------------------------------------------------------------------------------
/hidden-api/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/hidden-api/src/main/java/android/app/ActivityManagerNative.java:
--------------------------------------------------------------------------------
1 | package android.app;
2 |
3 | import android.os.IBinder;
4 |
5 | public class ActivityManagerNative {
6 | public static IActivityManager asInterface(IBinder binder) {
7 | throw new RuntimeException("Stub");
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/hidden-api/src/main/java/android/app/ActivityThread.java:
--------------------------------------------------------------------------------
1 | package android.app;
2 |
3 | public class ActivityThread {
4 | public static ActivityThread currentActivityThread() {
5 | throw new RuntimeException("STUB");
6 | }
7 |
8 | public static ActivityThread systemMain() {
9 | throw new RuntimeException("STUB");
10 | }
11 |
12 | public ContextImpl getSystemContext() {
13 | throw new RuntimeException("STUB");
14 | }
15 |
16 | public Application getApplication() {
17 | throw new RuntimeException("STUB");
18 | }
19 |
20 | public final LoadedApk peekPackageInfo(String packageName, boolean includeCode) {
21 | throw new RuntimeException("STUB");
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/hidden-api/src/main/java/android/app/ContentProviderHolder.java:
--------------------------------------------------------------------------------
1 | package android.app;
2 |
3 | import android.content.IContentProvider;
4 |
5 | public class ContentProviderHolder {
6 | public IContentProvider provider;
7 | }
8 |
--------------------------------------------------------------------------------
/hidden-api/src/main/java/android/app/ContextImpl.java:
--------------------------------------------------------------------------------
1 | package android.app;
2 |
3 | import android.content.BroadcastReceiver;
4 | import android.content.ComponentName;
5 | import android.content.ContentResolver;
6 | import android.content.Context;
7 | import android.content.Intent;
8 | import android.content.IntentFilter;
9 | import android.content.IntentSender;
10 | import android.content.ServiceConnection;
11 | import android.content.SharedPreferences;
12 | import android.content.pm.ApplicationInfo;
13 | import android.content.pm.PackageManager;
14 | import android.content.res.AssetManager;
15 | import android.content.res.Configuration;
16 | import android.content.res.Resources;
17 | import android.database.DatabaseErrorHandler;
18 | import android.database.sqlite.SQLiteDatabase;
19 | import android.graphics.Bitmap;
20 | import android.graphics.drawable.Drawable;
21 | import android.net.Uri;
22 | import android.os.Bundle;
23 | import android.os.Handler;
24 | import android.os.Looper;
25 | import android.os.UserHandle;
26 | import android.view.Display;
27 |
28 | import androidx.annotation.NonNull;
29 | import androidx.annotation.Nullable;
30 |
31 | import java.io.File;
32 | import java.io.FileInputStream;
33 | import java.io.FileNotFoundException;
34 | import java.io.FileOutputStream;
35 | import java.io.IOException;
36 | import java.io.InputStream;
37 |
38 | public class ContextImpl extends Context {
39 | @Override
40 | public AssetManager getAssets() {
41 | return null;
42 | }
43 |
44 | @Override
45 | public Resources getResources() {
46 | return null;
47 | }
48 |
49 | @Override
50 | public PackageManager getPackageManager() {
51 | return null;
52 | }
53 |
54 | @Override
55 | public ContentResolver getContentResolver() {
56 | return null;
57 | }
58 |
59 | @Override
60 | public Looper getMainLooper() {
61 | return null;
62 | }
63 |
64 | @Override
65 | public Context getApplicationContext() {
66 | return null;
67 | }
68 |
69 | @Override
70 | public void setTheme(int resid) {
71 |
72 | }
73 |
74 | @Override
75 | public Resources.Theme getTheme() {
76 | return null;
77 | }
78 |
79 | @Override
80 | public ClassLoader getClassLoader() {
81 | return null;
82 | }
83 |
84 | @Override
85 | public String getPackageName() {
86 | return null;
87 | }
88 |
89 | @Override
90 | public ApplicationInfo getApplicationInfo() {
91 | return null;
92 | }
93 |
94 | @Override
95 | public String getPackageResourcePath() {
96 | return null;
97 | }
98 |
99 | @Override
100 | public String getPackageCodePath() {
101 | return null;
102 | }
103 |
104 | @Override
105 | public SharedPreferences getSharedPreferences(String name, int mode) {
106 | return null;
107 | }
108 |
109 | @Override
110 | public boolean moveSharedPreferencesFrom(Context sourceContext, String name) {
111 | return false;
112 | }
113 |
114 | @Override
115 | public boolean deleteSharedPreferences(String name) {
116 | return false;
117 | }
118 |
119 | @Override
120 | public FileInputStream openFileInput(String name) throws FileNotFoundException {
121 | return null;
122 | }
123 |
124 | @Override
125 | public FileOutputStream openFileOutput(String name, int mode) throws FileNotFoundException {
126 | return null;
127 | }
128 |
129 | @Override
130 | public boolean deleteFile(String name) {
131 | return false;
132 | }
133 |
134 | @Override
135 | public File getFileStreamPath(String name) {
136 | return null;
137 | }
138 |
139 | @Override
140 | public File getDataDir() {
141 | return null;
142 | }
143 |
144 | @Override
145 | public File getFilesDir() {
146 | return null;
147 | }
148 |
149 | @Override
150 | public File getNoBackupFilesDir() {
151 | return null;
152 | }
153 |
154 | @Nullable
155 | @Override
156 | public File getExternalFilesDir(@Nullable String type) {
157 | return null;
158 | }
159 |
160 | @Override
161 | public File[] getExternalFilesDirs(String type) {
162 | return new File[0];
163 | }
164 |
165 | @Override
166 | public File getObbDir() {
167 | return null;
168 | }
169 |
170 | @Override
171 | public File[] getObbDirs() {
172 | return new File[0];
173 | }
174 |
175 | @Override
176 | public File getCacheDir() {
177 | return null;
178 | }
179 |
180 | @Override
181 | public File getCodeCacheDir() {
182 | return null;
183 | }
184 |
185 | @Nullable
186 | @Override
187 | public File getExternalCacheDir() {
188 | return null;
189 | }
190 |
191 | @Override
192 | public File[] getExternalCacheDirs() {
193 | return new File[0];
194 | }
195 |
196 | @Override
197 | public File[] getExternalMediaDirs() {
198 | return new File[0];
199 | }
200 |
201 | @Override
202 | public String[] fileList() {
203 | return new String[0];
204 | }
205 |
206 | @Override
207 | public File getDir(String name, int mode) {
208 | return null;
209 | }
210 |
211 | @Override
212 | public SQLiteDatabase openOrCreateDatabase(String name, int mode, SQLiteDatabase.CursorFactory factory) {
213 | return null;
214 | }
215 |
216 | @Override
217 | public SQLiteDatabase openOrCreateDatabase(String name, int mode, SQLiteDatabase.CursorFactory factory, @Nullable DatabaseErrorHandler errorHandler) {
218 | return null;
219 | }
220 |
221 | @Override
222 | public boolean moveDatabaseFrom(Context sourceContext, String name) {
223 | return false;
224 | }
225 |
226 | @Override
227 | public boolean deleteDatabase(String name) {
228 | return false;
229 | }
230 |
231 | @Override
232 | public File getDatabasePath(String name) {
233 | return null;
234 | }
235 |
236 | @Override
237 | public String[] databaseList() {
238 | return new String[0];
239 | }
240 |
241 | @Override
242 | public Drawable getWallpaper() {
243 | return null;
244 | }
245 |
246 | @Override
247 | public Drawable peekWallpaper() {
248 | return null;
249 | }
250 |
251 | @Override
252 | public int getWallpaperDesiredMinimumWidth() {
253 | return 0;
254 | }
255 |
256 | @Override
257 | public int getWallpaperDesiredMinimumHeight() {
258 | return 0;
259 | }
260 |
261 | @Override
262 | public void setWallpaper(Bitmap bitmap) throws IOException {
263 |
264 | }
265 |
266 | @Override
267 | public void setWallpaper(InputStream data) throws IOException {
268 |
269 | }
270 |
271 | @Override
272 | public void clearWallpaper() throws IOException {
273 |
274 | }
275 |
276 | @Override
277 | public void startActivity(Intent intent) {
278 |
279 | }
280 |
281 | @Override
282 | public void startActivity(Intent intent, @Nullable Bundle options) {
283 |
284 | }
285 |
286 | @Override
287 | public void startActivities(Intent[] intents) {
288 |
289 | }
290 |
291 | @Override
292 | public void startActivities(Intent[] intents, Bundle options) {
293 |
294 | }
295 |
296 | @Override
297 | public void startIntentSender(IntentSender intent, @Nullable Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags) throws IntentSender.SendIntentException {
298 |
299 | }
300 |
301 | @Override
302 | public void startIntentSender(IntentSender intent, @Nullable Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags, @Nullable Bundle options) throws IntentSender.SendIntentException {
303 |
304 | }
305 |
306 | @Override
307 | public void sendBroadcast(Intent intent) {
308 |
309 | }
310 |
311 | @Override
312 | public void sendBroadcast(Intent intent, @Nullable String receiverPermission) {
313 |
314 | }
315 |
316 | @Override
317 | public void sendOrderedBroadcast(Intent intent, @Nullable String receiverPermission) {
318 |
319 | }
320 |
321 | @Override
322 | public void sendOrderedBroadcast(@NonNull Intent intent, @Nullable String receiverPermission, @Nullable BroadcastReceiver resultReceiver, @Nullable Handler scheduler, int initialCode, @Nullable String initialData, @Nullable Bundle initialExtras) {
323 |
324 | }
325 |
326 | @Override
327 | public void sendBroadcastAsUser(Intent intent, UserHandle user) {
328 |
329 | }
330 |
331 | @Override
332 | public void sendBroadcastAsUser(Intent intent, UserHandle user, @Nullable String receiverPermission) {
333 |
334 | }
335 |
336 | @Override
337 | public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user, @Nullable String receiverPermission, BroadcastReceiver resultReceiver, @Nullable Handler scheduler, int initialCode, @Nullable String initialData, @Nullable Bundle initialExtras) {
338 |
339 | }
340 |
341 | @Override
342 | public void sendStickyBroadcast(Intent intent) {
343 |
344 | }
345 |
346 | @Override
347 | public void sendStickyOrderedBroadcast(Intent intent, BroadcastReceiver resultReceiver, @Nullable Handler scheduler, int initialCode, @Nullable String initialData, @Nullable Bundle initialExtras) {
348 |
349 | }
350 |
351 | @Override
352 | public void removeStickyBroadcast(Intent intent) {
353 |
354 | }
355 |
356 | @Override
357 | public void sendStickyBroadcastAsUser(Intent intent, UserHandle user) {
358 |
359 | }
360 |
361 | @Override
362 | public void sendStickyOrderedBroadcastAsUser(Intent intent, UserHandle user, BroadcastReceiver resultReceiver, @Nullable Handler scheduler, int initialCode, @Nullable String initialData, @Nullable Bundle initialExtras) {
363 |
364 | }
365 |
366 | @Override
367 | public void removeStickyBroadcastAsUser(Intent intent, UserHandle user) {
368 |
369 | }
370 |
371 | @Nullable
372 | @Override
373 | public Intent registerReceiver(@Nullable BroadcastReceiver receiver, IntentFilter filter) {
374 | return null;
375 | }
376 |
377 | @Nullable
378 | @Override
379 | public Intent registerReceiver(@Nullable BroadcastReceiver receiver, IntentFilter filter, int flags) {
380 | return null;
381 | }
382 |
383 | @Nullable
384 | @Override
385 | public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, @Nullable String broadcastPermission, @Nullable Handler scheduler) {
386 | return null;
387 | }
388 |
389 | @Nullable
390 | @Override
391 | public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, @Nullable String broadcastPermission, @Nullable Handler scheduler, int flags) {
392 | return null;
393 | }
394 |
395 | @Override
396 | public void unregisterReceiver(BroadcastReceiver receiver) {
397 |
398 | }
399 |
400 | @Nullable
401 | @Override
402 | public ComponentName startService(Intent service) {
403 | return null;
404 | }
405 |
406 | @Nullable
407 | @Override
408 | public ComponentName startForegroundService(Intent service) {
409 | return null;
410 | }
411 |
412 | @Override
413 | public boolean stopService(Intent service) {
414 | return false;
415 | }
416 |
417 | @Override
418 | public boolean bindService(Intent service, @NonNull ServiceConnection conn, int flags) {
419 | return false;
420 | }
421 |
422 | @Override
423 | public void unbindService(@NonNull ServiceConnection conn) {
424 |
425 | }
426 |
427 | @Override
428 | public boolean startInstrumentation(@NonNull ComponentName className, @Nullable String profileFile, @Nullable Bundle arguments) {
429 | return false;
430 | }
431 |
432 | @Override
433 | public Object getSystemService(@NonNull String name) {
434 | return null;
435 | }
436 |
437 | @Nullable
438 | @Override
439 | public String getSystemServiceName(@NonNull Class> serviceClass) {
440 | return null;
441 | }
442 |
443 | @Override
444 | public int checkPermission(@NonNull String permission, int pid, int uid) {
445 | return 0;
446 | }
447 |
448 | @Override
449 | public int checkCallingPermission(@NonNull String permission) {
450 | return 0;
451 | }
452 |
453 | @Override
454 | public int checkCallingOrSelfPermission(@NonNull String permission) {
455 | return 0;
456 | }
457 |
458 | @Override
459 | public int checkSelfPermission(@NonNull String permission) {
460 | return 0;
461 | }
462 |
463 | @Override
464 | public void enforcePermission(@NonNull String permission, int pid, int uid, @Nullable String message) {
465 |
466 | }
467 |
468 | @Override
469 | public void enforceCallingPermission(@NonNull String permission, @Nullable String message) {
470 |
471 | }
472 |
473 | @Override
474 | public void enforceCallingOrSelfPermission(@NonNull String permission, @Nullable String message) {
475 |
476 | }
477 |
478 | @Override
479 | public void grantUriPermission(String toPackage, Uri uri, int modeFlags) {
480 |
481 | }
482 |
483 | @Override
484 | public void revokeUriPermission(Uri uri, int modeFlags) {
485 |
486 | }
487 |
488 | @Override
489 | public void revokeUriPermission(String toPackage, Uri uri, int modeFlags) {
490 |
491 | }
492 |
493 | @Override
494 | public int checkUriPermission(Uri uri, int pid, int uid, int modeFlags) {
495 | return 0;
496 | }
497 |
498 | @Override
499 | public int checkCallingUriPermission(Uri uri, int modeFlags) {
500 | return 0;
501 | }
502 |
503 | @Override
504 | public int checkCallingOrSelfUriPermission(Uri uri, int modeFlags) {
505 | return 0;
506 | }
507 |
508 | @Override
509 | public int checkUriPermission(@Nullable Uri uri, @Nullable String readPermission, @Nullable String writePermission, int pid, int uid, int modeFlags) {
510 | return 0;
511 | }
512 |
513 | @Override
514 | public void enforceUriPermission(Uri uri, int pid, int uid, int modeFlags, String message) {
515 |
516 | }
517 |
518 | @Override
519 | public void enforceCallingUriPermission(Uri uri, int modeFlags, String message) {
520 |
521 | }
522 |
523 | @Override
524 | public void enforceCallingOrSelfUriPermission(Uri uri, int modeFlags, String message) {
525 |
526 | }
527 |
528 | @Override
529 | public void enforceUriPermission(@Nullable Uri uri, @Nullable String readPermission, @Nullable String writePermission, int pid, int uid, int modeFlags, @Nullable String message) {
530 |
531 | }
532 |
533 | @Override
534 | public Context createPackageContext(String packageName, int flags) throws PackageManager.NameNotFoundException {
535 | return null;
536 | }
537 |
538 | @Override
539 | public Context createContextForSplit(String splitName) throws PackageManager.NameNotFoundException {
540 | return null;
541 | }
542 |
543 | @Override
544 | public Context createConfigurationContext(@NonNull Configuration overrideConfiguration) {
545 | return null;
546 | }
547 |
548 | @Override
549 | public Context createDisplayContext(@NonNull Display display) {
550 | return null;
551 | }
552 |
553 | @Override
554 | public Context createDeviceProtectedStorageContext() {
555 | return null;
556 | }
557 |
558 | @Override
559 | public boolean isDeviceProtectedStorage() {
560 | return false;
561 | }
562 | }
563 |
--------------------------------------------------------------------------------
/hidden-api/src/main/java/android/app/IActivityManager.java:
--------------------------------------------------------------------------------
1 | package android.app;
2 |
3 | import android.os.Binder;
4 | import android.os.Build;
5 | import android.os.IBinder;
6 | import android.os.IInterface;
7 | import android.os.RemoteException;
8 |
9 | import androidx.annotation.RequiresApi;
10 |
11 | public interface IActivityManager extends IInterface {
12 | @RequiresApi(Build.VERSION_CODES.Q)
13 | ContentProviderHolder getContentProviderExternal(String name, int userId, IBinder token, String tag)
14 | throws RemoteException;
15 |
16 | ContentProviderHolder getContentProviderExternal(String name, int userId, IBinder token)
17 | throws RemoteException;
18 |
19 | abstract class Stub extends Binder implements IActivityManager {
20 | public static IActivityManager asInterface(IBinder binder) {
21 | throw new RuntimeException("Stub");
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/hidden-api/src/main/java/android/app/LoadedApk.java:
--------------------------------------------------------------------------------
1 | package android.app;
2 |
3 | public final class LoadedApk {
4 | }
5 |
--------------------------------------------------------------------------------
/hidden-api/src/main/java/android/content/IContentProvider.java:
--------------------------------------------------------------------------------
1 | package android.content;
2 |
3 | import android.os.IInterface;
4 |
5 | public interface IContentProvider extends IInterface {
6 | }
7 |
--------------------------------------------------------------------------------
/hidden-api/src/main/java/android/os/ServiceManager.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2009 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package android.os;
18 |
19 | import java.util.Map;
20 |
21 | public final class ServiceManager {
22 |
23 | /**
24 | * Returns a reference to a service with the given name.
25 | *
26 | * @param name the name of the service to get
27 | * @return a reference to the service, or null
if the service doesn't exist
28 | */
29 | public static IBinder getService(String name) {
30 | return null;
31 | }
32 |
33 | /**
34 | * Is not supposed to return null, but that is fine for layoutlib.
35 | */
36 | public static IBinder getServiceOrThrow(String name) throws ServiceNotFoundException {
37 | throw new ServiceNotFoundException(name);
38 | }
39 |
40 | /**
41 | * Place a new @a service called @a name into the service
42 | * manager.
43 | *
44 | * @param name the name of the new service
45 | * @param service the service object
46 | */
47 | public static void addService(String name, IBinder service) {
48 | // pass
49 | }
50 |
51 | /**
52 | * Retrieve an existing service called @a name from the
53 | * service manager. Non-blocking.
54 | */
55 | public static IBinder checkService(String name) {
56 | return null;
57 | }
58 |
59 | /**
60 | * Return a list of all currently running services.
61 | * @return an array of all currently running services, or null
in
62 | * case of an exception
63 | */
64 | public static String[] listServices() {
65 | // actual implementation returns null sometimes, so it's ok
66 | // to return null instead of an empty list.
67 | return null;
68 | }
69 |
70 | /**
71 | * This is only intended to be called when the process is first being brought
72 | * up and bound by the activity manager. There is only one thread in the process
73 | * at that time, so no locking is done.
74 | *
75 | * @param cache the cache of service references
76 | * @hide
77 | */
78 | public static void initServiceCache(Map cache) {
79 | // pass
80 | }
81 |
82 | /**
83 | * Exception thrown when no service published for given name. This might be
84 | * thrown early during boot before certain services have published
85 | * themselves.
86 | *
87 | * @hide
88 | */
89 | public static class ServiceNotFoundException extends Exception {
90 | // identical to the original implementation
91 | public ServiceNotFoundException(String name) {
92 | super("No service published for: " + name);
93 | }
94 | }
95 | }
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | repositories {
3 | google()
4 | mavenCentral()
5 | gradlePluginPortal()
6 | }
7 | }
8 |
9 | dependencyResolutionManagement {
10 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
11 | repositories {
12 | google()
13 | mavenCentral()
14 | }
15 | }
16 |
17 | rootProject.name = "App-Process"
18 | include ":app-process", ":hidden-api", ":app"
19 |
--------------------------------------------------------------------------------