Como verificar se o APK está assinado ou "compilação de depuração"?


121

Tanto quanto eu sei, no Android "release build" é assinado APK. Como verificá-lo a partir do código ou o Eclipse tem algum tipo de definição secreta?

Eu preciso disso para depurar o preenchimento de itens do ListView a partir de dados de serviço da web (não, logcat não é uma opção).

Meus pensamentos:

  • Aplicativo android:debuggable, mas por algum motivo que não parece confiável.
  • O código do dispositivo de codificação não é uma boa ideia, porque estou usando o mesmo dispositivo para testar APKs assinados.
  • Usando sinalizador manual em algum lugar no código? Plausível, mas com certeza vou esquecer de mudar em algum momento, além de todos os programadores serem preguiçosos.

Edição de Phil com suporte de rolo. Não se trata de um programa legalmente distribuído no mercado ou não. É questão sobre o programa ainda está no "modo de depuração".
precisa saber é o seguinte

Dessa maneira, é a maneira mais fácil de fazê-lo: stackoverflow.com/a/23844716/2296787
MBH

Respostas:


80

Há uma maneira diferente de verificar se o aplicativo é compilado usando o certificado de depuração ou liberação, mas a seguinte maneira me parece melhor.

De acordo com as informações na documentação do Android, Signing Your Application , a chave de depuração contém o seguinte nome distinto do assunto: " CN = Android Debug, O = Android, C = US ". Podemos usar essas informações para testar se o pacote está assinado com chave de depuração sem a assinatura da chave de depuração codificada em nosso código.

Dado:

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

Você pode implementar um método isDebuggable desta maneira:

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;
}

6
Para ajudar a resolver várias correspondências de importação, as classes usadas aqui são java.security.cert.X509Certificate, java.security.cert.CertificateExceptione android.content.pm.Signature. Todas as outras classes não apresentam várias correspondências para mim
Christian García

1
Resposta editada com essas importações. Obrigado!
Cory Petosky 18/04

é eficiente o suficiente para ser executado no método onCreate da classe de aplicativo?
desenvolvedor android

Não anotei o tempo de execução, mas o uso no meu aplicativo e não tenho problemas com eficiência.
Omar Rehman

O resultado pode ser armazenado em cache para obter eficiência.
ftvs

138

Para verificar o sinalizador depurável, você pode usar este código:

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

Kotlin:

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

Para mais informações, consulte Protegendo aplicativos Android LVL .

Como alternativa, se você estiver usando o Gradle corretamente, poderá verificar se BuildConfig.DEBUGé verdadeiro ou falso.


parece que isso ainda verificar de manifesto do Android: debuggable
xster

2
O primeiro teste para o Manifest debuggable, isso foi preterido. O segundo não é possível para bibliotecas, a lib terá seu próprio BuildConfig - não poderá importar o BuildConfig do aplicativo, que está usando a lib. Portanto, a resposta marcada é "ok"
Christoph

Esta resposta funcionará em todos os casos, independentemente do projeto da biblioteca ou do aplicativo.
Lavekush Agrawal

131

Respondida por Mark Murphy

A solução mais simples e melhor a longo prazo é usar BuildConfig.DEBUG. Este é um booleanvalor que será truepara uma compilação de depuração, falsecaso contrário:

if (BuildConfig.DEBUG) {
  // do something for a debug build
}

