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
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package android.content.pm;

import android.content.IntentSender;
import android.os.Binder;
import android.os.IBinder;
import android.os.IInterface;
Expand All @@ -24,8 +25,9 @@
* Stub for {@code android.content.pm.IPackageInstaller}.
*/
public interface IPackageInstaller extends IInterface {
void abandonSession(int sessionId);
IPackageInstallerSession openSession(int sessionId);
void uninstall(String packageName, int flags, IntentSender statusReceiver, int userId);
void uninstall(String packageName, String callerPackageName, int flags, IntentSender statusReceiver, int userId);
abstract class Stub extends Binder implements IPackageInstaller {
public static IPackageInstaller asInterface(IBinder binder) {
throw new UnsupportedOperationException();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ public class LibsuPlugin : PrivilegedPlugin<
}

/**
* Uninstall parameters for [LibsuPlugin]. Uninstall flags take effect only on Android 8.1+.
* Uninstall parameters for [LibsuPlugin].
*/
public class UninstallParameters private constructor(snapshot: Snapshot) : PrivilegedUninstallParameters(snapshot) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import android.content.pm.PackageInstallerHidden
import android.os.Build
import android.os.Handler
import android.os.IBinder
import android.os.RemoteException
import android.os.UserHandleHidden
import androidx.annotation.RequiresApi
import androidx.annotation.RequiresPermission
Expand Down Expand Up @@ -58,6 +59,9 @@ public abstract class PackageInstallerProxy protected constructor(
val targetUser = installParameters[sessionId]?.targetUser
?: uninstallParameters[sessionId]?.targetUser
?: TargetUser.CURRENT
val installerPackageName = installParameters[sessionId]?.installerPackageName
?.ifEmpty { installerPackageName }
?: installerPackageName
val resolvedUserId = if (targetUser == TargetUser.CURRENT) {
UserHandleHidden.myUserId()
} else {
Expand All @@ -66,7 +70,7 @@ public abstract class PackageInstallerProxy protected constructor(
val packageInstaller = packageInstallers.computeIfAbsentCompat(resolvedUserId, packageInstallerLocks) {
createPackageInstaller(context, remotePackageInstaller, installerPackageName, resolvedUserId)
}
return BoundPackageInstaller(packageInstaller!!)
return BoundPackageInstaller(packageInstaller!!, resolvedUserId, installerPackageName)
}

final override fun createSession(
Expand Down Expand Up @@ -106,7 +110,9 @@ public abstract class PackageInstallerProxy protected constructor(
)

private inner class BoundPackageInstaller(
private val packageInstaller: PackageInstaller
private val packageInstaller: PackageInstaller,
private val userId: Int,
private val installerPackageName: String
) : PackageInstallerService {

override val uid: Int
Expand Down Expand Up @@ -166,21 +172,35 @@ public abstract class PackageInstallerProxy protected constructor(

override fun abandonSession(sessionId: Int): Unit = packageInstaller.abandonSession(sessionId)

@Suppress("CAST_NEVER_SUCCEEDS")
@RequiresPermission(anyOf = [Manifest.permission.REQUEST_DELETE_PACKAGES, Manifest.permission.DELETE_PACKAGES])
override fun uninstall(packageName: String, statusReceiver: IntentSender, ackpineSessionId: UUID) {
if (Build.VERSION.SDK_INT < 27) {
packageInstaller.uninstall(packageName, statusReceiver)
return
}
val privilegedParameters = uninstallParameters[ackpineSessionId]
var flags = 0
if (privilegedParameters != null) {
flags = applyFlag(flags, privilegedParameters.keepData, DELETE_KEEP_DATA)
flags = applyFlag(flags, privilegedParameters.allUsers, DELETE_ALL_USERS)
flags = applyFlag(flags, privilegedParameters.systemApp, DELETE_SYSTEM_APP)
val flags = uninstallParameters[ackpineSessionId]?.toFlags() ?: 0
when {
Build.VERSION.SDK_INT >= 27 -> (packageInstaller as PackageInstallerHidden).uninstall(
packageName,
flags,
statusReceiver
)

Build.VERSION.SDK_INT >= 23 -> try {
remotePackageInstaller.uninstall(
packageName,
installerPackageName,
flags,
statusReceiver,
userId
)
} catch (e: RemoteException) {
e.rethrow()
}

else -> try {
remotePackageInstaller.uninstall(packageName, flags, statusReceiver, userId)
} catch (e: RemoteException) {
e.rethrow()
}
}
@Suppress("CAST_NEVER_SUCCEEDS")
(packageInstaller as PackageInstallerHidden).uninstall(packageName, flags, statusReceiver)
}
}

Expand Down Expand Up @@ -245,10 +265,26 @@ public abstract class PackageInstallerProxy protected constructor(
params.installFlags = flags
}

private fun PrivilegedUninstallParameters.toFlags(): Int {
var flags = 0
flags = applyFlag(flags, keepData, DELETE_KEEP_DATA)
flags = applyFlag(flags, allUsers, DELETE_ALL_USERS)
flags = applyFlag(flags, systemApp, DELETE_SYSTEM_APP)
return flags
}

private fun applyFlag(flags: Int, isFlagPresent: Boolean, flag: Int): Int {
if (isFlagPresent) {
return flags or flag
}
return flags and flag.inv()
}

@Suppress("NewApi", "ThrowableNotThrown") // methods are available, but were hidden before API 30
private fun RemoteException.rethrow() {
if (Build.VERSION.SDK_INT >= 24) {
rethrowFromSystemServer()
}
rethrowAsRuntimeException()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,7 @@ public abstract class PrivilegedInstallParameters protected constructor(snapshot
public val allUsers: Boolean = snapshot.allUsers

/**
* Installer package for the app. Empty by default, so the calling app package name will be used. Works only on
* Android 9+.
* Installer package for the app. Empty by default, so the calling app package name will be used.
*/
public val installerPackageName: String = snapshot.installerPackageName

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,25 +76,17 @@ public abstract class PrivilegedPlugin<
}

override fun getCapabilities(context: UninstallCapabilityContext): UninstallCapabilities {
val isPackageInstallerBased = context.uninstallerType == UninstallerType.PACKAGE_INSTALLER_BASED
val flagsStatus = if (
isPackageInstallerBased &&
context.sdkInt >= Build.VERSION_CODES.O_MR1
) {
val status = if (context.uninstallerType == UninstallerType.PACKAGE_INSTALLER_BASED) {
CapabilityStatus.SUPPORTED
} else {
CapabilityStatus.UNSUPPORTED
}
return createUninstallCapabilities(
PrivilegedUninstallCapabilities.Snapshot(
keepData = flagsStatus,
allUsers = flagsStatus,
systemApp = flagsStatus,
targetUser = if (isPackageInstallerBased) {
CapabilityStatus.SUPPORTED
} else {
CapabilityStatus.UNSUPPORTED
}
keepData = status,
allUsers = status,
systemApp = status,
targetUser = status
)
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ import ru.solrudev.ackpine.plugability.AckpinePlugin
/**
* Shared uninstall parameters for privileged Ackpine plugins.
*/
public abstract class PrivilegedUninstallParameters protected constructor(snapshot: Snapshot) : AckpinePlugin.Parameters {
public abstract class PrivilegedUninstallParameters protected constructor(
snapshot: Snapshot
) : AckpinePlugin.Parameters {

/**
* Flag parameter to indicate that you don't want to delete the package's data directory.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ public class ShizukuPlugin private constructor() : PrivilegedPlugin<
}

/**
* Uninstall parameters for [ShizukuPlugin]. Uninstall flags take effect only on Android 8.1+.
* Uninstall parameters for [ShizukuPlugin].
*/
public class UninstallParameters private constructor(snapshot: Snapshot) : PrivilegedUninstallParameters(snapshot) {

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.1 (2026-07-20)
---------------------------

### Bug fixes and improvements

- Uninstall flags for privileged (Shizuku/root) sessions now work on Android 5.0+.
- Setting a custom installer package name for privileged (Shizuku/root) install sessions now works on Android 5.0+.

Version 0.25.0 (2026-07-19)
---------------------------

Expand Down
4 changes: 1 addition & 3 deletions docs/guide/libsu.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,12 +200,10 @@ Flag to indicate that this install should immediately be visible to all users.

#### `installerPackageName`

Installer package for the app. Empty by default, so the calling app package name will be used. Takes effect only on Android 9+.
Installer package for the app. Empty by default, so the calling app package name will be used.

### Uninstall flags

Take effect only on Android 8.1+.

#### `keepData`

Flag parameter to indicate that you don't want to delete the package's data directory.
Expand Down
4 changes: 1 addition & 3 deletions docs/guide/shizuku.md
Original file line number Diff line number Diff line change
Expand Up @@ -223,12 +223,10 @@ Flag to indicate that this install should immediately be visible to all users.

#### `installerPackageName`

Installer package for the app. Empty by default, so the calling app package name will be used. Takes effect only on Android 9+.
Installer package for the app. Empty by default, so the calling app package name will be used.

### Uninstall flags

Take effect only on Android 8.1+.

#### `keepData`

Flag parameter to indicate that you don't want to delete the package's data directory.
Expand Down
2 changes: 1 addition & 1 deletion requirements.in
Original file line number Diff line number Diff line change
@@ -1 +1 @@
zensical==0.0.47
zensical==0.0.51
28 changes: 14 additions & 14 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -251,18 +251,18 @@ tomli==2.4.1 \
--hash=sha256:ff18e6a727ee0ab0388507b89d1bc6a22b138d1e2fa56d1ad494586d61d2eae9 \
--hash=sha256:ff2983983d34813c1aeb0fa89091e76c3a22889ee83ab27c5eeb45100560c049
# via zensical
zensical==0.0.47 \
--hash=sha256:1bd94937c48a2e42b5b65b32c5075849937f23cebccaf250d249efa27266e0be \
--hash=sha256:2ee29ff819372eaab02ca0f14ac82e804332d898c57c56ffd4d89674f3e5ff71 \
--hash=sha256:319cf370ecc6d87da69c935c5acd6e1959dd48954766e9f954319bdb0bec5d36 \
--hash=sha256:324f783b22cd0deed0d0f3b69e28d5380b47238a1ef0f25913b88014af2bade2 \
--hash=sha256:4162fb8b62f38e6d9b75688c1fb87e18a1cffef5eee51d2c18b79334b548e973 \
--hash=sha256:4702605b991bece11494a9bb318d5ba7229f00e5adcc9e6010acd58e9719686b \
--hash=sha256:59656bf604a8b03eede4ce1a847640bab1129ab86dec2e39e5bd4b808b1802d2 \
--hash=sha256:6f6b2de477c45284201e92301f997415f45f78493bd20f479ea9a1c86bbbbcca \
--hash=sha256:77f08ffcc3da9ca2f972330e501927aa7e8e445bfa7b758107edc645acda266e \
--hash=sha256:81a13a8bacadedada4847eed4aaa4a3f4ef0e5b78dbb2a2022bc6fb7e7dc9464 \
--hash=sha256:944a309be69b11daa8bba46c61fb74f32a98b637f3e7c11135e7cc2a5eccbe32 \
--hash=sha256:97ed2b21aba5f788fc39d1597d00938d602b4d2724ed599a1dab7548fe4f0025 \
--hash=sha256:cdc2d84f38da809a28402eda5f2b6dbb150e14427f296c5b521e0dfc6a2e8a39
zensical==0.0.51 \
--hash=sha256:12529d3d3991b63820952111dc1d1edc29b2c9b3a3abb16c243bcb649631ebf2 \
--hash=sha256:134d776afa526098e05e34713e2f577c075e57a232e01b97842bb0206716afce \
--hash=sha256:186ef37e0eee0e969e2cfae47b1b97775e3164e2cba95c71faa4dd6ef47ed009 \
--hash=sha256:296dc7a14aa28b81a58eb57df2d5c9c9a4b0de7e90c11d99c943354287952925 \
--hash=sha256:3c9579809f88608e7aa2cff516fff9d267d74a843cf6088a5f4227de2f092bb5 \
--hash=sha256:67f813a1514a90890ca86248a8d54b81b2164bcbff11a6bcf11b01e1c01a1454 \
--hash=sha256:6fa0ecaf14f56841bfc595fa141396350c72aafbec73a016ebe3c824ed21ac72 \
--hash=sha256:b1108eae82c6e8ffc33026f60b485c1512647a5333be4f547166b7c8877b98af \
--hash=sha256:de25de067bedfa18f916d7f366fd64a7fbf09bfcc615b44d1ddbe3b5fe02ab49 \
--hash=sha256:e97ab39668ae3b452c550634e921a0336443743aae5e1fe031c7bb57d049e535 \
--hash=sha256:f5d91ce246ed930224603083cef02ae8947132fc7c52901d72015ea03526fa58 \
--hash=sha256:f779d2d87b4bf228cf2e279bc0ae6bcf3b36a9335ff283a317d01f7c15ae46b2 \
--hash=sha256:fb7ff4946b72168759c6af0a29cf5de4c38aebe633a83292e8cd4145b5213cc2
# via -r requirements.in
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": 0,
"patchVersion": 1,
"suffix": "",
"isSnapshot": false
}