Как да заобиколите скрития черен списък на API на Android 9+

Google въведе ограничения в Android 9 за това кои API могат да бъдат достъпни от разработчиците. Ето как да заобиколите тези скрити ограничения на API.

Още през 2018 г. Google пусна Android Pie. Сред промените в потребителския интерфейс и новите функции имаше и някои промени от страна на разработчиците. Тези промени включват нови API, корекции на грешки за съществуващи API, както и ограничения за достъп до скрити API.

За щастие обаче има начини да заобиколите тези ограничения. Преди да навляза в това как да заобиколя ограниченията, трябва да обясня малко за това какво представляват скритите API, защо са били ограничени на първо място и защо може да искате да получите достъп до тях.

Скритите API са API в Android, които разработчиците на приложения обикновено не могат да видят. Ако погледнете кода на AOSP, ще видите цял куп класове, променливи и методи, които имат @hide анотация в блок за коментари над тях.

Тази анотация инструктира какъв инструмент Google използва при компилирането на SDK, за да изключи елемента под него. След това този SDK се разпространява на разработчиците в SDK, изтеглени чрез Android Studio. Освен ако не използвате модифициран SDK, Android Studio ще смята, че някой от тези скрити елементи просто не съществува. Ако се опитате да използвате такъв директно, той ще го покаже в червено и ще откаже да се компилира.

Има много причини, поради които API може да бъде скрит. Някои неща са предназначени да се използват само от вътрешни или системни приложения и няма да работят, ако се използват от приложение на трета страна. Други са експериментални или нестабилни и може да бъдат премахнати или променени в бъдеще. Някои дори са само приложни програмни интерфейси (API). Google просто не иска да прилага нормалния цикъл на отмяна, ако някога бъдат премахнати.

Докато стандартният Android SDK има a много в него понякога не е достатъчно. Понякога има нещо, което искате да направите, което вече съществува в Android, но просто не е изложено публично.

Например, много от приложенията, които правя, включително SystemUI тунер и Уиджети за заключен екран, използвайте куп различни скрити API. SystemUI Tuner има нужда от достъп до някои, за да проследява правилно, променя и нулира опциите. Lockscreen Widgets използва някои, за да покаже тапета под него, наред с други неща.

Повечето разработчици не се нуждаят от достъп до скрити API, но понякога те могат да бъдат доста полезни.

С пускането на Android 9 (Pie) Google въведе скрития черен списък на API. Не всеки скрит API беше включен и имаше различни нива на списъци. Скритите API в белия списък могат да бъдат достъпни от всеки. Скритите API в светлосивия списък могат да бъдат достъпни от всяко приложение, но може да са недостъпни в бъдещите версии на Android. Всичко в тъмносивия списък може да бъде достъпно само от приложения, насочени към API нива преди Pie (т.е. преди API ниво 28). На приложения, насочени към Pie и по-нови, ще бъде отказан достъп. И накрая, скритите API в черния списък не могат да бъдат достъпни от никое приложение, което не е системно (или не е в белия списък), независимо от целевия API.

Android 10 промени начина на организиране на списъците и ги опрости леко, но идеята остана същата. Някои скрити API можеха да бъдат достъпни от приложения, докато други бяха блокирани. Android 11 засили откриването на достъп да се блокирайте байпас използван за пай и 10.

Във всички версии на Android всеки път, когато приложение на трета страна се опита да получи достъп до скрит API в черния списък, Android ще изведе съответната грешка „не е намерено“.

Всъщност има доста начини да преминете през скрития черен списък на API. В зависимост от нуждите ви можете да изберете такива, които работят за всички версии на Android, такива, които работят само за Android 9 и 10, такива, които използват естествен C++ код, и такива, които са изцяло базирани на Java. Има дори заобиколно решение само за разработка с помощта на ADB.

Заобиколно решение на ADB

Ако вашето устройство работи с Android Pie, изпълнете следните две ADB команди, за да активирате скрит API достъп.

adb shell settings put global hidden_api_policy_pre_p_apps 1
adb shell settings put global hidden_api_policy_p_apps 1

Ако вашето устройство работи с Android 10 или по-нова версия, изпълнете следната ADB команда, за да активирате скрит API достъп.

adb shell settings put global hidden_api_policy 1

За да се върнете към поведението по подразбиране, просто заменете put с delete и премахнете 1.

Очевидно тези команди не са точно полезни за производствено приложение. Мога да ви кажа от първа ръка, че правилното инструктиране на потребителите как да използват ADB е невероятно трудно. Но те могат да бъдат полезни, ако трябва да актуализирате старо приложение, за да отговаря на новите ограничения.

Заобиколно решение на Native/JNI

Има два начина, по които можете да заобиколите скрития черен списък на API с помощта на JNI във вашето приложение за Android. Единият работи за Android 9 и 10, а другият работи за Android 9 и по-нови версии.

Android 9 и 10

