├── .gitattributes ├── app-process ├── src │ └── main │ │ ├── AndroidManifest.xml │ │ ├── aidl │ │ └── com │ │ │ └── rosan │ │ │ └── app_process │ │ │ ├── ParcelableBinder.aidl │ │ │ ├── IRemoteProcess.aidl │ │ │ └── IProcessManager.aidl │ │ └── java │ │ └── com │ │ └── rosan │ │ └── app_process │ │ ├── ParcelableBinder.java │ │ ├── RemoteProcess.java │ │ ├── BinderWrapper.java │ │ ├── ProcessManager.java │ │ ├── RemoteProcessImpl.java │ │ ├── NewProcessReceiver.java │ │ ├── NewProcess.java │ │ └── AppProcess.java ├── proguard-rules.pro └── build.gradle ├── hidden-api ├── src │ └── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── android │ │ │ ├── app │ │ │ ├── LoadedApk.java │ │ │ ├── ContentProviderHolder.java │ │ │ ├── ActivityManagerNative.java │ │ │ ├── ActivityThread.java │ │ │ ├── IActivityManager.java │ │ │ └── ContextImpl.java │ │ │ ├── content │ │ │ └── IContentProvider.java │ │ │ └── os │ │ │ └── ServiceManager.java │ │ └── aidl │ │ └── android │ │ └── content │ │ └── pm │ │ └── IPackageManager.aidl └── build.gradle ├── gradle ├── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties └── libs.versions.toml ├── .gitignore ├── settings.gradle ├── app ├── src │ └── main │ │ ├── res │ │ └── layout │ │ │ └── activity_main.xml │ │ ├── AndroidManifest.xml │ │ └── java │ │ └── com │ │ └── rosan │ │ └── app_process │ │ └── demo │ │ └── MainActivity.java ├── proguard-rules.pro └── build.gradle ├── .github ├── dependabot.yml └── workflows │ └── build.yml ├── README.md ├── gradle.properties ├── gradlew.bat ├── LICENSE └── gradlew /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | *.bat text eol=crlf 3 | *.jar binary 4 | -------------------------------------------------------------------------------- /app-process/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /hidden-api/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /hidden-api/src/main/java/android/app/LoadedApk.java: -------------------------------------------------------------------------------- 1 | package android.app; 2 | 3 | public final class LoadedApk { 4 | } 5 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamr0s/AndroidAppProcess/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app-process/src/main/aidl/com/rosan/app_process/ParcelableBinder.aidl: -------------------------------------------------------------------------------- 1 | package com.rosan.app_process; 2 | 3 | parcelable ParcelableBinder; -------------------------------------------------------------------------------- /hidden-api/src/main/aidl/android/content/pm/IPackageManager.aidl: -------------------------------------------------------------------------------- 1 | package android.content.pm; 2 | 3 | interface IPackageManager { 4 | String[] getSystemSharedLibraryNames(); 5 | } 6 | -------------------------------------------------------------------------------- /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/app/ContentProviderHolder.java: -------------------------------------------------------------------------------- 1 | package android.app; 2 | 3 | import android.content.IContentProvider; 4 | 5 | public class ContentProviderHolder { 6 | public IContentProvider provider; 7 | } 8 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | } -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /app-process/src/main/aidl/com/rosan/app_process/IProcessManager.aidl: -------------------------------------------------------------------------------- 1 | package com.rosan.app_process; 2 | 3 | import com.rosan.app_process.IRemoteProcess; 4 | import com.rosan.app_process.ParcelableBinder; 5 | 6 | interface IProcessManager { 7 | void exit(int code) = 1; 8 | 9 | // remote binder transact: 2 10 | 11 | IRemoteProcess remoteProcess(in List cmdList, in Map env, in String directory) = 3; 12 | 13 | ParcelableBinder serviceBinder(in ComponentName componentName) = 4; 14 | } -------------------------------------------------------------------------------- /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-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 | -------------------------------------------------------------------------------- /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 | buildFeatures { 14 | aidl = true 15 | } 16 | 17 | compileOptions { 18 | sourceCompatibility JavaVersion.VERSION_21 19 | targetCompatibility JavaVersion.VERSION_21 20 | } 21 | } 22 | 23 | dependencies { 24 | compileOnly(libs.annotation) 25 | } 26 | 27 | tasks.withType(JavaCompile).configureEach { 28 | options.compilerArgs << "-Xlint:deprecation" << "-Xlint:unchecked" 29 | } 30 | -------------------------------------------------------------------------------- /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.13.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.10.0" } 20 | hiddenapibypass = { group = "org.lsposed.hiddenapibypass", name = "hiddenapibypass", version = "6.1" } 21 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /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 | compileOnly project(":hidden-api") 36 | implementation project(":app-process") 37 | } 38 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Android AppProcess 2 | 3 | Encapsulation of Android app_process, you can use it just like Android Service. 4 | 5 | ![Maven Central](https://img.shields.io/maven-central/v/io.github.iamr0s/AndroidAppProcess) 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); 22 | ``` 23 | 24 | - Root 25 | 26 | ```java 27 | AppProcess process = new AppProcess.Root(); 28 | process.init(context); 29 | ``` 30 | 31 | ### 2. Use it. 32 | 33 | - Remote Binder Transact 34 | 35 | ```java 36 | AppProcess process = new AppProcess.Root(); 37 | process.init(context); 38 | 39 | IBinder manager = 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/src/main/java/com/rosan/app_process/ParcelableBinder.java: -------------------------------------------------------------------------------- 1 | package com.rosan.app_process; 2 | 3 | import android.os.IBinder; 4 | import android.os.Parcel; 5 | import android.os.Parcelable; 6 | 7 | import androidx.annotation.NonNull; 8 | 9 | public class ParcelableBinder implements Parcelable { 10 | public static final Creator CREATOR = new Creator() { 11 | @Override 12 | public ParcelableBinder createFromParcel(Parcel in) { 13 | return new ParcelableBinder(in); 14 | } 15 | 16 | @Override 17 | public ParcelableBinder[] newArray(int size) { 18 | return new ParcelableBinder[size]; 19 | } 20 | }; 21 | 22 | private final IBinder binder; 23 | 24 | public ParcelableBinder(IBinder binder) { 25 | this.binder = binder; 26 | } 27 | 28 | public ParcelableBinder(Parcel parcel) { 29 | this.binder = parcel.readStrongBinder(); 30 | } 31 | 32 | public IBinder getBinder() { 33 | return binder; 34 | } 35 | 36 | @Override 37 | public int describeContents() { 38 | return 0; 39 | } 40 | 41 | @Override 42 | public void writeToParcel(@NonNull Parcel dest, int flags) { 43 | dest.writeStrongBinder(binder); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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.content.pm.IPackageManager; 5 | import android.os.Bundle; 6 | import android.os.IBinder; 7 | import android.os.RemoteException; 8 | import android.os.ServiceManager; 9 | import android.widget.TextView; 10 | import android.widget.Toast; 11 | 12 | import com.rosan.app_process.AppProcess; 13 | 14 | import java.util.Arrays; 15 | 16 | public class MainActivity extends Activity { 17 | 18 | private void makeText(String msg) { 19 | runOnUiThread(() -> Toast.makeText(this, msg, Toast.LENGTH_SHORT).show()); 20 | } 21 | 22 | @Override 23 | protected void onCreate(Bundle savedInstanceState) { 24 | super.onCreate(savedInstanceState); 25 | setContentView(R.layout.activity_main); 26 | 27 | AppProcess process = new AppProcess.Default(); 28 | if (!process.init(this)) { 29 | makeText("AppProcess: failed initialize."); 30 | finishAndRemoveTask(); 31 | return; 32 | } 33 | 34 | IBinder manager = ServiceManager.getService("package"); 35 | IBinder binderWrapper; 36 | try { 37 | binderWrapper = process.binderWrapper(manager); 38 | } catch (IllegalStateException ignored) { 39 | makeText("AppProcess: please call init() first."); 40 | finishAndRemoveTask(); 41 | return; 42 | } 43 | IPackageManager managerWrapper = IPackageManager.Stub.asInterface(binderWrapper); 44 | 45 | String[] libraries = new String[]{"NULL"}; 46 | try { 47 | if (managerWrapper != null) { 48 | libraries = managerWrapper.getSystemSharedLibraryNames(); 49 | } 50 | } catch (RemoteException ignored) { 51 | makeText("RemoteException occurred."); 52 | } 53 | 54 | TextView text = findViewById(R.id.text); 55 | text.setText(Arrays.toString(libraries)); 56 | 57 | process.close(); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /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/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 IProcessManager mManager; 15 | 16 | private final IBinder mBinder; 17 | 18 | BinderWrapper(@NonNull IProcessManager manager, @NonNull IBinder binder) { 19 | this.mManager = manager; 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(mManager, 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/ProcessManager.java: -------------------------------------------------------------------------------- 1 | package com.rosan.app_process; 2 | 3 | import android.content.ComponentName; 4 | import android.content.pm.PackageManager; 5 | import android.os.Binder; 6 | import android.os.IBinder; 7 | import android.os.Parcel; 8 | import android.os.RemoteException; 9 | 10 | import androidx.annotation.Keep; 11 | 12 | import java.io.File; 13 | import java.io.IOException; 14 | import java.lang.reflect.InvocationTargetException; 15 | import java.util.List; 16 | import java.util.Map; 17 | 18 | public class ProcessManager extends IProcessManager.Stub { 19 | @Keep 20 | public ProcessManager() { 21 | super(); 22 | } 23 | 24 | @Override 25 | public void exit(int code) { 26 | System.exit(code); 27 | } 28 | 29 | private boolean targetTransact(IBinder binder, int code, Parcel data, Parcel reply, int flags) throws RemoteException { 30 | try { 31 | return AppProcess.binderWithCleanCallingIdentity(() -> binder.transact(code, data, reply, flags)); 32 | } catch (RemoteException e) { 33 | throw e; 34 | } catch (Exception e) { 35 | throw new RuntimeException(e); 36 | } 37 | } 38 | 39 | @Override 40 | public IRemoteProcess remoteProcess(List cmdList, Map env, String directory) { 41 | ProcessBuilder builder = new ProcessBuilder().command(cmdList); 42 | if (directory != null) builder = builder.directory(new File(directory)); 43 | if (env != null) builder.environment().putAll(env); 44 | File file = null; 45 | try { 46 | return new RemoteProcessImpl(builder.start()); 47 | } catch (IOException e) { 48 | throw new IllegalStateException(e); 49 | } 50 | } 51 | 52 | @Override 53 | public ParcelableBinder serviceBinder(ComponentName componentName) { 54 | try { 55 | return new ParcelableBinder(NewProcess.createBinder(componentName)); 56 | } catch (PackageManager.NameNotFoundException | NoSuchFieldException | 57 | InvocationTargetException | NoSuchMethodException | IllegalAccessException | 58 | ClassNotFoundException e) { 59 | throw new IllegalStateException(e); 60 | } 61 | } 62 | 63 | @Override 64 | public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { 65 | if (code != Binder.FIRST_CALL_TRANSACTION + 2) 66 | return super.onTransact(code, data, reply, flags); 67 | Parcel targetData = Parcel.obtain(); 68 | try { 69 | data.enforceInterface(this.asBinder().getInterfaceDescriptor()); 70 | IBinder binder = data.readStrongBinder(); 71 | int targetCode = data.readInt(); 72 | int targetFlags = data.readInt(); 73 | targetData.appendFrom(data, data.dataPosition(), data.dataAvail()); 74 | return targetTransact(binder, targetCode, targetData, reply, targetFlags); 75 | } finally { 76 | targetData.recycle(); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /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/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 | } -------------------------------------------------------------------------------- /app-process/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | alias(libs.plugins.agp.lib) 3 | id "maven-publish" 4 | id "signing" 5 | } 6 | 7 | def publishingProps = new Properties() 8 | def publishingFile = file("${rootDir}/keystore/publishing.properties") 9 | if (publishingFile.exists()) { 10 | publishingProps.load(publishingFile.newInputStream()) 11 | } 12 | 13 | android { 14 | namespace = "com.rosan.app_process" 15 | compileSdk = TARGET_SDK 16 | 17 | defaultConfig { 18 | minSdk = MIN_SDK 19 | targetSdk = TARGET_SDK 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 | afterEvaluate { 49 | tasks.named('sourceReleaseJar', Jar) { 50 | duplicatesStrategy = DuplicatesStrategy.EXCLUDE 51 | } 52 | } 53 | 54 | tasks.withType(JavaCompile).configureEach { 55 | options.compilerArgs << "-Xlint:deprecation" << "-Xlint:unchecked" 56 | } 57 | 58 | publishing { 59 | publications { 60 | mavenRelease(MavenPublication) { 61 | afterEvaluate { 62 | from components.findByName('release') 63 | } 64 | 65 | group = publishingProps['groupId'] 66 | artifactId = publishingProps['artifactId'] 67 | version = ARTIFACT_VERSION 68 | 69 | pom { 70 | name = publishingProps['pom.name'] 71 | description = publishingProps['pom.description'] 72 | inceptionYear = publishingProps['pom.inceptionYear'] 73 | url = publishingProps['pom.url'] 74 | scm { 75 | url = publishingProps['pom.scmUrl'] 76 | connection = publishingProps['pom.scmConnection'] 77 | } 78 | licenses { 79 | license { 80 | name = publishingProps['pom.licenceName'] 81 | url = publishingProps['pom.licenceUrl'] 82 | } 83 | } 84 | developers { 85 | developer { 86 | name = publishingProps['pom.developerName'] 87 | url = publishingProps['pom.developerUrl'] 88 | } 89 | } 90 | } 91 | } 92 | } 93 | repositories { 94 | maven { 95 | url = uri(rootProject.layout.buildDirectory.dir("staging").get().asFile) 96 | } 97 | } 98 | } 99 | 100 | signing { 101 | if (publishingFile.exists()) { 102 | def privateKey = publishingProps['signing.privateKey'] 103 | if (privateKey == null) { 104 | privateKey = file(publishingProps['signing.secretKeyRingFile']).text 105 | } 106 | useInMemoryPgpKeys( 107 | publishingProps['signing.keyId'], 108 | privateKey, 109 | publishingProps['signing.password'] 110 | ) 111 | } 112 | sign publishing.publications 113 | } 114 | -------------------------------------------------------------------------------- /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-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/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, InvocationTargetException, NoSuchMethodException, IllegalAccessException, ClassNotFoundException { 75 | return createBinder(getUIDContext(), componentName); 76 | } 77 | 78 | public static IBinder createBinder(Context context, ComponentName componentName) throws PackageManager.NameNotFoundException, ClassNotFoundException { 79 | Context packageContext = getSystemContext().createPackageContext(componentName.getPackageName(), Context.CONTEXT_IGNORE_SECURITY | Context.CONTEXT_INCLUDE_CODE); 80 | Class clazz = packageContext.getClassLoader().loadClass(componentName.getClassName()); 81 | Constructor constructor = null; 82 | try { 83 | constructor = clazz.getDeclaredConstructor(Context.class); 84 | } catch (NoSuchMethodException ignored) { 85 | } 86 | Object result; 87 | try { 88 | result = constructor != null ? constructor.newInstance(context) : clazz.newInstance(); 89 | } catch (IllegalAccessException | InstantiationException | InvocationTargetException e) { 90 | throw new RuntimeException(e); 91 | } 92 | return ((IInterface) result).asBinder(); 93 | } 94 | 95 | public static List getPackagesForUid(Context context, int uid) { 96 | String[] packageNames = context.getPackageManager().getPackagesForUid(uid); 97 | if (packageNames == null) return Collections.emptyList(); 98 | return Arrays.asList(packageNames); 99 | } 100 | 101 | private static @NonNull ActivityThread getActivityThread() { 102 | if (mActivityThread != null) return mActivityThread; 103 | if (Looper.getMainLooper() == null) Looper.prepareMainLooper(); 104 | if (Looper.myLooper() == null) Looper.prepare(); 105 | 106 | mActivityThread = ActivityThread.systemMain(); 107 | Objects.requireNonNull(mActivityThread); 108 | return getActivityThread(); 109 | } 110 | 111 | private static @NonNull Context getSystemContext() { 112 | return getActivityThread().getSystemContext(); 113 | } 114 | 115 | public static Context getUIDContext() throws PackageManager.NameNotFoundException, NoSuchFieldException, InvocationTargetException, NoSuchMethodException, IllegalAccessException { 116 | Context context = getSystemContext(); 117 | 118 | int uid = Process.myUid(); 119 | List packageNames = getPackagesForUid(context, uid); 120 | if (packageNames.isEmpty()) return context; 121 | if (packageNames.contains(context.getPackageName()) && (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q || packageNames.contains(context.getOpPackageName()))) 122 | return context; 123 | 124 | return createAppContext(context, packageNames.get(0)); 125 | } 126 | 127 | @SuppressLint("PrivateApi") 128 | private static Context createAppContext(Context context, String packageName) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, PackageManager.NameNotFoundException, NoSuchFieldException { 129 | Context impl = context.createPackageContext(packageName, Context.CONTEXT_IGNORE_SECURITY | Context.CONTEXT_INCLUDE_CODE); 130 | while (impl instanceof ContextWrapper) { 131 | impl = ((ContextWrapper) impl).getBaseContext(); 132 | } 133 | 134 | Method method = impl.getClass().getDeclaredMethod("createAppContext", ActivityThread.class, LoadedApk.class); 135 | method.setAccessible(true); 136 | return (Context) method.invoke(null, getActivityThread(), getActivityThread().peekPackageInfo(packageName, true)); 137 | } 138 | } -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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.Objects; 24 | import java.util.concurrent.Callable; 25 | 26 | public abstract class AppProcess implements Closeable { 27 | protected Context mContext = null; 28 | 29 | protected IProcessManager mManager = null; 30 | 31 | protected final Map mChildProcess = new HashMap<>(); 32 | 33 | public static ProcessParams generateProcessParams(@NonNull String classPath, @NonNull String entryClassName, @NonNull List args) { 34 | List cmdList = new ArrayList<>(); 35 | cmdList.add("/system/bin/app_process"); 36 | cmdList.add("-Djava.class.path=" + classPath); 37 | cmdList.add("/system/bin"); 38 | cmdList.add(entryClassName); 39 | cmdList.addAll(args); 40 | return new ProcessParams(cmdList, null, null); 41 | } 42 | 43 | public static T binderWithCleanCallingIdentity(Callable action) throws Exception { 44 | final long callingIdentity = Binder.clearCallingIdentity(); 45 | try { 46 | return action.call(); 47 | } finally { 48 | Binder.restoreCallingIdentity(callingIdentity); 49 | } 50 | } 51 | 52 | public static IBinder binderWrapper(IProcessManager manager, IBinder binder) { 53 | return new BinderWrapper(manager, binder); 54 | } 55 | 56 | public static boolean remoteTransact(IProcessManager manager, IBinder binder, int code, Parcel data, Parcel reply, int flags) { 57 | IBinder managerBinder = manager.asBinder(); 58 | Parcel processData = Parcel.obtain(); 59 | try { 60 | processData.writeInterfaceToken(Objects.requireNonNull(managerBinder.getInterfaceDescriptor())); 61 | processData.writeStrongBinder(binder); 62 | processData.writeInt(code); 63 | processData.writeInt(flags); 64 | processData.appendFrom(data, 0, data.dataSize()); 65 | return managerBinder.transact(IBinder.FIRST_CALL_TRANSACTION + 2, processData, reply, 0); 66 | } catch (RemoteException e) { 67 | throw new RuntimeException(e); 68 | } finally { 69 | processData.recycle(); 70 | } 71 | } 72 | 73 | public @NonNull Process start(@NonNull String classPath, @NonNull String entryClassName, @NonNull List args) throws IOException { 74 | return startProcess(generateProcessParams(classPath, entryClassName, args)); 75 | } 76 | 77 | public @NonNull Process start(@NonNull String classPath, @NonNull String entryClassName, @NonNull String[] args) throws IOException { 78 | return start(classPath, entryClassName, Arrays.asList(args)); 79 | } 80 | 81 | public @NonNull Process start(@NonNull String classPath, @NonNull Class entryClass, @NonNull List args) throws IOException { 82 | return start(classPath, entryClass.getName(), args); 83 | } 84 | 85 | public @NonNull Process start(@NonNull String classPath, @NonNull Class entryClass, @NonNull String[] args) throws IOException { 86 | return start(classPath, entryClass, Arrays.asList(args)); 87 | } 88 | 89 | public boolean init() { 90 | return init(ActivityThread.currentActivityThread().getApplication()); 91 | } 92 | 93 | public synchronized boolean init(@NonNull Context context) { 94 | if (initialized()) return true; 95 | mContext = context; 96 | 97 | IProcessManager manager = newManager(); 98 | if (manager == null) return false; 99 | mManager = manager; 100 | 101 | try { 102 | manager.asBinder().linkToDeath(() -> { 103 | if (manager.asBinder() != mManager.asBinder()) return; 104 | mManager = null; 105 | }, 0); 106 | } catch (RemoteException e) { 107 | e.printStackTrace(); 108 | } 109 | return initialized(); 110 | } 111 | 112 | /* 113 | * 启动一个新的Process,并返回一个Android的IBinder,方便进行远程进程管理 114 | * */ 115 | protected @Nullable IProcessManager newManager() { 116 | IBinder binder = isolatedServiceBinder(new ComponentName(mContext.getPackageName(), ProcessManager.class.getName())); 117 | if (binder == null) return null; 118 | return IProcessManager.Stub.asInterface(binder); 119 | } 120 | 121 | public boolean initialized() { 122 | return mContext != null && mManager != null && mManager.asBinder().isBinderAlive(); 123 | } 124 | 125 | @Override 126 | public void close() { 127 | mContext = null; 128 | if (mManager == null || !mManager.asBinder().pingBinder()) return; 129 | try { 130 | mManager.exit(0); 131 | } catch (RuntimeException rethrown) { 132 | throw rethrown; 133 | } catch (Exception ignored) { 134 | } 135 | // 无需在此处设置为null,因为已经在Binder::linkToDeath中实现了此操作,当ProcessManager::exit时,会调用到此方法 136 | // mManager = null; 137 | } 138 | 139 | /* 140 | * 根据传来的进程参数启动一个Process 141 | * */ 142 | protected @NonNull Process newProcess(@NonNull ProcessParams params) throws IOException { 143 | List cmdList = params.getCmdList(); 144 | Map env = params.getEnv(); 145 | String directory = params.getDirectory(); 146 | ProcessBuilder builder = new ProcessBuilder().command(cmdList); 147 | if (directory != null) builder = builder.directory(new File(directory)); 148 | if (env != null) builder.environment().putAll(env); 149 | return builder.start(); 150 | } 151 | 152 | private @NonNull Process startProcess(@NonNull ProcessParams params) throws IOException { 153 | if (!initialized()) return newProcess(params); 154 | return remoteProcess(params.getCmdList(), params.getEnv(), params.getDirectory()); 155 | } 156 | 157 | private @NonNull IProcessManager requireManager() { 158 | if (!initialized()) throw new IllegalStateException("please call init() first."); 159 | return mManager; 160 | } 161 | 162 | public boolean remoteTransact(IBinder binder, int code, Parcel data, Parcel reply, int flags) { 163 | return remoteTransact(requireManager(), binder, code, data, reply, flags); 164 | } 165 | 166 | public IBinder binderWrapper(IBinder binder) { 167 | return binderWrapper(requireManager(), binder); 168 | } 169 | 170 | public Process remoteProcess(@NonNull List cmdList, @Nullable Map env, @Nullable String directory) { 171 | try { 172 | return new RemoteProcess(requireManager().remoteProcess(cmdList, env, directory)); 173 | } catch (RemoteException e) { 174 | throw new RuntimeException(e); 175 | } 176 | } 177 | 178 | public IBinder serviceBinder(ComponentName componentName) { 179 | try { 180 | return requireManager().serviceBinder(componentName).getBinder(); 181 | } catch (RemoteException e) { 182 | throw new RuntimeException(e); 183 | } 184 | } 185 | 186 | private final Map locks = new HashMap<>(); 187 | 188 | private synchronized Object buildLock(String token) { 189 | Object lock = locks.get(token); 190 | if (lock == null) lock = new Object(); 191 | locks.put(token, lock); 192 | return lock; 193 | } 194 | 195 | public IBinder isolatedServiceBinder(@NonNull ComponentName componentName, boolean useCache) { 196 | if (!useCache) isolatedServiceBinderUnchecked(componentName); 197 | return isolatedServiceBinder(componentName); 198 | } 199 | 200 | public IBinder isolatedServiceBinder(@NonNull ComponentName componentName) { 201 | String token = componentName.flattenToString(); 202 | synchronized (buildLock(token)) { 203 | IBinder existsBinder = mChildProcess.get(token); 204 | if (existsBinder != null) return existsBinder; 205 | final IBinder binder = isolatedServiceBinderUnchecked(componentName); 206 | if (binder == null) return null; 207 | mChildProcess.put(token, binder); 208 | try { 209 | binder.linkToDeath(() -> { 210 | IBinder curBinder = mChildProcess.get(token); 211 | if (curBinder == null || curBinder != binder) return; 212 | mChildProcess.remove(token); 213 | }, 0); 214 | } catch (RemoteException e) { 215 | e.printStackTrace(); 216 | } 217 | return binder; 218 | } 219 | } 220 | 221 | private IBinder isolatedServiceBinderUnchecked(@NonNull ComponentName componentName) { 222 | return NewProcessReceiver.start(mContext, this, componentName); 223 | } 224 | 225 | public static class Default extends AppProcess { 226 | } 227 | 228 | /* 229 | * 不在新进程启动,而是直接在当前进程中进行 230 | * */ 231 | public static class None extends AppProcess { 232 | @Nullable 233 | @Override 234 | protected IProcessManager newManager() { 235 | return new ProcessManager(); 236 | } 237 | 238 | @Override 239 | public void close() { 240 | mManager = null; 241 | } 242 | } 243 | 244 | public abstract static class Terminal extends Default { 245 | protected abstract @NonNull List newTerminal(); 246 | 247 | @NonNull 248 | @Override 249 | protected Process newProcess(@NonNull ProcessParams params) throws IOException { 250 | ProcessParams newParams = new ProcessParams(params).setCmdList(newTerminal()); 251 | Process process = super.newProcess(newParams); 252 | PrintWriter printWriter = new PrintWriter(process.getOutputStream(), true); 253 | int count = 0; 254 | StringBuilder buffer = new StringBuilder(); 255 | for (String element : params.getCmdList()) { 256 | if (++count > 1) buffer.append(" "); 257 | buffer.append(element); 258 | } 259 | printWriter.println(buffer); 260 | printWriter.println("exit $?"); 261 | return process; 262 | } 263 | } 264 | 265 | public static class Root extends Terminal { 266 | 267 | @NonNull 268 | @Override 269 | protected List newTerminal() { 270 | List terminal = new ArrayList<>(); 271 | terminal.add("su"); 272 | return terminal; 273 | } 274 | } 275 | 276 | public static class RootSystem extends Terminal { 277 | @NonNull 278 | @Override 279 | protected List newTerminal() { 280 | List terminal = new ArrayList<>(); 281 | terminal.add("su"); 282 | terminal.add("1000"); 283 | return terminal; 284 | } 285 | } 286 | 287 | public static class ProcessParams { 288 | private @NonNull List mCmdList; 289 | 290 | private @Nullable Map mEnv; 291 | 292 | private @Nullable String mDirectory; 293 | 294 | public ProcessParams(@NonNull List cmdList, @Nullable Map env, @Nullable String directory) { 295 | this.mCmdList = cmdList; 296 | this.mEnv = env; 297 | this.mDirectory = directory; 298 | } 299 | 300 | public ProcessParams(@NonNull ProcessParams params) { 301 | this.mCmdList = new ArrayList<>(params.getCmdList()); 302 | Map env = params.getEnv(); 303 | this.mEnv = env != null ? new HashMap<>(env) : null; 304 | this.mDirectory = params.getDirectory(); 305 | } 306 | 307 | @NonNull 308 | public List getCmdList() { 309 | return mCmdList; 310 | } 311 | 312 | @Nullable 313 | public Map getEnv() { 314 | return mEnv; 315 | } 316 | 317 | @Nullable 318 | public String getDirectory() { 319 | return mDirectory; 320 | } 321 | 322 | public ProcessParams setCmdList(@NonNull List mCmdList) { 323 | this.mCmdList = mCmdList; 324 | return this; 325 | } 326 | 327 | public ProcessParams setEnv(@Nullable Map mEnv) { 328 | this.mEnv = mEnv; 329 | return this; 330 | } 331 | 332 | public ProcessParams setDirectory(@Nullable String mDirectory) { 333 | this.mDirectory = mDirectory; 334 | return this; 335 | } 336 | } 337 | } -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------