Как проверить, подписан ли APK или «отладочная сборка»?

121

Насколько я знаю, в android "release build" подписан APK. Как это проверить по коду или у Eclipse есть какие-то секретные определения?

Мне это нужно для отладки заполнения элементов ListView из данных веб-службы (нет, logcat не вариант).

Мои мысли:

  • Приложения android:debuggable, но по какой-то причине это не выглядит надежным.
  • Идентификатор устройства с жестким кодированием - не лучшая идея, потому что я использую то же устройство для тестирования подписанных APK.
  • Используете ручной флаг где-нибудь в коде? Правдоподобно, но когда-нибудь обязательно забудут что-то изменить, плюс все программисты ленивы.
Im0rtality
источник
Откатил правку Фила. Вопрос не в том, будет ли программа законно распространяться на рынке или нет. Речь идет о том, что программа все еще находится в "режиме отладки".
Im0rtality
Это самый простой способ сделать это: stackoverflow.com/a/23844716/2296787
MBH

Ответы:

80

Есть другой способ проверить, создано ли приложение с помощью сертификата отладки или выпуска, но мне больше всего кажется, что это следующий способ.

Согласно информации в документации Android « Подписание вашего приложения» , ключ отладки содержит следующее отличительное имя субъекта: « CN = Android Debug, O = Android, C = US ». Мы можем использовать эту информацию, чтобы проверить, подписан ли пакет с помощью отладочного ключа без жесткого кодирования подписи отладочного ключа в нашем коде.

Дано:

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

Вы можете реализовать метод isDebuggable следующим образом:

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
Для того, чтобы помочь в решении нескольких паросочетаний импорта, классы , используемые здесь java.security.cert.X509Certificate, java.security.cert.CertificateExceptionи android.content.pm.Signature. Все остальные классы не представляют для меня несколько матчей,
Кристиан Гарсия,
1
Отредактированный ответ с этим импортом. Спасибо!
Кори Петоски
достаточно ли он эффективен для запуска в методе onCreate класса приложения?
разработчик Android
Я не заметил время выполнения, но использую его в своем приложении и не имею проблем с эффективностью.
Омар Рехман
Результат можно кэшировать для повышения эффективности.
ftvs 07
138

Чтобы проверить флаг отлаживаемости, вы можете использовать этот код:

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

Котлин:

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

Дополнительную информацию см. В разделе « Защита приложений Android LVL» .

В качестве альтернативы, если вы правильно используете Gradle, вы можете проверить, BuildConfig.DEBUGистинно оно или ложно.

Бландел
источник
похоже, что это все еще проверяет манифест android:
debuggable
2
Первый тестирует возможность отладки манифеста, он устарел. Второй вариант невозможен для библиотек, у библиотеки будет собственный BuildConfig - не удастся импортировать BuildConfig приложения, которое использует Lib. Поэтому отмеченный ответ - «хорошо»
Кристоф
Этот ответ будет работать во всех случаях, независимо от проекта библиотеки или проекта приложения.
Лавекуш Агравал,
131

Отвечает Марк Мерфи

Самым простым и лучшим долгосрочным решением является использование BuildConfig.DEBUG. Это booleanзначение будет trueдля отладочной сборки, в falseпротивном случае:

