131

As far as I know, in android "release build" is signed APK. How to check it from code or does Eclipse has some kinda of secret defines?

I need this to debug populating ListView items from web service data (no, logcat not an option).

My thoughts:

  • Application's android:debuggable, but for some reason that doesn't look reliable.
  • Hard-coding device ID isn't good idea, because I am using same device for testing signed APKs.
  • Using manual flag somewhere in code? Plausible, but gonna definitely forget to change at some time, plus all programmers are lazy.
Cristian
  • 198,401
  • 62
  • 356
  • 264
Im0rtality
  • 3,463
  • 3
  • 31
  • 41
  • Roll-backed Phil's edit. This is not question about program being legally distributed over market or not. It's question about is program in still "debug mode". – Im0rtality Aug 17 '11 at 07:14
  • This way is easiest way for doing so: http://stackoverflow.com/a/23844716/2296787 – MBH Nov 30 '15 at 09:06

10 Answers10

146

To check the debuggable flag, you can use this code:

boolean isDebuggable =  ( 0 != ( getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE ) );

Kotlin:

val isDebuggable = 0 != applicationInfo.flags and ApplicationInfo.FLAG_DEBUGGABLE

For more information, please see Securing Android LVL Applications.

Alternatively, if you're using Gradle correctly, you can check if BuildConfig.DEBUG is true or false.

amorenew
  • 10,760
  • 10
  • 47
  • 69
Blundell
  • 75,855
  • 30
  • 208
  • 233
  • it seems like this does still check manifest's android:debuggable – xster Apr 17 '13 at 17:23
  • 2
    The first one tests for Manifest debuggable, this is deprecated. The second one is not possible for libraries, the lib will have it's own BuildConfig - not able to import the BuildConfig of the App, which is using the Lib. Therefor the marked answer is "ok" – Christoph Aug 13 '14 at 09:52
  • This answer will work in all cases regardless of library project or application project. – Lavekush Agrawal Oct 26 '17 at 09:46
134

Answered by Mark Murphy

The simplest, and best long-term solution, is to use BuildConfig.DEBUG. This is a boolean value that will be true for a debug build, false otherwise:

