Perbarui dengan Android 8.0 Oreo
Meskipun pertanyaan tersebut awalnya diminta untuk dukungan Android L, orang-orang tampaknya masih mengajukan pertanyaan dan jawaban ini, jadi perlu dijelaskan peningkatan yang diperkenalkan di Android 8.0 Oreo. Metode kompatibel mundur masih dijelaskan di bawah ini.
Apa yang berubah?
Dimulai dengan Android 8.0 Oreo , grup izin TELEPON juga berisi izin ANSWER_PHONE_CALLS . Seperti yang disarankan oleh nama izin, menahannya memungkinkan aplikasi Anda menerima panggilan masuk secara terprogram melalui panggilan API yang tepat tanpa peretasan apa pun di sekitar sistem menggunakan refleksi atau simulasi pengguna.
Bagaimana kami memanfaatkan perubahan ini?
Anda harus memeriksa versi sistem pada waktu proses jika Anda mendukung versi Android yang lebih lama sehingga Anda dapat merangkum panggilan API baru ini sambil mempertahankan dukungan untuk versi Android yang lebih lama tersebut. Anda harus mengikuti permintaan izin pada waktu proses untuk mendapatkan izin baru tersebut selama waktu proses, seperti standar pada versi Android yang lebih baru.
Setelah mendapatkan izin, aplikasi Anda cukup memanggil metode acceptRingingCall TelecomManager . Doa dasar terlihat sebagai berikut:
TelecomManager tm = (TelecomManager) mContext
.getSystemService(Context.TELECOM_SERVICE);
if (tm == null) {
throw new NullPointerException("tm == null");
}
tm.acceptRingingCall();
Metode 1: TelephonyManager.answerRingingCall ()
Saat Anda memiliki kendali tak terbatas atas perangkat.
Apa ini?
Ada TelephonyManager.answerRingingCall () yang merupakan metode internal tersembunyi. Ini berfungsi sebagai jembatan untuk ITelephony.answerRingingCall () yang telah dibahas di interwebs dan tampaknya menjanjikan di awal. Ini tidak tersedia di 4.4.2_r1 karena diperkenalkan hanya di commit 83da75d untuk Android 4.4 KitKat ( baris 1537 di 4.4.3_r1 ) dan kemudian "diperkenalkan kembali" di commit f1e1e77 untuk Lollipop ( baris 3138 di 5.0.0_r1 ) karena cara Pohon Git terstruktur. Ini berarti bahwa kecuali Anda hanya mendukung perangkat dengan Lollipop, yang mungkin merupakan keputusan yang buruk karena kecilnya pangsa pasar saat ini, Anda masih perlu menyediakan metode fallback jika menggunakan cara ini.
Bagaimana kita menggunakan ini?
Karena metode yang dimaksud tersembunyi dari penggunaan aplikasi SDK, Anda perlu menggunakan refleksi untuk memeriksa dan menggunakan metode tersebut secara dinamis selama runtime. Jika Anda tidak terbiasa dengan refleksi, Anda dapat dengan cepat membaca Apa itu refleksi, dan mengapa itu berguna? . Anda juga dapat menggali lebih dalam secara spesifik di Trail: The Reflection API jika Anda tertarik untuk melakukannya.
Dan bagaimana itu terlihat dalam kode?
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);
}
Ini terlalu indah untuk menjadi kenyataan!
Sebenarnya, ada satu masalah kecil. Metode ini harus berfungsi penuh, tetapi manajer keamanan ingin penelepon memegang android.permission.MODIFY_PHONE_STATE . Izin ini hanya terdapat pada fitur sistem yang terdokumentasi sebagian karena pihak ketiga tidak diharapkan untuk menyentuhnya (seperti yang Anda lihat dari dokumentasinya). Anda dapat mencoba menambahkan <uses-permission>
untuk itu tetapi itu tidak akan berguna karena tingkat perlindungan untuk izin ini adalah tanda tangan | sistem ( lihat baris 1201 dari core / AndroidManifest di 5.0.0_r1 ).
Anda dapat membaca Masalah 34785: Perbarui dokumentasi android: protectionLevel yang dibuat pada tahun 2012 untuk melihat bahwa kami kehilangan detail tentang "sintaks pipa" tertentu, tetapi dari bereksperimen, tampaknya itu harus berfungsi sebagai 'DAN' yang berarti semua bendera tertentu harus dipenuhi untuk mendapatkan izin. Bekerja dengan asumsi itu, itu berarti Anda harus memiliki aplikasi Anda:
Dipasang sebagai aplikasi sistem.
Ini seharusnya baik-baik saja dan dapat dilakukan dengan meminta pengguna untuk menginstal menggunakan ZIP dalam pemulihan, seperti saat melakukan rooting atau menginstal aplikasi Google pada ROM kustom yang belum dikemas.
Ditandatangani dengan tanda tangan yang sama sebagai kerangka kerja / basis alias sistem, alias ROM.
Di sinilah masalah muncul. Untuk melakukan ini, Anda harus memiliki kunci yang digunakan untuk menandatangani framework / base. Anda tidak hanya harus mendapatkan akses ke kunci Google untuk gambar pabrik Nexus, tetapi Anda juga harus mendapatkan akses ke semua kunci pengembang OEM dan ROM lainnya. Ini tampaknya tidak masuk akal sehingga Anda dapat membuat aplikasi Anda ditandatangani dengan kunci sistem dengan membuat ROM khusus dan meminta pengguna Anda untuk beralih ke sana (yang mungkin sulit) atau dengan menemukan eksploitasi yang dapat digunakan untuk melewati tingkat perlindungan izin. (yang mungkin sulit juga).
Selain itu, perilaku ini tampaknya terkait dengan Masalah 34792: Android Jelly Bean / 4.1: android.permission.READ_LOGS tidak lagi berfungsi yang menggunakan tingkat perlindungan yang sama bersama dengan tanda pengembangan yang tidak berdokumen juga.
Bekerja dengan TelephonyManager terdengar bagus, tapi tidak akan berhasil kecuali Anda mendapatkan izin yang sesuai yang tidak mudah dilakukan dalam praktiknya.
Bagaimana dengan menggunakan TelephonyManager dengan cara lain?
Sayangnya, tampaknya Anda harus memegang izin android.permission.MODIFY_PHONE_STATE untuk menggunakan alat keren yang pada gilirannya berarti Anda akan kesulitan mendapatkan akses ke metode tersebut.
Metode 2: Panggilan Layanan KODE LAYANAN
Saat Anda dapat menguji apakah build yang berjalan di perangkat akan berfungsi dengan kode yang ditentukan.
Tanpa bisa berinteraksi dengan TelephonyManager, ada juga kemungkinan untuk berinteraksi dengan layanan melalui service
executable.
Bagaimana cara kerjanya?
Ini cukup sederhana, tetapi dokumentasi tentang rute ini bahkan lebih sedikit daripada yang lain. Kami tahu pasti bahwa executable membutuhkan dua argumen - nama layanan dan kode.
Nama layanan yang ingin kami gunakan adalah telepon .
Ini bisa dilihat dengan berlari service list
.
The kode kita ingin menggunakan tampaknya telah 6 tetapi tampaknya sekarang menjadi 5 .
Sepertinya itu telah didasarkan pada IBinder.FIRST_CALL_TRANSACTION + 5 untuk banyak versi sekarang (dari 1.5_r4 hingga 4.4.4_r1 ) tetapi selama pengujian lokal, kode 5 berfungsi untuk menjawab panggilan masuk. Karena Lollipo adalah pembaruan besar-besaran, hal itu dapat dimengerti bahwa internal juga berubah di sini.
Ini hasil dengan perintah service call phone 5
.
Bagaimana kita memanfaatkan ini secara terprogram?
Jawa
Kode berikut adalah implementasi kasar yang dibuat untuk berfungsi sebagai bukti konsep. Jika Anda benar-benar ingin melanjutkan dan menggunakan metode ini, Anda mungkin ingin memeriksa pedoman untuk penggunaan su bebas masalah dan mungkin beralih ke libsuperuser yang lebih dikembangkan oleh 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();
}
Nyata
<uses-permission android:name="android.permission.ACCESS_SUPERUSER"/>
Apakah ini benar-benar membutuhkan akses root?
Sayangnya, sepertinya begitu. Anda dapat mencoba menggunakan Runtime.exec di atasnya, tetapi saya tidak bisa mendapatkan keberuntungan dengan rute itu.
Seberapa stabil ini?
Saya senang Anda bertanya. Karena tidak didokumentasikan, ini dapat merusak berbagai versi, seperti yang digambarkan oleh perbedaan kode yang tampak di atas. Nama layanan mungkin harus tetap telepon di berbagai build, tetapi untuk semua yang kita tahu, nilai kode dapat berubah di beberapa build dari versi yang sama (modifikasi internal oleh, katakanlah, skin OEM) pada gilirannya melanggar metode yang digunakan. Oleh karena itu perlu disebutkan pengujian dilakukan pada Nexus 4 (mako / occam). Saya pribadi menyarankan Anda untuk tidak menggunakan metode ini, tetapi karena saya tidak dapat menemukan metode yang lebih stabil, saya yakin ini adalah bidikan terbaik.
Metode asli: Maksud kode tombol headset
Saat-saat ketika Anda harus puas.
Bagian berikut sangat dipengaruhi oleh jawaban ini oleh Riley C .
Metode maksud headset yang disimulasikan seperti yang diposting dalam pertanyaan asli tampaknya disiarkan seperti yang diharapkan, tetapi tampaknya tidak mencapai tujuan untuk menjawab panggilan tersebut. Meskipun tampaknya ada kode di tempat yang harus menangani maksud tersebut, mereka sama sekali tidak diperhatikan, yang berarti harus ada semacam tindakan penanggulangan baru yang diterapkan terhadap metode ini. Log juga tidak menunjukkan apa pun yang menarik dan saya pribadi tidak percaya menggali melalui sumber Android untuk ini akan bermanfaat hanya karena kemungkinan Google memperkenalkan sedikit perubahan yang dengan mudah merusak metode yang digunakan.
Adakah yang bisa kami lakukan sekarang?
Perilaku tersebut dapat direproduksi secara konsisten menggunakan input yang dapat dieksekusi. Dibutuhkan dalam argumen kode kunci, yang hanya kita berikan di KeyEvent.KEYCODE_HEADSETHOOK . Metode ini bahkan tidak memerlukan akses root sehingga cocok untuk kasus penggunaan umum di masyarakat umum, tetapi ada sedikit kekurangan dalam metode ini - acara penekanan tombol headset tidak dapat ditentukan untuk memerlukan izin, artinya ini berfungsi seperti aslinya. tekan tombol dan menggelembung melalui seluruh rantai, yang pada gilirannya berarti Anda harus berhati-hati tentang kapan harus mensimulasikan penekanan tombol karena bisa, misalnya, memicu pemutar musik untuk memulai pemutaran jika tidak ada orang lain dengan prioritas lebih tinggi yang siap untuk menangani acara.
Kode?
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; dr
Ada API publik yang bagus untuk Android 8.0 Oreo dan yang lebih baru.
Tidak ada API publik sebelum Android 8.0 Oreo. API internal terlarang atau tanpa dokumentasi. Anda harus melanjutkan dengan hati-hati.