if (BuildConfig.DEBUG) {
  // do something for a debug build
}
Зар Э Ахмер
источник
8
Единственный недостаток этого подхода - то, что он не будет работать в библиотечных проектах (aar's). Когда библиотеки построены, это приведет к ложному результату, поэтому даже если приложение, которое использует библиотеку, находится в режиме отладки, эта проверка приведет к ложному результату в коде библиотеки.
Вито Андолини,
24

Если вы хотите проверить APKстатически, вы можете использовать

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

Это выводит, 0если APKне удается отладить, и 1если это так.

Heath Borders
источник
3
это единственное решение, чтобы проверить последний apk. В других ответах предполагается, что у вас есть источник.
Гильермо Тобар,
1
aaptживет здесь/Users/USER_NAME/library/Android/sdk/build-tools/28.0.3/aapt
Кейси
21

Может поздно, но iosched использует BuildConfig.DEBUG

Ursus
источник
теперь безопасно использовать? есть статья о некоторых проблемах: digipom.com/be-careful-with-buildconfig-debug
разработчик Android
Это лучший ответ!
Питер Фортуин
нет, если вы пишете стороннюю библиотеку и не знаете пакет BuildConfig во время компиляции.
Sam Dozor
Сэм, ты можешь подробнее рассказать об этом?
Агамемнус
10

Сначала добавьте это в свой файл build.gradle, это также позволит параллельно запускать сборки отладки и выпуска:

buildTypes {
    debug {
        applicationIdSuffix ".debug"
    }
}

Добавьте этот метод:

public static boolean isDebug(Context context) {
    String pName = context.getPackageName();
    if (pName != null && pName.endsWith(".debug")) {
        return true;
    } else {
        return false;
    }
}
Meanman
источник
1
Я предпочитаю этот ответ, потому что он надежный. Мне действительно нужно было добавить новую запись «Разрешенное приложение для Android» в мой ключ API Карт Google (поскольку идентификатор приложения отличается).
Baz
5

Отладочная сборка также подписывается, только с другим ключом. Он автоматически генерируется Eclipse, и его сертификат действителен только в течение одного года. В чем проблема android:debuggable? Вы можете получить это значение из кода, используя PackageManager.

Николай Еленков
источник
3

Еще один вариант, о котором стоит упомянуть. Если вам нужно выполнить какой-то код только при подключенном отладчике, используйте этот код:

if (Debug.isDebuggerConnected() || Debug.waitingForDebugger()) { 
    //code to be executed 
}
Игорь Горянц
источник
0

Решено с помощью android:debuggable. Это была ошибка при чтении элемента, когда в некоторых случаях флаг отладки для элемента не сохранялся в записи, которая if (m.debug && !App.isDebuggable(getContext()))всегда оценивалась false. Виноват.

Im0rtality
источник
13
Я понимаю, что этому больше года, однако вы должны были принять ответ @Omar Rehman, а не этот. Хотя то, что вы опубликовали, - это то, что вы в конечном итоге сделали, на самом деле оно не отвечает на заданный вами вопрос, в то время как решение Омара, похоже, делает это, что означает, что он заслуживает признания.
mah
7
@Mah - совершенно неуместно запугивать кого-то за то, что он не принял ответ, который был опубликован почти через год после того, как они сами решили свою проблему! И это даже без учета того, насколько сложнее предложенный вами ответ, чем тот, с которым они пошли - который, по сути, отвечает на заданный вопрос, поскольку вопрос был вызван ошибкой, приводящей к ошибочному впечатлению, что флаг ненадежен. ,
Крис Стрэттон,
4
@ChrisStratton, если вы думаете, что мой ответ был издевательством, я думаю, вы не очень много читаете в Интернете. Я поддерживаю ваше право опубликовать вашу противоположную точку зрения в комментарии, как и вы, и в результате я просмотрел свой комментарий и другие сообщения в этом вопросе, и я поддерживаю свой первоначальный комментарий: автор задал вопрос, который был законным и правильно кто-то ответил. Основываясь на его собственном «ответе», его первоначальный вопрос - это не то, что он хотел задать в первую очередь ... подпись APK (выпуск или отладка) не имеет абсолютно никакого отношения к манифесту.
mah
3
@mah - это зависит от того, кто задает вопрос, а не от вас, чтобы решить, что удовлетворяет их фактическую потребность в приложении, в том числе, является ли различие, которое вы поднимаете, одним из важных - или, как очевидно в этом случае, нет. Что еще более важно, вы полностью забываете о том, что предложенный вами ответ был опубликован почти через год после того, как их реальная потребность была удовлетворена . Это наказание за то, что он не вернулся, чтобы изменить свое согласие большую часть года спустя, что является издевательством.
Крис Стрэттон,
3
@ChrisStratton также должен задать вопрос, на который он хочет получить ответ, - то, что не было сделано в данном случае. Вы правы, я упустил из виду, когда был дан ответ, который фактически отвечает на то, что было опубликовано, однако никакого наказания нет, есть только разумное выражение моего мнения. Если вы считаете, что я здесь хулиган, настоятельно прошу вас пометить мой комментарий как оскорбительный. Однако перед этим вы можете проверить свои собственные сообщения здесь.
mah
0

Решение на Kotlin, которое я использую сейчас:

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

таким образом я все еще могу ПОДПИСАТЬСЯ при отладке, и о них будет сообщено в Crashlytics (например, для процесса QA)

Рафаэль Руис Муньос
источник