if (BuildConfig.DEBUG) {
  // do something for a debug build
}
Zar E Ahmer
  • 33,936
  • 20
  • 234
  • 300
  • 14
    The only drawback to this approach is that it will not work in library projects (aar's). When libraries are built, this will result to false, so even if an application that uses the library is in debug mode, this check will result to false within the library code. – Vito Andolini Sep 30 '15 at 14:47
81

There are different way to check if the application is build using debug or release certificate, but the following way seems best to me.

According to the info in Android documentation Signing Your Application, debug key contain following subject distinguished name: "CN=Android Debug,O=Android,C=US". We can use this information to test if package is signed with debug key without hardcoding debug key signature into our code.

Given:

import android.content.pm.Signature;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

You can implement an isDebuggable method this way:

private static final X500Principal DEBUG_DN = new X500Principal("CN=Android Debug,O=Android,C=US");
private boolean isDebuggable(Context ctx)
{
    boolean debuggable = false;

    try
    {
        PackageInfo pinfo = ctx.getPackageManager().getPackageInfo(ctx.getPackageName(),PackageManager.GET_SIGNATURES);
        Signature signatures[] = pinfo.signatures;

        CertificateFactory cf = CertificateFactory.getInstance("X.509");

        for ( int i = 0; i < signatures.length;i++)
        {   
            ByteArrayInputStream stream = new ByteArrayInputStream(signatures[i].toByteArray());
            X509Certificate cert = (X509Certificate) cf.generateCertificate(stream);       
            debuggable = cert.getSubjectX500Principal().equals(DEBUG_DN);
            if (debuggable)
                break;
        }
    }
    catch (NameNotFoundException e)
    {
        //debuggable variable will remain false
    }
    catch (CertificateException e)
    {
        //debuggable variable will remain false
    }
    return debuggable;
}
Omar Rehman
  • 2,045
  • 17
  • 17
  • is it efficient enough to be run on the onCreate method of the application class? – android developer Jul 25 '13 at 12:14
  • I havn't noted the execution time, but I have been using it in my app and don't have any issues with efficiency. – Omar Rehman Oct 01 '13 at 09:44
  • The result can be cached for efficiency. – ftvs Feb 07 '14 at 12:04
  • It works indeed, but i realised there are multiple "versions" of the debug certificate, therefor it's not 100% correct. Our Debug certificate looks like this "CN=Android Debug, OU=Android, O=Android, L=Unknown, ST=Unknown, C=US" – Christoph Aug 13 '14 at 10:00
  • The timing on this is about 2-3 milliseconds for me. – Agamemnus May 06 '15 at 18:56
  • Hmm. This doesn't work if the certificate is a release certificate and not generated with Android Studio or the like. – Agamemnus Jun 08 '15 at 02:36
  • Suggesting: debuggable = cert.getSubjectX500Principal().getName().contains("CN=Android Debug");. What do you say? – Eitan Schwartz Nov 26 '18 at 09:22
  • This answer will work most of the time, but it's not correct. The signature is not what determines if an app is debuggable. Our app is in the firmware. Android won't let you overlay a package with different signature, so we have to use the release key to debug. I believe getApplicationInfo() is the correct answer. – Dustin Jan 05 '21 at 18:33
34

If you want to check an APK statically, you could use

aapt dump badging /path/to/apk | grep -c application-debuggable

This outputs 0 if the APK isn't debuggable and 1 if it is.

Heath Borders
  • 30,998
  • 16
  • 147
  • 256
22

Maybe late, but iosched uses BuildConfig.DEBUG

urSus
  • 12,492
  • 12
  • 69
  • 89
10

First add this to your build.gradle file, this will also allow side by side running of debug and release builds:

buildTypes {
    debug {
        applicationIdSuffix ".debug"
    }
}

Add this method:

public static boolean isDebug(Context context) {
    String pName = context.getPackageName();
    if (pName != null && pName.endsWith(".debug")) {
        return true;
    } else {
        return false;
    }
}
Meanman
  • 1,474
  • 20
  • 17
  • 1
    I prefer this answer, because it's reliable. I did need to add a new "allowed Android application" entry to my Google Maps API key though (as the application id is different). – Baz Mar 10 '15 at 12:44
  • I used this as well, but I would suggest removing the if and just return pName != null && pName.endsWith(".debug") – Manuel Rego Nov 25 '20 at 13:36
5

A debug build is signed as well, just with a different key. It's generated automatically by Eclipse, and its certificate is valid for one year only. What's the problem with android:debuggable? You can get this value from code using PackageManager.

Nikolay Elenkov
  • 52,576
  • 10
  • 84
  • 84
4

Another option, worth mentioning. If you need to execute some code only when debugger is attached, use this code:

if (Debug.isDebuggerConnected() || Debug.waitingForDebugger()) { 
    //code to be executed 
}
Milad Bahmanabadi
  • 946
  • 11
  • 27
Igor Gorjanc
  • 535
  • 4
  • 17
0

Solution in Kotlin that I'm using at the moment:

@SuppressLint("PackageManagerGetSignatures")
@Suppress("DEPRECATION")
fun isSigned(context: Context?): Boolean {
    return (context?.packageManager?.getPackageInfo(context.packageName, PackageManager.GET_SIGNATURES)?.signatures?.firstOrNull()?.toByteArray()
            ?.let {
                return@let CertificateFactory.getInstance("X.509").generateCertificate(ByteArrayInputStream(it))
            } as? X509Certificate)
            ?.issuerDN
            ?.name
            ?.contains("O=Android", ignoreCase = false) ?: true
}

that way I can still SIGN in debug and those will be reported to Crashlytics (example, for the QA process)

Rafael Ruiz Muñoz
  • 5,333
  • 6
  • 46
  • 92
0

Solved with android:debuggable. It was bug in reading item where in some cases debug flag on item was not being stored in record getting if (m.debug && !App.isDebuggable(getContext())) always evaluated to false. My bad.

Im0rtality
  • 3,463
  • 3
  • 31
  • 41
  • 13
    I realize this is over a year old, however you should have accepted @Omar Rehman's answer, not this one. While what you've posted is what you eventually did, it doesn't really answer the question you asked, while Omar's solution appears to do that, meaning he deserves the accepted credit. – mah Sep 20 '12 at 11:15
  • 4
    @ChrisStratton if you think my response was bullying, I think you don't read the internet much. I support your right to post your opposing viewpoint in a comment as you did, and as a result I've reviewed my comment and the other posts in this question, and I stand by my original comment: the poster asked a question which was legitimately and correctly answered by someone. Based on his own "answer", his original question is not what he wanted to ask in the first place... an APK's signature (release or debug) has exactly zero bearing on the manifest. – mah Apr 22 '13 at 14:26
  • 3
    @ChrisStratton it's also up to the asker to ask the actual question s/he wants an answer to -- something that was not done in this case. You are correct that I overlooked when the answer that is actually answering what was posted was made, however there is no punishment at all, there is only a reasonable expression of my opinion. If you think I am acting the bully here, I _strongly_ request that you flag my comment for abuse. Before doing that however, you may wish to examine your own postings here. – mah Apr 22 '13 at 15:20