Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get minSdkVersion and targetSdkVersion from apk file

I am trying to get the values of minSdkVersion and targetSdkVersion from an apk stored on the device. Getting other details are discussed here, but only the targetSdkVersion is available in the ApplicationInfo class. Can the minSdkVersion be obtained other than by extracting the apk file and reading AndroidManifest.xml?

like image 890
Ruben Roy Avatar asked Dec 04 '13 09:12

Ruben Roy


People also ask

What is minsdkversion attribute in Android Studio?

The value of the attribute is an integer representing the Android API level. So, if you are only testing your application on Android 2.1 and newer versions of Android, you would set your android:minSdkVersion to be 7. Note that your app will not be available for installation in older versions of android devices.

What is the minimum SDK version and target SDK version?

sdkInfo: minSdkVersion: '11' targetSdkVersion: '17' You can match the SDK version to Android version here. In the above example minimum Android version is 3.0 and target version is 4.2.

What is targetSdkVersion in android testing?

That’s where targetSdkVersion comes into play. targetSdkVersion is a property that tells the system for which Android version the app was designed and tested on.

How to change Min SDK version in Android Studio?

Click android studio menu ” File —> Project Structure “. In Project Structure dialog, select app in Modules list. Select Flavors tab in right panel, then you can select your desired android Min Sdk Version and Target Sdk Version.


2 Answers

I do not believe this is possible to do on your own and there is no pre-made api for this. The current methods that read and parse the AndroidManifest do not consider minSdkVersion at all.

In order to check your apk file without using the ready made functions you end up needing to add it manually to the asset manager. And that method is marked with "Not for use by applications" which in my experience usually means that it's not a good idea to call it from an application.

http://androidxref.com/5.1.1_r6/xref/frameworks/base/core/java/android/content/res/AssetManager.java#612

If you do manage to call:

public final int addAssetPath(String path) {

From your application you should be able to get the minSdkVersion by parsing the XML file, consider this code:

private static final String ANDROID_MANIFEST_FILENAME = "AndroidManifest.xml";

....
method:

final int cookie = loadApkIntoAssetManager(assets, apkPath, flags);

Resources res = null;
XmlResourceParser parser = null;
try {
    res = new Resources(assets, mMetrics, null);
    assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            Build.VERSION.RESOURCES_SDK_INT);
    parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);

    final String[] outError = new String[1];
    final Package pkg = parseBaseApk(res, parser, flags, outError);
    if (pkg == null) {
        throw new PackageParserException(mParseError,
                apkPath + " (at " + parser.getPositionDescription() + "): " + outError[0]);
    }
}

Code: http://androidxref.com/5.1.1_r6/xref/frameworks/base/core/java/android/content/pm/PackageParser.java#863

Where you should be able to parse your AndroidManifest file using the XmlResourceParser and find the element for the minSdkVersion.

If you want to try it out yourself, just copy following static methods and call getMinSdkVersion(yourApkFile):

/**
 * Parses AndroidManifest of the given apkFile and returns the value of
 * minSdkVersion using undocumented API which is marked as
 * "not to be used by applications"
 * 
 * @param apkFile
 * @return minSdkVersion or -1 if not found in Manifest
 * @throws IOException
 * @throws XmlPullParserException
 */
public static int getMinSdkVersion(File apkFile) throws IOException,
        XmlPullParserException {

    XmlResourceParser parser = getParserForManifest(apkFile);
    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.getAttributeName(i).equals("minSdkVersion")) {
                    return parser.getAttributeIntValue(i, -1);
                }
            }
        }
    }
    return -1;

}

/**
 * Tries to get the parser for the given apkFile from {@link AssetManager}
 * using undocumented API which is marked as
 * "not to be used by applications"
 * 
 * @param apkFile
 * @return
 * @throws IOException
 */
private static XmlResourceParser getParserForManifest(final File apkFile)
        throws IOException {
    final Object assetManagerInstance = getAssetManager();
    final int cookie = addAssets(apkFile, assetManagerInstance);
    return ((AssetManager) assetManagerInstance).openXmlResourceParser(
            cookie, "AndroidManifest.xml");
}

