Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion ackpine-plugins/libsu/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -46,5 +46,4 @@ dependencies {
implementation(projects.ackpineRuntime)
implementation(androidx.room.runtime)
implementation(libs.libsu.service)
implementation(libs.hiddenApiBypass)
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,9 @@ package ru.solrudev.ackpine.libsu
import android.content.Context
import android.content.pm.IPackageInstaller
import android.content.pm.IPackageManager
import android.os.Build
import android.os.IBinder
import android.os.ServiceManager
import androidx.annotation.RestrictTo
import org.lsposed.hiddenapibypass.HiddenApiBypass
import ru.solrudev.ackpine.plugability.AckpinePlugin
import ru.solrudev.ackpine.privileged.PackageInstallerProxy
import java.util.UUID
Expand All @@ -48,16 +46,6 @@ internal class RootPackageInstaller(

@JvmSynthetic
internal fun create(context: Context): RootPackageInstaller {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
HiddenApiBypass.addHiddenApiExemptions(
"Landroid/content/pm/IPackageManager",
"Landroid/content/pm/IPackageInstaller",
"Landroid/content/pm/IPackageInstallerSession",
"Landroid/content/pm/PackageInstaller",
"Landroid/os/UserHandle",
"Landroid/os/ServiceManager"
)
}
val rootService = RootProxyService.bind(context)
val remotePackageManager = IPackageManager.Stub.asInterface(
RootProxyBinderWrapper(rootService, ServiceManager.getService("package"))
Expand Down
1 change: 1 addition & 0 deletions ackpine-plugins/privileged/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,5 @@ dependencies {
api(projects.ackpineCore)
compileOnly(projects.ackpinePlugins.androidStubs)
implementation(projects.ackpineRuntime)
implementation(libs.hiddenApiBypass)
}
32 changes: 32 additions & 0 deletions ackpine-plugins/privileged/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2026 Ilya Fomichev
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

<application>

Check warning on line 21 in ackpine-plugins/privileged/src/main/AndroidManifest.xml

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Make sure backup of application data is safe here.

See more on https://sonarcloud.io/project/issues?id=solrudev_Ackpine&issues=AZ99939DF7HKEtxhZxp8&open=AZ99939DF7HKEtxhZxp8&pullRequest=217

Check warning on line 21 in ackpine-plugins/privileged/src/main/AndroidManifest.xml

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

"usesCleartextTraffic" is implicitly enabled for older Android versions. Make sure allowing clear-text traffic is safe here.

See more on https://sonarcloud.io/project/issues?id=solrudev_Ackpine&issues=AZ99939DF7HKEtxhZxp7&open=AZ99939DF7HKEtxhZxp7&pullRequest=217
Comment thread
sonarqubecloud[bot] marked this conversation as resolved.
Dismissed
Comment thread
sonarqubecloud[bot] marked this conversation as resolved.
Dismissed
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="merge">
<meta-data
android:name="ru.solrudev.ackpine.privileged.HiddenApiExemptionsInitializer"
android:value="androidx.startup" />
</provider>
</application>
</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright (C) 2026 Ilya Fomichev
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package ru.solrudev.ackpine.privileged

import android.content.Context
import android.os.Build
import androidx.annotation.RestrictTo
import androidx.startup.Initializer
import org.lsposed.hiddenapibypass.HiddenApiBypass

/**
* Initializes hidden API exemptions using `androidx.startup`.
*/
@RestrictTo(RestrictTo.Scope.LIBRARY)
internal class HiddenApiExemptionsInitializer : Initializer<Unit> {

override fun create(context: Context) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
return
}
HiddenApiBypass.setHiddenApiExemptions(
"Landroid/content/pm/IPackageManager",
"Landroid/content/pm/IPackageInstaller",
"Landroid/content/pm/IPackageInstallerSession",
"Landroid/content/pm/PackageInstaller",
"Landroid/os/UserHandle",
"Landroid/os/ServiceManager"
)
}

override fun dependencies(): List<Class<out Initializer<*>>> {
return emptyList()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public abstract class PrivilegedPlugin<
requestDowngrade = isSupported,
grantAllRequestedPermissions = isSupportedOnApi(Build.VERSION_CODES.M),
allUsers = isSupported,
installerPackageName = isSupportedOnApi(Build.VERSION_CODES.P),
installerPackageName = isSupported,
targetUser = isSupported
)
)
Expand Down
1 change: 0 additions & 1 deletion ackpine-plugins/shizuku/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -46,5 +46,4 @@ dependencies {
implementation(projects.ackpineRuntime)
implementation(androidx.room.runtime)
implementation(libs.shizuku.api)
implementation(libs.hiddenApiBypass)
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,8 @@ import android.content.Context
import android.content.pm.IPackageInstaller
import android.content.pm.IPackageManager
import android.content.pm.PackageInstaller
import android.os.Build
import android.os.IBinder
import androidx.annotation.RestrictTo
import org.lsposed.hiddenapibypass.HiddenApiBypass
import rikka.shizuku.Shizuku
import rikka.shizuku.ShizukuBinderWrapper
import rikka.shizuku.SystemServiceHelper
Expand Down Expand Up @@ -56,15 +54,6 @@ internal class ShizukuPackageInstaller(

@JvmSynthetic
internal fun create(context: Context): ShizukuPackageInstaller {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
HiddenApiBypass.addHiddenApiExemptions(
"Landroid/content/pm/IPackageManager",
"Landroid/content/pm/IPackageInstaller",
"Landroid/content/pm/IPackageInstallerSession",
"Landroid/content/pm/PackageInstaller",
"Landroid/os/UserHandle"
)
}
val remotePackageManager = IPackageManager.Stub.asInterface(
ShizukuBinderWrapper(SystemServiceHelper.getSystemService("package"))
)
Expand Down
8 changes: 8 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@ hide:
Change Log
==========

Version 0.25.2 (2026-07-20)
---------------------------

### Bug fixes and improvements

- Fix `installerPackageName` privileged plugins capability incorrectly reported as unsupported on API < 28.
- Set hidden API exemptions only once for all privileged plugins using AndroidX startup. On future Android versions, multiple calls to `HiddenApiBypass.setHiddenExemptions()` can result in a no-op or throw. If you have your own exemptions, disable Ackpine's privileged plugins exemptions initializer and add its exemptions list to your `setHiddenExemptions` call. See docs for details.

Version 0.25.1 (2026-07-20)
---------------------------

Expand Down
32 changes: 32 additions & 0 deletions docs/guide/libsu.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,38 @@ Also, you can use libsu for uninstall sessions:

Plugin doesn't close the cached shell by itself to avoid interfering with other possible in-flight shell operations.

Hidden APIs
-----------

This plugin initializes hidden Android API exemptions through AndroidX Startup using [LSPosed's HiddenApiBypass](https://github.com/LSPosed/AndroidHiddenApiBypass). In case you have your own additional exemptions:

1. Disable the initializer in `AndroidManifest.xml`:

```xml
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="merge">
<meta-data
android:name="ru.solrudev.ackpine.privileged.HiddenApiExemptionsInitializer"
tools:node="remove" />
</provider>
```

2. Add this list to your `HiddenApiBypass.setHiddenApiExemptions` call:

```kotlin
HiddenApiBypass.setHiddenApiExemptions(
"Landroid/content/pm/IPackageManager",
"Landroid/content/pm/IPackageInstaller",
"Landroid/content/pm/IPackageInstallerSession",
"Landroid/content/pm/PackageInstaller",
"Landroid/os/UserHandle",
"Landroid/os/ServiceManager"
)
```

Plugin parameters
-----------------

Expand Down
32 changes: 32 additions & 0 deletions docs/guide/shizuku.md
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,38 @@ Also, you can use Shizuku for uninstall sessions:

If Shizuku service is not running, or if Shizuku permission is not granted for your app, session will fail.

Hidden APIs
-----------

This plugin initializes hidden Android API exemptions through AndroidX Startup using [LSPosed's HiddenApiBypass](https://github.com/LSPosed/AndroidHiddenApiBypass). In case you have your own additional exemptions:

1. Disable the initializer in `AndroidManifest.xml`:

```xml
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="merge">
<meta-data
android:name="ru.solrudev.ackpine.privileged.HiddenApiExemptionsInitializer"
tools:node="remove" />
</provider>
```

2. Add this list to your `HiddenApiBypass.setHiddenApiExemptions` call:

```kotlin
HiddenApiBypass.setHiddenApiExemptions(
"Landroid/content/pm/IPackageManager",
"Landroid/content/pm/IPackageInstaller",
"Landroid/content/pm/IPackageInstallerSession",
"Landroid/content/pm/PackageInstaller",
"Landroid/os/UserHandle",
"Landroid/os/ServiceManager"
)
```

Plugin parameters
-----------------

Expand Down
2 changes: 1 addition & 1 deletion version.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"majorVersion": 0,
"minorVersion": 25,
"patchVersion": 1,
"patchVersion": 2,
"suffix": "",
"isSnapshot": false
}
Loading