Android: getting an APKs minSdkVersion from Android code [duplicate]
Asked Answered
M

1

7

I'm developing an Android app that has the ability of installing additional apps (which act as plugins for my app) if the user requires it.

However, each of these additional apps may require a specific Android version to run. I would like to perform a check at runtime to see if the APK I'm trying to install is actually compatible with the device.

Now, with the following method:

public PackageManager getPackageArchiveInfo(String archiveFilePath, int flags)

I can get info on an APK file. However, the problem is that the returned information seems to only include the APK's targetSdkVersion but not the minSdkVersion, which to my understanding is the one that actually determines the minimum version of Android an app can be installed/run on. The targetSdkVersion if I understand correctly is just the "optimal" version.

So, long story short, how can I determine whether an APK cna run on the device from Android itself? (I know I can use AAPT on desktop, but that is not available on Android itself)

Metatarsal answered 26/5, 2015 at 10:25 Comment(1)
hey, did you find an answer for that? thanksToulon
P
5

You can do it.

For Android N and above, use the official API.

For earlier versions, you can use this code, which is very efficient and fast. Here's a bit better version of it (works even if getAttributeName returns an empty string) :

public static int getMinSdkVersion(File apkFile) throws ClassNotFoundException, IllegalAccessException, InstantiationException,
        NoSuchMethodException, InvocationTargetException, IOException, XmlPullParserException {
    final Class assetManagerClass = Class.forName("android.content.res.AssetManager");
    final AssetManager assetManager = (AssetManager) assetManagerClass.newInstance();
    final Method addAssetPath = assetManager.getClass().getMethod("addAssetPath", String.class);
    final int cookie = (Integer) addAssetPath.invoke(assetManager, apkFile.getAbsolutePath());
    final XmlResourceParser parser = assetManager.openXmlResourceParser(cookie, "AndroidManifest.xml");
    while (parser.next() != XmlPullParser.END_DOCUMENT)
        if (parser.getEventType() == XmlPullParser.START_TAG && parser.getName().equals("uses-sdk"))
            for (int i = 0; i < parser.getAttributeCount(); ++i)
                if (parser.getAttributeNameResource(i) == android.R.attr.minSdkVersion)//alternative, which works most of the times: "minSdkVersion".equals(parser.getAttributeName(i)))
                    return parser.getAttributeIntValue(i, -1);
    return -1;
}

And, if you want an all around solution (which sadly can take a lot of heap memory and time), you can use an APK parsing library, such as APKParser. If you want just the basics of it, consider the APKParser improvement I suggested here.

Proptosis answered 25/4, 2017 at 6:42 Comment(4)
I think the question is duplicate and it should be closed as such, yet your answer adds some value. Still, would you mind commenting/editing the answer you linked to so we'd close this question as dup?Ylem
@MarcinOrlowski What's wrong with what I wrote?Proptosis
There's nothing wrong with your answer, I even +1, yet the question is duplicate and you already linked to the one that contains extensive answer. I'd normally close it as duplicate but your answer adds additional value combined with the other answer. Also the other Q got 10 times more views therefore putting your answer as i.e. comment to accepted answer there is simply better and it adds more value than keeping this question opened.Ylem
Do as you wish. I don't mind about those rules. For me it's an answer to a question, like many others, which is the point to this website. I don't like the strict rules being enforced here sometimes.Proptosis

© 2022 - 2024 — McMap. All rights reserved.