236

How can I get programmatically get the version code or version name of my apk from the AndroidManifest.xml file after downloading it and without installing it.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="xxx.xx.xxx"
    android:versionCode="1"
    android:versionName="1.1" >

For example I want to check if a new version is uploaded on my IIS service, after install it on device, if it is not a new version I don't want to install it.

Benjamin Loison
  • 3,782
  • 4
  • 16
  • 33
Big.Child
  • 2,948
  • 4
  • 19
  • 26
  • check this http://stackoverflow.com/a/4761689/1109425 – nandeesh Nov 20 '12 at 08:32
  • BTW, I hope no one thinks a good solution is to download the file, and *then* check whether it is needed - Which is what the first sentence suggests. Presumably you would instead want code that runs *on your server*, and responds with the version number that is available. – ToolmakerSteve Feb 07 '17 at 17:47
  • 1
    `./aapt d badging release.apk | grep -Po "(?<=\sversion(Code|Name)=')([0-9.]+)"` – LF00 Sep 09 '19 at 09:05
  • aapt will cost about `2.6M` disk space, you can also write a parser to parse the apk file with `AXMLResource` which will just cost about `52k`. Refer to [Java parse xml with undeclared namespace](https://stackoverflow.com/q/57193580/6521116) – LF00 Sep 09 '19 at 10:34

12 Answers12

501

Following worked for me from the command line:

aapt dump badging myapp.apk

NOTE: aapt.exe is found in a build-tools sub-folder of SDK. For example:

<sdk_path>/build-tools/23.0.2/aapt.exe
Benjamin Loison
  • 3,782
  • 4
  • 16
  • 33
Sileria
  • 15,223
  • 4
  • 49
  • 28
102
final PackageManager pm = getPackageManager();
String apkName = "example.apk";
String fullPath = Environment.getExternalStorageDirectory() + "/" + apkName;        
PackageInfo info = pm.getPackageArchiveInfo(fullPath, 0);
Toast.makeText(this, "VersionCode : " + info.versionCode + ", VersionName : " + info.versionName, Toast.LENGTH_LONG).show();
Benjamin Loison
  • 3,782
  • 4
  • 16
  • 33
Patrick Cho
  • 1,321
  • 1
  • 10
  • 8
  • 6
    how can I read `apk` content **without installing** the application? @PatrickCho – talha06 Feb 22 '14 at 14:27
  • 4
    This does indeed work. And you don't need to install it. You just have an apk somewhere on the your device's storage, and supply the path to it to the package manager. It will indeed return the package info. – Daniel Zolnai Mar 07 '15 at 14:07
  • 2
    this is the best answer imo – buradd Oct 28 '20 at 16:41
57

If you are using version 2.2 and above of Android Studio then in Android Studio use Build Analyze APK then select AndroidManifest.xml file.

vovahost
  • 34,185
  • 17
  • 113
  • 116
  • 14
    OP asked how to get it `programmatically` – forresthopkinsa Sep 22 '17 at 23:11
  • 5
    Because he didn't know that a tool like this is built in Android Studio. He also asked *without installing the apk*. – vovahost Sep 23 '17 at 08:22
  • 8
    Of course he didn't know that, because Android Studio didn't exist when this question was asked. Additionally, even if it did, this does not solve his problem -- he wants his device to *programmatically* check for updates from his IIS server, as he stated in the question. Patrick Cho's answer explains how to do it programmatically without installing. – forresthopkinsa Sep 25 '17 at 16:19
  • Thank you! This is the simplest way to known versionCode of APK. It is what I searched. – antaki93 Apr 06 '22 at 13:16
24
aapt dump badging test.apk | grep "versionName" | sed -e "s/.*versionName='//" -e "s/' .*//"

This answers the question by returning only the version number as a result. However......

The goal as previously stated should be to find out if the apk on the server is newer than the one installed BEFORE attempting to download or install it. The easiest way to do this is include the version number in the filename of the apk hosted on the server eg myapp_1.01.apk

You will need to establish the name and version number of the apps already installed (if it is installed) in order to make the comparison. You will need a rooted device or a means of installing the aapt binary and busybox if they are not already included in the rom.

This script will get the list of apps from your server and compare with any installed apps. The result is a list flagged for upgrade/installation.

#/system/bin/sh
SERVER_LIST=$(wget -qO- "http://demo.server.com/apk/" | grep 'href' | grep '\.apk' | sed 's/.*href="//' | \
              sed 's/".*//' | grep -v '\/' | sed -E "s/%/\\\\x/g" | sed -e "s/x20/ /g" -e "s/\\\\//g")
LOCAL_LIST=$(for APP in $(pm list packages -f | sed -e 's/package://' -e 's/=.*//' | sort -u); do \
              INFO=$(echo -n $(aapt dump badging $APP | grep -e 'package: name=' -e 'application: label=')) 2>/dev/null; \
              PACKAGE=$(echo $INFO | sed "s/.*package: name='//" | sed "s/'.*$//"); \
              LABEL=$(echo $INFO | sed "s/.*application: label='//" | sed "s/'.*$//"); if [ -z "$LABEL" ]; then LABEL="$PACKAGE"; fi; \
              VERSION=$(echo $INFO | sed -e "s/.*versionName='//" -e "s/' .*//"); \
              NAME=$LABEL"_"$VERSION".apk"; echo "$NAME"; \
              done;)
OFS=$IFS; IFS=$'\t\n'
for REMOTE in $SERVER_LIST; do
    INSTALLED=0
    REMOTE_NAME=$(echo $REMOTE | sed 's/_.*//'); REMOTE_VER=$(echo $REMOTE | sed 's/^[^_]*_//g' | sed 's/[^0-9]*//g')
    for LOCAL in $LOCAL_LIST; do
        LOCAL_NAME=$(echo $LOCAL | sed 's/_.*//'); LOCAL_VER=$(echo $LOCAL | sed 's/^[^_]*_//g' | sed 's/[^0-9]*//g')
        if [ "$REMOTE_NAME" == "$LOCAL_NAME" ]; then INSTALLED=1; fi
        if [ "$REMOTE_NAME" == "$LOCAL_NAME" ] && [ ! "$REMOTE_VER" == "$LOCAL_VER" ]; then echo remote=$REMOTE ver=$REMOTE_VER local=$LOCAL ver=$LOCAL_VER; fi
    done
    if [ "$INSTALLED" == "0" ]; then echo "$REMOTE"; fi
done
IFS=$OFS

As somebody asked how to do it without using aapt. It is also possible to extract apk info with apktool and a bit of scripting. This way is slower and not simple in android but will work on windows/mac or linux as long as you have working apktool setup.

#!/bin/sh
APK=/path/to/your.apk
TMPDIR=/tmp/apktool
rm -f -R $TMPDIR
apktool d -q -f -s --force-manifest -o $TMPDIR $APK
APK=$(basename $APK)
VERSION=$(cat $TMPDIR/apktool.yml | grep "versionName" | sed -e "s/versionName: //")
LABEL=$(cat $TMPDIR/res/values/strings.xml | grep 'string name="title"' | sed -e 's/.*">//' -e 's/<.*//')
rm -f -R $TMPDIR
echo ${LABEL}_$(echo $V).apk

Also consider a drop folder on your server. Upload apks to it and a cron task renames and moves them to your update folder.

#!/bin/sh
# Drop Folder script for renaming APKs
# Read apk file from SRC folder and move it to TGT folder while changing filename to APKLABEL_APKVERSION.apk
# If an existing version of the APK exists in the target folder then script will remove it
# Define METHOD as "aapt" or "apktool" depending upon what is available on server 

# Variables
METHOD="aapt"
SRC="/home/user/public_html/dropfolders/apk"
TGT="/home/user/public_html/apk"
if [ -d "$SRC" ];then mkdir -p $SRC
if [ -d "$TGT" ]then mkdir -p $TGT

# Functions
get_apk_filename () {
    if [ "$1" = "" ]; then return 1; fi
    local A="$1"
    case $METHOD in
        "apktool")
            local D=/tmp/apktool
            rm -f -R $D
            apktool d -q -f -s --force-manifest -o $D $A
            local A=$(basename $A)
            local V=$(cat $D/apktool.yml | grep "versionName" | sed -e "s/versionName: //")
            local T=$(cat $D/res/values/strings.xml | grep 'string name="title"' | sed -e 's/.*">//' -e 's/<.*//')
            rm -f -R $D<commands>
            ;;
        "aapt")
            local A=$(aapt dump badging $A | grep -e "application-label:" -e "VersionName")
            local V=$(echo $A | sed -e "s/.*versionName='//" -e "s/' .*//")
            local T=$(echo $A | sed -e "s/.*application-label:'//" -e "s/'.*//")
            ;;
        esac
    echo ${T}_$(echo $V).apk
}