8
A única desvantagem dessa abordagem é que ela não funcionará em projetos de bibliotecas (aar's). Quando as bibliotecas são construídas, isso resulta em falso; portanto, mesmo que um aplicativo que use a biblioteca esteja no modo de depuração, essa verificação resultará em falso no código da biblioteca.
Vito Andolini

24

Se você deseja verificar APKestaticamente, pode usar

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

Isso gera 0se APKnão for depurável e 1se for.


3
esta é a única solução, para verificar sobre o apk final. As outras respostas assumem que você tem a fonte.
Guillermo Tobar

1
aaptvive aqui/Users/USER_NAME/library/Android/sdk/build-tools/28.0.3/aapt
Casey


10

Primeiro adicione isso ao seu arquivo build.gradle, isso também permitirá a execução lado a lado das versões de depuração e lançamento:

buildTypes {
    debug {
        applicationIdSuffix ".debug"
    }
}

Adicione este método:

public static boolean isDebug(Context context) {
    String pName = context.getPackageName();
    if (pName != null && pName.endsWith(".debug")) {
        return true;
    } else {
        return false;
    }
}

1
Eu prefiro esta resposta, porque é confiável. Porém, eu precisava adicionar uma nova entrada "aplicativo Android permitido" à minha chave da API do Google Maps (como o ID do aplicativo é diferente).
Baz

5

Uma compilação de depuração também é assinada, apenas com uma chave diferente. É gerado automaticamente pelo Eclipse e seu certificado é válido por apenas um ano. Qual é o problema android:debuggable? Você pode obter esse valor do código usando PackageManager.


3

Outra opção, vale a pena mencionar. Se você precisar executar algum código apenas quando o depurador estiver conectado, use este código:

if (Debug.isDebuggerConnected() || Debug.waitingForDebugger()) { 
    //code to be executed 
}

0

Resolvido com android:debuggable. Foi um erro na leitura do item, onde, em alguns casos, o sinalizador de depuração no item não estava sendo armazenado no registro, sendo if (m.debug && !App.isDebuggable(getContext()))sempre avaliado false. Foi mal.


13
Sei que isso tem mais de um ano, mas você deveria ter aceitado a resposta de @Omar Rehman, não esta. Embora o que você postou seja o que acabou fazendo, ele realmente não responde à pergunta que você fez, enquanto a solução de Omar parece fazer isso, o que significa que ele merece o crédito aceito.
mah

7
@ Mah - é totalmente inapropriado intimidar alguém por não aceitar uma resposta que foi postada quase um ano depois que eles mesmos resolveram o problema! E isso até ignora o quão mais complicada é a resposta que você indicaria do que a que foi - o que, de fato, responde à pergunta, já que a pergunta foi solicitada por um bug que levou a uma impressão equivocada de que a bandeira não é confiável .
Chris Stratton

4
@ ChrisStratton, se você acha que minha resposta foi bullying, acho que você não lê muito a internet. Apoio o seu direito de postar seu ponto de vista oposto em um comentário como você fez e, como resultado, revi meu comentário e os outros posts desta pergunta, e mantenho meu comentário original: o pôster fez uma pergunta que era legitimamente e respondido corretamente por alguém. Com base em sua própria "resposta", sua pergunta original não é o que ele queria perguntar ... a assinatura de um APK (liberação ou depuração) tem exatamente zero impacto no manifesto.
mah

3
@mah - depende do solicitante, não decidir o que satisfaz a necessidade real de sua aplicação, incluindo se a distinção que você faz é de importância - ou, como aparentemente neste caso, não. Mais importante, você ignora completamente que a resposta que você indicou foi publicada quase um ano após a satisfação real de suas necessidades . Está punindo alguém por não voltar a mudar sua aceitação mais de um ano depois, o que constitui bullying.
precisa

3
@ChrisStratton também cabe ao solicitante fazer a pergunta real para a qual ele quer uma resposta - algo que não foi feito nesse caso. Você está certo que eu ignorei quando a resposta que realmente está respondendo ao que foi postado foi feita; no entanto, não há punição alguma, existe apenas uma expressão razoável da minha opinião. Se você acha que estou agindo como um valentão aqui, solicito fortemente que você sinalize meu comentário por abuso. Antes de fazer isso, no entanto, convém examinar suas próprias postagens aqui.
mah

0

Solução em Kotlin que estou usando no momento:

@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
}

Dessa forma, ainda posso ASSINAR na depuração e eles serão relatados ao Crashlytics (exemplo, para o processo de controle de qualidade)

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.