Ако вече имате собствена част от приложението си, това ще бъде лесно за изпълнение. Просто използвайте JNI_OnLoad() функция.

статично изкуство:: Runtime* runtime = nullptr;

extern "C"jint JNI_OnLoad(JavaVM *vm, void *reserved){
...
runtime = reinterpret_cast<: javavmext>(vm)->GetRuntime();
runtime->SetHiddenApiEnforcementPolicy(art:: hiddenapi:: EnforcementPolicy:: kNoChecks);
...
}

Имайте предвид, че този метод работи само на Android 9 и 10.

Android 9 и по-нова версия

За всяка версия на Android имате избор от две библиотеки за заобикаляне на скритото API ограничение: FreeReflection и RestrictionBypass.

И двете са лесни за изпълнение и използване.

За внедряване на FreeReflection, добавете зависимостта към вашия build.gradle на ниво модул.

implementation 'me.weishu: free_reflection: 3.0.1'

След това отменете attachBaseContext() във вашия клас Application.

@Override
protectedvoidattachBaseContext(Context base){
super.attachBaseContext(base);
Reflection.unseal(base);
}

Ако нямате клас Application, можете да го добавите доста лесно. Създайте нов клас, който се разширява Application и след това го посочете във вашия AndroidManifest.xml.

Пример:

publicclassAppextendsApplication{
...
@Override
protectedvoidattachBaseContext(Context base){
super.attachBaseContext(base);

Reflection.unseal(base);
}
}

<manifest>
...
...
name=".App">
...
application>
manifest>

За прилагане на RestrictionBypass, добавете хранилището на JitPack към вашия build.gradle на ниво проект.

allprojects {
repositories {
...
maven { url "https://jitpack.io" }
}
}

След това добавете зависимостта към вашия build.gradle на ниво модул.

implementation 'com.github.ChickenHook: RestrictionBypass: 2.2'

И това е. Тази библиотека автоматично премахва ограниченията на черния списък.

Заобиколно решение на Java

Въпреки че JNI решенията са ефективни, има моменти, в които може да не искате да използвате естествен код. Ако все още не правите неща в C++, това може да добави ненужен размер, заедно с ограничения на платформата, към вашето приложение. За щастие има начини да заобиколите скрития черен списък на API, като използвате само Java.

Android 9 и 10

В Android 9 и 10 можете да използвате това, което може да се нарече двойно отражение или мета-отражение, за да заобиколите скрития черен списък на API. Тъй като системата проверява само какво извикват приложенията на трети страни, двойното отражение я подвежда да мисли, че системата прави скритите API извиквания.

Този трик може да се използва за извикване на метод, който да даде на приложението ви скрити API изключения, подходящо наименувани setHiddenApiExemptions(). Просто добавете следния код някъде в началото на жизнения цикъл на вашето приложение (като Application's onCreate() метод) и ще се справи със заобикалянето на черния списък.

Method forName = Class.class.getDeclaredMethod("forName", String.class);
Method getDeclaredMethod = Class.class.getDeclaredMethod("getDeclaredMethod", String.class, Class[].class);

Class vmRuntimeClass = (Class) forName.invoke(null, "dalvik.system.VMRuntime");
Method getRuntime = (Method) getDeclaredMethod.invoke(vmRuntimeClass, "getRuntime", null);
Method setHiddenApiExemptions = (Method) getDeclaredMethod.invoke(vmRuntimeClass, "setHiddenApiExemptions", newClass[] { String[].class} );

Object vmRuntime = getRuntime.invoke(null);
setHiddenApiExemptions.invoke(vmRuntime, newString[][] { newString[] { "L" } });

Ако приложението ви е съвместимо с версии на Android по-ниски от 9, не забравяйте да включите това в проверка на версията.

Android 9 и по-нова версия

За да заобиколите скрития черен списък на API на Android 9 и всяка следваща версия, можете да използвате библиотеката на LSPosed. Тази библиотека използва Unsafe API на Java, така че е малко вероятно някога да се повреди.

За да го приложите, просто добавете зависимостта към вашия build.gradle на ниво модул.

implementation 'org.lsposed.hiddenapibypass: hiddenapibypass: 2.0'

След това го използвайте, за да заобиколите черния списък.

HiddenApiBypass.addHiddenApiExemptions("L");

Ако приложението ви е съвместимо с версии на Android по-ниски от 9, не забравяйте да включите това в проверка на версията.

Заключение и повече информация

Има много опции за заобикаляне на скрития черен списък на API на Android, без значение към коя версия на платформата се насочвате или използвате. Ако сте любопитни да научите повече за това как работят тези методи и библиотеки, не забравяйте да разгледате следните връзки.

  • Моите въпроси и отговори за препълване на стека.
  • Библиотеката Hidden API Bypass на LSposed в GitHub.
  • Библиотеката RestrictionBypass на ChickenHook в GitHub.
  • библиотеката FreeReflection на tiann в GitHub.
  • Документация на Google за скрития черен списък на API.