# Begin script
for APK in $(ls "$SRC"/*.apk); do
    APKNAME=$(get_apk_filename "$APK")
    rm -f $TGT/$(echo APKNAME | sed "s/_.*//")_*.apk
    mv "$APK" "$TGT"/$APKNAME
done
Benjamin Loison
  • 3,782
  • 4
  • 16
  • 33
5p0ng3b0b
  • 511
  • 4
  • 10
10

At the moment, this can be done as follows

$ANDROID_HOME/build-tools/28.0.3/aapt dump badging /<path to>/<app name>.apk

In General, it will be:

$ANDROID_HOME/build-tools/<version_of_build_tools>/aapt dump badging /<path to>/<app name>.apk
SergeyYu
  • 354
  • 1
  • 5
  • 20
6

I can now successfully retrieve the version of an APK file from its binary XML data.

This topic is where I got the key to my answer (I also added my version of Ribo's code): How to parse the AndroidManifest.xml file inside an .apk package

Additionally, here's the XML parsing code I wrote, specifically to fetch the version:

XML Parsing

/**
 * Verifies at Conductor APK path if package version if newer 
 * 
 * @return True if package found is newer, false otherwise
 */
public static boolean checkIsNewVersion(String conductorApkPath) {

    boolean newVersionExists = false;

    // Decompress found APK's Manifest XML
    // Source: https://stackoverflow.com/questions/2097813/how-to-parse-the-androidmanifest-xml-file-inside-an-apk-package/4761689#4761689
    try {

        if ((new File(conductorApkPath).exists())) {

            JarFile jf = new JarFile(conductorApkPath);
            InputStream is = jf.getInputStream(jf.getEntry("AndroidManifest.xml"));
            byte[] xml = new byte[is.available()];
            int br = is.read(xml);

            //Tree tr = TrunkFactory.newTree();
            String xmlResult = SystemPackageTools.decompressXML(xml);
            //prt("XML\n"+tr.list());

            if (!xmlResult.isEmpty()) {

                InputStream in = new ByteArrayInputStream(xmlResult.getBytes());

                // Source: http://developer.android.com/training/basics/network-ops/xml.html
                XmlPullParser parser = Xml.newPullParser();
                parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);

                parser.setInput(in, null);
                parser.nextTag();

                String name = parser.getName();
                if (name.equalsIgnoreCase("Manifest")) {

                    String pakVersion = parser.getAttributeValue(null, "versionName");
                            //NOTE: This is specific to my project. Replace with whatever is relevant on your side to fetch your project's version
                    String curVersion = SharedData.getPlayerVersion();

                    int isNewer = SystemPackageTools.compareVersions(pakVersion, curVersion); 

                    newVersionExists = (isNewer == 1); 
                }

            }
        }

    } catch (Exception ex) {
        android.util.Log.e(TAG, "getIntents, ex: "+ex);
        ex.printStackTrace();
    }

    return newVersionExists;
}

Version Comparison (seen as SystemPackageTools.compareVersions in previous snippet) NOTE: This code is inspired from the following topic: Efficient way to compare version strings in Java

/**
 * Compare 2 version strings and tell if the first is higher, equal or lower
 * Source: https://stackoverflow.com/questions/6701948/efficient-way-to-compare-version-strings-in-java
 * 
 * @param ver1 Reference version
 * @param ver2 Comparison version
 * 
 * @return 1 if ver1 is higher, 0 if equal, -1 if ver1 is lower
 */
public static final int compareVersions(String ver1, String ver2) {

    String[] vals1 = ver1.split("\\.");
    String[] vals2 = ver2.split("\\.");
    int i=0;
    while(i<vals1.length&&i<vals2.length&&vals1[i].equals(vals2[i])) {
      i++;
    }

    if (i<vals1.length&&i<vals2.length) {
        int diff = Integer.valueOf(vals1[i]).compareTo(Integer.valueOf(vals2[i]));
        return diff<0?-1:diff==0?0:1;
    }

    return vals1.length<vals2.length?-1:vals1.length==vals2.length?0:1;
}

I hope this helps.

Community
  • 1
  • 1
Mathieu
  • 303
  • 2
  • 10
  • Have you upgraded to the new Gradle or Android Studio? Does this still work? The Robio code does not work to decompress the XML for me anymore. – Brian S Nov 30 '17 at 16:39
6
  1. Drag the .apk file into Android Studio

  2. Then double click on AndroidManifest.xml file.

  3. See the Version code and Version Name

enter image description here

Akshay Chopra
  • 1,035
  • 14
  • 10
4

For the upgrade scenario specifically an alternative approach might be to have a web service that delivers the current version number and check that instead of downloading the entire apk just to check its version. It would save some bandwidth, be a little more performant (much faster to download than an apk if the whole apk isn't needed most of the time) and much simpler to implement.

In the simplest form you could have a simple text file on your server... http://some-place.com/current-app-version.txt

Inside of that text file have something like

3.1.4

and then download that file and check against the currently installed version.

Building a more advanced solution to that would be to implement a proper web service and have an api call at launch which could return some json, i.e. http://api.some-place.com/versionCheck:

{
    "current_version": "3.1.4"
}
grego
  • 480
  • 6
  • 8
  • You are on the right track - should not download file, just to get its version. I just want to mention that I wouldn't recommend creating the text file MANUALLY - too easy to have it not match the actual apk file. Instead, write a script (that uses one of the other answers) to look inside the apk to see what its version is, storing that in file or database, to be retrieved by the means you discuss. – ToolmakerSteve Feb 07 '17 at 17:56
  • Yeah in this scenario the case is not that it needs to match the APK at all, just that the latest version number is somewhere publicly accessible. – grego Feb 08 '17 at 20:53
3

Using apkanalyzer that is now part of cmdline-tools:

$ apkanalyzer manifest version-code my_app.apk
1
$ apkanalyzer manifest version-name my_app.apk
1.2.3.4
Yang Zhao
  • 927
  • 6
  • 5
  • 1
    Maybe off-topic, but I'm getting `? ` as output. not sure why, but with the old `appt` it gets me the good value – crgarridos Jun 23 '21 at 09:32
0
    EditText ET1 = (EditText) findViewById(R.id.editText1);

    PackageInfo pinfo;
    try {
        pinfo = getPackageManager().getPackageInfo(getPackageName(), 0);
        String versionName = pinfo.versionName;
        ET1.setText(versionName);
        //ET2.setText(versionNumber);
    } catch (NameNotFoundException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
Wayne Len
  • 56
  • 3
  • 11
    This is not an answer to the question. This is code that, inside a running app, gets some version info, and displays it. The question is how to get this information from an APK file, *without installing the app*. – ToolmakerSteve Feb 07 '17 at 17:37
  • This is only for already installed application not for an .apk file. – Rhusfer May 06 '19 at 05:02
0

There's more simpler way to identify version code and version name. Please drag and drop the latest apk to Android studio for apk analyzer, here you can cross verify the version code and version name as shown below: enter image description here

Kirtikumar A.
  • 4,140
  • 43
  • 43
-1

Kotlin:

var ver: String = packageManager.getPackageInfo(packageName, 0).versionName
drunia
  • 1