Как проверить, подписан ли APK или "отладочная сборка"?
Насколько я знаю, в андроиде "релизная сборка" подписана APK. Как проверить это из кода или Eclipse имеет какие-то секретные определения?
Мне нужно это для отладки заполнения элементов ListView из данных веб-службы (нет, logcat не вариант).
Мои мысли:
- Приложения
android:debuggable
, но по какой-то причине это не выглядит надежным. - Жесткое кодирование идентификатора устройства не очень хорошая идея, потому что я использую то же устройство для тестирования подписанных APK.
- Используя ручной флаг где-нибудь в коде? Правдоподобно, но определенно забудет что-то изменить, плюс все программисты ленивы.
9 ответов
Есть другой способ проверить, собирается ли приложение с использованием сертификата отладки или выпуска, но следующий способ кажется мне лучшим.
Согласно информации в документации 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;
}
Чтобы проверить флаг отладки, вы можете использовать этот код:
boolean isDebuggable = ( 0 != ( getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE ) );
Для получения дополнительной информации см. Защита приложений Android LVL.
Кроме того, если вы используете Gradle правильно, вы можете проверить, если BuildConfig.DEBUG
это правда или ложь.
Ответ Марком Мерфи
Самое простое и лучшее долгосрочное решение- использовать BuildConfig.DEBUG
, Это boolean
значение, которое будет true
для отладочной сборки, false
иначе:
if (BuildConfig.DEBUG) {
// do something for a debug build
}
Если вы хотите проверить APK
статически, вы могли бы использовать
aapt dump badging /path/to/apk | grep -c application-debuggable
Это выводы 0
если APK
не отлаживается и 1
если это.
Сначала добавьте это в свой файл 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;
}
}
Отладочная сборка также подписана, просто с другим ключом. Он генерируется автоматически Eclipse, и его сертификат действителен только в течение одного года. В чем проблема android:debuggable
? Вы можете получить это значение из кода, используя PackageManager
,
Еще один вариант, заслуживающий упоминания. Если вам нужно выполнить какой-то код только при подключенном отладчике, используйте этот код: if (Debug.isDebuggerConnected() || Debug.waitingForDebugger()) {
//code to be executed
}
Решено с android:debuggable
, Это была ошибка чтения элемента, когда в некоторых случаях флаг отладки на элементе не сохранялся при получении записи if (m.debug && !App.isDebuggable(getContext()))
всегда оценивается в false
, Виноват.
Решение на 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)