How to check if an application has unknown sources in Android 8.0

Viewed 1195

Is it possible to check if another app can install unknown sources? prior to Oreo the following code worked:

int isUnknownAllowed = Settings.Secure.getInt(context.getContentResolver(), Settings.Secure.INSTALL_NON_MARKET_APPS, 0);
return isUnknownAllowed == 1

In Oreo, the permission is granted per-app as described in the documentation, and the check is now:

getPackageManager().canRequestPackageInstalls()

But this only returns whether the current app can install packages.

Can I check if another app has this permission?

1 Answers

The implementation of canRequestPackageInstalls() uses the internal API IPackageManager, which actually takes a package name argument:

@Override
public boolean canRequestPackageInstalls() {
    try {
        return mPM.canRequestPackageInstalls(mContext.getPackageName(), mContext.getUserId());
    } catch (RemoteException e) {
        throw e.rethrowAsRuntimeException();
    }
}

Using reflection or a modified SDK JAR, it's possible to call API directly (note: if your app targets Android 9 or later, you'll need to bypass the hidden API restrictions):

IPackageManager.Stub.asInterface(ServiceManager.getService("package")).canRequestPackageInstalls("some.package.name", context.userId)

Unfortunately, the underlying PackageManagerService has a check that prevents normal apps from checking other apps' install status:

private boolean canRequestPackageInstallsInternal(String packageName, int flags, int userId,
        boolean throwIfPermNotDeclared) {
    int callingUid = Binder.getCallingUid();
    int uid = getPackageUid(packageName, 0, userId);
    if (callingUid != uid && callingUid != Process.ROOT_UID
            && callingUid != Process.SYSTEM_UID) {
        throw new SecurityException(
                "Caller uid " + callingUid + " does not own package " + packageName);
    }
    ...
}

Unless your app's UID and the UID of the package name you pass match (or your app is root or a system app), you can't check other apps.

There is a workaround for this, which works best on Android 11 or later: use Shizuku. Shizuku lets you call Java APIs with ADB/shell permissions, which should let you use the API:

IPackageManager.Stub.asInterface(SystemServiceWrapper(SystemServiceHelper.getService("package"))).canRequestPackageInstalls("some.package.name", context.userId)

If you need a silent/zero-interaction way to check, I don't think you can without root access.

Related