Оновлення Android 8.0 Oreo
Незважаючи на те, що спочатку питання було задано для підтримки Android L, люди все ще, здається, натискають на це питання та відповідь, тому варто описати вдосконалення, введені в Android 8.0 Oreo. Методи зворотної сумісності все ще описані нижче.
Що змінилося?
Починаючи з Android 8.0 Oreo , група дозволів PHONE також містить дозвіл ANSWER_PHONE_CALLS . Як випливає з назви дозволу, його утримання дозволяє програмі програмно приймати вхідні дзвінки через відповідний виклик API без будь-якого злому системи за допомогою відображення або імітації користувача.
Як ми можемо використати цю зміну?
Вам слід перевірити версію системи під час виконання, якщо ви підтримуєте старіші версії Android, щоб ви могли інкапсулювати цей новий виклик API, зберігаючи підтримку цих старих версій Android. Вам слід дотримуватися запиту дозволів під час виконання, щоб отримати цей новий дозвіл під час роботи, як це стандартно для нових версій Android.
Отримавши дозвіл, вашій програмі потрібно просто викликати метод acceptRingingCall від TelecomManager . Тоді базовий виклик виглядає наступним чином:
TelecomManager tm = (TelecomManager) mContext
.getSystemService(Context.TELECOM_SERVICE);
if (tm == null) {
throw new NullPointerException("tm == null");
}
tm.acceptRingingCall();
Спосіб 1: TelephonyManager.answerRingingCall ()
Коли ви маєте необмежений контроль над пристроєм.
Що це?
Існує TelephonyManager.answerRingingCall (), який є прихованим внутрішнім методом. Він працює як міст для ITelephony.answerRingingCall (), який обговорювався на веб-сайтах і здається перспективним на початку. Це НЕ є на 4.4.2_r1 , як вона була введена тільки в фіксації 83da75d для Android 4.4 KitKat ( рядок 1537 на 4.4.3_r1 ) , а потім «повторно» в фіксації f1e1e77 для Lollipop ( лінії 3138 на 5.0.0_r1 ) з - за того , як Дерево Git було структуровано. Це означає, що якщо ви не підтримуєте лише пристрої з льодяником, що, мабуть, є поганим рішенням, виходячи з крихітної частки його ринку на даний момент, вам все одно потрібно надати резервні методи, якщо йти цим шляхом.
Як би ми це використали?
Оскільки розглянутий метод прихований від використання програм SDK, вам потрібно використовувати відображення для динамічного вивчення та використання методу під час виконання. Якщо ви не знайомі з рефлексією, ви можете швидко прочитати, що таке рефлексія, і чому вона корисна? . Ви також можете глибше заглибитися в особливості Trail: The Reflection API, якщо вам це цікаво.
І як це виглядає в коді?
final String LOG_TAG = "TelephonyAnswer";
TelephonyManager tm = (TelephonyManager) mContext
.getSystemService(Context.TELEPHONY_SERVICE);
try {
if (tm == null) {
throw new NullPointerException("tm == null");
}
tm.getClass().getMethod("answerRingingCall").invoke(tm);
} catch (Exception e) {
Log.e(LOG_TAG, "Unable to use the Telephony Manager directly.", e);
}
Це занадто добре, щоб бути правдою!
Насправді є одна незначна проблема. Цей метод повинен бути повністю функціональним, але менеджер безпеки хоче, щоб абоненти утримували android.permission.MODIFY_PHONE_STATE . Цей дозвіл стосується лише частково задокументованих особливостей системи, оскільки сторонні сторони не повинні її торкатися (як видно з документації до неї). Ви можете спробувати додати <uses-permission>
для нього, але це не принесе користі, оскільки рівень захисту для цього дозволу - це система підпису | ( див. Рядок 1201 ядра / AndroidManifest на 5.0.0_r1 ).
Ви можете прочитати випуск 34785: Оновлення документації для android: protectionLevel, яка була створена ще в 2012 році, щоб побачити, що нам бракує деталей про конкретний "синтаксис конвеєра", але, експериментуючи навколо, здається, він повинен функціонувати як "І", що означає всі зазначені прапори повинні бути виконані для отримання дозволу. Працюючи з цим припущенням, це означало б, що ви повинні мати свою заявку:
Встановлюється як системний додаток.
Це повинно бути добре, і це можна зробити, попросивши користувачів встановити за допомогою ZIP-коду під час відновлення, наприклад, при рутуванні або встановленні програм Google на власні ПЗУ, в яких вони ще не упаковані.
Підписано тим самим підписом, що і фреймворки / база, відома як система, вона ж ПЗУ.
Тут виникають проблеми. Для цього потрібно взяти в руки клавіші, що використовуються для підписання фреймворків / бази. Вам потрібно було б не тільки отримати доступ до ключів Google для заводських зображень Nexus, але ви також мали б отримати доступ до всіх інших ключів розробників OEM та ROM. Це не здається правдоподібним, тому ви можете підписати свою програму системними клавішами, або зробивши власний ПЗУ і попросивши користувачів перейти на нього (що може бути важко), або знайшовши експлойт, за допомогою якого рівень захисту дозволів можна обійти (що також може бути важко).
Крім того, така поведінка, схоже, пов’язана з випуском 34792: Android Jelly Bean / 4.1: android.permission.READ_LOGS більше не працює, що використовує той самий рівень захисту, а також недокументований прапор розвитку.
Робота з TelephonyManager звучить добре, але не буде працювати, якщо ви не отримаєте відповідний дозвіл, що на практиці зробити не так просто.
А як щодо використання TelephonyManager іншими способами?
На жаль, схоже, вам потрібно тримати android.permission.MODIFY_PHONE_STATE, щоб користуватися крутими інструментами, що, в свою чергу, означає, що вам буде важко отримати доступ до цих методів.
Спосіб 2: виклик служби СЕРВІСНИЙ КОД
Коли ви зможете перевірити, що збірка, що працює на пристрої, працюватиме із зазначеним кодом.
Не маючи можливості взаємодіяти з TelephonyManager, існує також можливість взаємодії зі службою через service
виконуваний файл.
Як це працює?
Це досить просто, але документації про цей маршрут є ще менше, ніж для інших. Ми точно знаємо, що виконуваний файл бере два аргументи - ім'я служби та код.
Назва послуги, яку ми хочемо використовувати, - телефон .
Це можна побачити, запустивши service list
.
Код ми хочемо використовувати , як видається , було 6 , але , здається, тепер буде 5 .
Схоже, він заснований на IBinder.FIRST_CALL_TRANSACTION + 5 для багатьох версій зараз (з 1.5_r4 до 4.4.4_r1 ), але під час локального тестування код 5 працював, щоб відповісти на вхідний дзвінок. Оскільки Lollipo є масовим оновленням навколо, зрозуміло, що і тут змінилися внутрішні елементи.
Це призводить до команди service call phone 5
.
Як ми можемо використовувати це програмно?
Java
Наступний код - це груба реалізація, яка виконується як доказ концепції. Якщо ви дійсно хочете продовжувати використовувати цей метод, ви, мабуть, хочете ознайомитися з рекомендаціями щодо безпроблемного використання su та, можливо, перейти на більш розроблений libsuperuser від Chainfire .
try {
Process proc = Runtime.getRuntime().exec("su");
DataOutputStream os = new DataOutputStream(proc.getOutputStream());
os.writeBytes("service call phone 5\n");
os.flush();
os.writeBytes("exit\n");
os.flush();
if (proc.waitFor() == 255) {
}
} catch (IOException e) {
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
Маніфест
<uses-permission android:name="android.permission.ACCESS_SUPERUSER"/>
Чи справді для цього потрібен root-доступ?
На жаль, так здається. Ви можете спробувати використати на ньому Runtime.exec , але мені не вдалося пощастити з цим маршрутом.
Наскільки це стабільно?
Я рада, що ви запитали. Через те, що це не задокументовано, це може розбитися на різні версії, як показано на перший погляд різницею коду. Ім'я служби, ймовірно, повинно залишатися телефоном у різних збірках, але наскільки нам відомо, значення коду може змінюватися в декількох збірках однієї і тієї ж версії (внутрішні модифікації, скажімо, шкіри OEM), в свою чергу порушуючи використаний метод. Тому варто згадати, що тестування проводилося на Nexus 4 (mako / occam). Я особисто радив би вам не використовувати цей метод, але оскільки я не можу знайти більш стабільний метод, я вважаю, що це найкращий знімок.
Оригінальний метод: наміри коду гарнітури
На часи, коли доводиться влаштовуватися.
У наступному розділі під сильним впливом цієї відповіді по Райлі C .
Змодельований метод наміру гарнітури, розміщений у вихідному питанні, здається, транслюється так, як можна було б очікувати, але, схоже, він не досягає мети відповісти на дзвінок. Незважаючи на те, що існує код, який повинен обробляти ці наміри, про них просто не дбають, що має означати, що повинні бути введені якісь нові заходи проти цього методу. Журнал також не показує нічого цікавого, і я особисто не вірю, що копатися в джерелі Android для цього буде варто лише завдяки можливості Google внести невелику зміну, яка в будь-якому випадку легко порушує метод, що використовується.
Чи можна щось зробити зараз?
Поведінка може бути послідовно відтворена за допомогою вхідного виконуваного файлу. Він бере аргумент коду ключа , для якого ми просто передаємо KeyEvent.KEYCODE_HEADSETHOOK . Метод навіть не вимагає кореневого доступу, що робить його придатним для поширених випадків широкого загалу, але в методі є невеликий недолік - подія натискання кнопки гарнітури не може бути вказана, щоб вимагати дозволу, це означає, що вона працює як справжня натискання кнопок і бульбашки вгору по всьому ланцюжку, що, в свою чергу, означає, що ви повинні бути обережними щодо того, коли імітувати натискання кнопки, оскільки це може, наприклад, викликати музичний плеєр для початку відтворення, якщо ніхто інший з вищим пріоритетом не готовий подія.
Код?
new Thread(new Runnable() {
@Override
public void run() {
try {
Runtime.getRuntime().exec("input keyevent " +
Integer.toString(KeyEvent.KEYCODE_HEADSETHOOK));
} catch (IOException e) {
String enforcedPerm = "android.permission.CALL_PRIVILEGED";
Intent btnDown = new Intent(Intent.ACTION_MEDIA_BUTTON).putExtra(
Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_DOWN,
KeyEvent.KEYCODE_HEADSETHOOK));
Intent btnUp = new Intent(Intent.ACTION_MEDIA_BUTTON).putExtra(
Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP,
KeyEvent.KEYCODE_HEADSETHOOK));
mContext.sendOrderedBroadcast(btnDown, enforcedPerm);
mContext.sendOrderedBroadcast(btnUp, enforcedPerm);
}
}
}).start();
tl; д-р
Існує приємний загальнодоступний API для Android 8.0 Oreo та пізніших версій.
До Android 8.0 Oreo не існує загальнодоступного API. Внутрішні API заборонені або просто не мають документації. Слід діяти обережно.