/**
 * Get the cookie of an asset using an undocumented API call that is marked
 * as "no to be used by applications" in its source code
 * 
 * @see <a
 *      href="http://androidxref.com/5.1.1_r6/xref/frameworks/base/core/java/android/content/res/AssetManager.java#612">AssetManager.java#612</a>
 * @return the cookie
 */
private static int addAssets(final File apkFile,
        final Object assetManagerInstance) {
    try {
        Method addAssetPath = assetManagerInstance.getClass().getMethod(
                "addAssetPath", new Class[] { String.class });
        return (Integer) addAssetPath.invoke(assetManagerInstance,
                apkFile.getAbsolutePath());
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }
    return -1;
}

/**
 * Get {@link AssetManager} using reflection
 * 
 * @return
 */
private static Object getAssetManager() {
    Class assetManagerClass = null;
    try {
        assetManagerClass = Class
                .forName("android.content.res.AssetManager");
        Object assetManagerInstance = assetManagerClass.newInstance();
        return assetManagerInstance;
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } catch (InstantiationException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }
    return null;
}

You may need a reflection call to set this as well:

assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            Build.VERSION.RESOURCES_SDK_INT);

No guarantees that it will work (nor that it won't be bad for your phone) the operation should be safe since you're creating a new AssetManager and not relying on the AssetManager for your application. From a quick look in the C++ code it seems that it's not being added to any global list.

Code: http://androidxref.com/5.1.1_r6/xref/frameworks/base/libs/androidfw/AssetManager.cpp#173

like image 160
JohanShogun Avatar answered Oct 04 '22 18:10

JohanShogun


Use aapt:

aapt list -a package.apk | grep SdkVersion

You will see version numbers in hex. e.g.:

A: android:minSdkVersion(0x0101020c)=(type 0x10)0x3 A: android:targetSdkVersion(0x01010270)=(type 0x10)0xc

For this apk, minSdkVersion is 0x3 i.e. 3, and targetSdkVersion is 0xc i.e. 12.

Edited answer below :-

Then you can achieve it by reverse engineering you can get the source code from the apk by following steps Procedure for decoding .apk files, step-by-step method: Step 1:

Make a new folder and copy over the .apk file that you want to decode.

Now rename the extension of this .apk file to .zip (e.g. rename from filename.apk to filename.zip) and save it. Now you can access the classes.dex files, etc. At this stage you are able to see drawables but not xml and java files, so continue.

Step 2:

Now extract this .zip file in the same folder (or NEW FOLDER).

Download dex2jar and extract it to the same folder (or NEW FOLDER).

Move the classes.dex file into the dex2jar folder.

Now open command prompt and change directory to that folder (or NEW FOLDER). Then write d2j-dex2jar classes.dex (for mac terminal or ubuntu write ./d2j-dex2jar.sh classes.dex) and press enter. You now have the classes.dex.dex2jar file in the same folder.

Download java decompiler, double click on jd-gui, click on open file, and open classes.dex.dex2jar file from that folder: now you get class files.

Save all of these class files (In jd-gui, click File -> Save All Sources) by src name. At this stage you get the java source but the .xml files are still unreadable, so continue.

Step 3:

Now open another new folder

Put in the .apk file which you want to decode

Download the latest version of apktool AND apktool install window (both can be downloaded from the same link) and place them in the same folder

Download framework-res.apk and put it in the same folder (Not all apk file need this file, but it doesn't hurt)

Open a command window

Navigate to the root directory of APKtool and type the following command: apktool if framework-res.apk

apktool d myApp.apk (where myApp.apk denotes the filename that you want to decode)

now you get a file folder in that folder and can easily read the apk's xml files. Step 4:

It's not any step just copy contents of both folder(in this case both new folder)to the single one

and enjoy the source code...

like image 40
kuljeet singh Avatar answered Oct 04 '22 18:10

kuljeet singh