Saat saya mencoba membuat layar khusus untuk panggilan masuk, saya mencoba menjawab panggilan masuk secara terprogram. Saya menggunakan kode berikut tetapi tidak berfungsi di Android 5.0.
// Simulate a press of the headset button to pick up the call
Intent buttonDown = new Intent(Intent.ACTION_MEDIA_BUTTON);
buttonDown.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_HEADSETHOOK));
context.sendOrderedBroadcast(buttonDown, "android.permission.CALL_PRIVILEGED");
// froyo and beyond trigger on buttonUp instead of buttonDown
Intent buttonUp = new Intent(Intent.ACTION_MEDIA_BUTTON);
buttonUp.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_HEADSETHOOK));
context.sendOrderedBroadcast(buttonUp, "android.permission.CALL_PRIVILEGED");
Jawaban:
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) { // whether you want to handle this is up to you really 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?
// set the logging tag constant; you probably want to change this final String LOG_TAG = "TelephonyAnswer"; TelephonyManager tm = (TelephonyManager) mContext .getSystemService(Context.TELEPHONY_SERVICE); try { if (tm == null) { // this will be easier for debugging later on throw new NullPointerException("tm == null"); } // do reflection magic tm.getClass().getMethod("answerRingingCall").invoke(tm); } catch (Exception e) { // we catch it all as the following things could happen: // NoSuchMethodException, if the answerRingingCall() is missing // SecurityException, if the security manager is not happy // IllegalAccessException, if the method is not accessible // IllegalArgumentException, if the method expected other arguments // InvocationTargetException, if the method threw itself // NullPointerException, if something was a null value along the way // ExceptionInInitializerError, if initialization failed // something more crazy, if anything else breaks // TODO decide how to handle this state // you probably want to set some failure state/go to fallback 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) { // TODO handle being declined root access // 255 is the standard code for being declined root for SU } } catch (IOException e) { // TODO handle I/O going wrong // this probably means that the device isn't rooted } catch (InterruptedException e) { // don't swallow interruptions Thread.currentThread().interrupt(); }
Nyata
<!-- Inform the user we want them root accesses. --> <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) { // Runtime.exec(String) had an I/O problem, try to fall back 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.
sumber
Solusi yang berfungsi sepenuhnya didasarkan pada kode @Valter Strods.
Untuk membuatnya berfungsi, Anda harus menampilkan aktivitas (tidak terlihat) di layar kunci tempat kode dijalankan.
AndroidManifest.xml
<uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.DISABLE_KEYGUARD" /> <activity android:name="com.mysms.android.lib.activity.AcceptCallActivity" android:launchMode="singleTop" android:excludeFromRecents="true" android:taskAffinity="" android:configChanges="orientation|keyboardHidden|screenSize" android:theme="@style/Mysms.Invisible"> </activity>
Panggilan Terima Aktivitas
package com.mysms.android.lib.activity; import android.app.Activity; import android.app.KeyguardManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.media.AudioManager; import android.os.Build; import android.os.Bundle; import android.telephony.TelephonyManager; import android.view.KeyEvent; import android.view.WindowManager; import org.apache.log4j.Logger; import java.io.IOException; public class AcceptCallActivity extends Activity { private static Logger logger = Logger.getLogger(AcceptCallActivity.class); private static final String MANUFACTURER_HTC = "HTC"; private KeyguardManager keyguardManager; private AudioManager audioManager; private CallStateReceiver callStateReceiver; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); keyguardManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE); audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); } @Override protected void onResume() { super.onResume(); registerCallStateReceiver(); updateWindowFlags(); acceptCall(); } @Override protected void onPause() { super.onPause(); if (callStateReceiver != null) { unregisterReceiver(callStateReceiver); callStateReceiver = null; } } private void registerCallStateReceiver() { callStateReceiver = new CallStateReceiver(); IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED); registerReceiver(callStateReceiver, intentFilter); } private void updateWindowFlags() { if (keyguardManager.inKeyguardRestrictedInputMode()) { getWindow().addFlags( WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED); } else { getWindow().clearFlags( WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED); } } private void acceptCall() { // for HTC devices we need to broadcast a connected headset boolean broadcastConnected = MANUFACTURER_HTC.equalsIgnoreCase(Build.MANUFACTURER) && !audioManager.isWiredHeadsetOn(); if (broadcastConnected) { broadcastHeadsetConnected(false); } try { try { logger.debug("execute input keycode headset hook"); Runtime.getRuntime().exec("input keyevent " + Integer.toString(KeyEvent.KEYCODE_HEADSETHOOK)); } catch (IOException e) { // Runtime.exec(String) had an I/O problem, try to fall back logger.debug("send keycode headset hook intents"); 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)); sendOrderedBroadcast(btnDown, enforcedPerm); sendOrderedBroadcast(btnUp, enforcedPerm); } } finally { if (broadcastConnected) { broadcastHeadsetConnected(false); } } } private void broadcastHeadsetConnected(boolean connected) { Intent i = new Intent(Intent.ACTION_HEADSET_PLUG); i.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); i.putExtra("state", connected ? 1 : 0); i.putExtra("name", "mysms"); try { sendOrderedBroadcast(i, null); } catch (Exception e) { } } private class CallStateReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { finish(); } } }
Gaya
<style name="Mysms.Invisible"> <item name="android:windowFrame">@null</item> <item name="android:windowBackground">@android:color/transparent</item> <item name="android:windowContentOverlay">@null</item> <item name="android:windowNoTitle">true</item> <item name="android:windowIsTranslucent">true</item> <item name="android:windowAnimationStyle">@null</item> </style>
Akhirnya panggil keajaiban!
Intent intent = new Intent(context, AcceptCallActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); context.startActivity(intent);
sumber
Berikut ini adalah pendekatan alternatif yang berhasil bagi saya. Ini mengirimkan peristiwa kunci ke server telekomunikasi secara langsung menggunakan MediaController API. Ini mengharuskan aplikasi memiliki izin BIND_NOTIFICATION_LISTENER_SERVICE dan diberikan secara eksplisit akses Notifikasi dari pengguna:
@TargetApi(Build.VERSION_CODES.LOLLIPOP) void sendHeadsetHookLollipop() { MediaSessionManager mediaSessionManager = (MediaSessionManager) getApplicationContext().getSystemService(Context.MEDIA_SESSION_SERVICE); try { List<MediaController> mediaControllerList = mediaSessionManager.getActiveSessions (new ComponentName(getApplicationContext(), NotificationReceiverService.class)); for (MediaController m : mediaControllerList) { if ("com.android.server.telecom".equals(m.getPackageName())) { m.dispatchMediaButtonEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_HEADSETHOOK)); log.info("HEADSETHOOK sent to telecom server"); break; } } } catch (SecurityException e) { log.error("Permission error. Access to notification not granted to the app."); } }
NotificationReceiverService.class
pada kode diatas bisa jadi hanya kelas kosong.import android.service.notification.NotificationListenerService; public class NotificationReceiverService extends NotificationListenerService{ public NotificationReceiverService() { } }
Dengan bagian yang sesuai di manifes:
<service android:name=".NotificationReceiverService" android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE" android:enabled="true" android:exported="true"> <intent-filter> <action android:name="android.service.notification.NotificationListenerService" /> </intent-filter>
Karena target acara eksplisit, ini mungkin harus menghindari efek samping apa pun yang memicu pemutar media.
Catatan: server telekomunikasi mungkin tidak langsung aktif setelah peristiwa dering. Agar ini bekerja dengan andal, mungkin berguna bagi aplikasi untuk mengimplementasikan MediaSessionManager.OnActiveSessionsChangedListener untuk memantau kapan server telekomunikasi menjadi aktif, sebelum mengirim peristiwa.
Memperbarui:
Di Android O , seseorang perlu melakukan simulasi
ACTION_DOWN
sebelumnyaACTION_UP
, jika tidak, hal di atas tidak akan berpengaruh. yaitu yang berikut ini diperlukan:m.dispatchMediaButtonEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_HEADSETHOOK)); m.dispatchMediaButtonEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_HEADSETHOOK));
Tetapi karena panggilan resmi untuk menjawab panggilan tersedia sejak Android O (lihat jawaban teratas), mungkin tidak diperlukan lagi peretasan ini, kecuali seseorang terjebak dengan tingkat API kompilasi lama sebelum Android O.
sumber
Untuk menguraikan sedikit jawaban oleh @Muzikant, dan memodifikasinya sedikit agar bekerja sedikit lebih bersih di perangkat saya, coba
input keyevent 79
, konstanta untuk KeyEvent.KEYCODE_HEADSETHOOK . Sangat kasar:new Thread(new Runnable() { @Override public void run() { try { Runtime.getRuntime().exec( "input keyevent " + KeyEvent.KEYCODE_HEADSETHOOK ); } catch (Throwable t) { // do something proper here. } } }).start();
Maafkan konvensi pengkodean yang cukup buruk, saya tidak terlalu berpengalaman dalam panggilan Runtime.exec (). Perhatikan bahwa perangkat saya tidak di-root, dan saya juga tidak meminta hak akses root.
Masalah dengan pendekatan ini adalah bahwa itu hanya berfungsi dalam kondisi tertentu (untuk saya). Artinya, jika saya menjalankan utas di atas dari opsi menu yang dipilih pengguna saat panggilan berdering, panggilan akan menjawab dengan baik. Jika saya menjalankannya dari penerima yang memantau status panggilan masuk, itu akan benar-benar diabaikan.
Jadi pada Nexus 5 saya, ini berfungsi dengan baik untuk jawaban yang digerakkan oleh pengguna dan harus sesuai dengan tujuan layar panggilan khusus. Itu tidak akan berfungsi untuk semua jenis aplikasi jenis kontrol panggilan otomatis.
Yang juga perlu diperhatikan adalah semua kemungkinan peringatan, termasuk bahwa ini juga mungkin akan berhenti berfungsi dalam satu atau dua pembaruan.
sumber
input keyevent 79
berfungsi dengan baik di Sony Xperia 5.0. Berfungsi saat menelepon dari suatu aktivitas atau dari penerima siaran.melalui perintah adb Cara menerima panggilan oleh adb
Perlu diingat bahwa Android adalah Linux dengan JVM besar di bagian depan. Anda dapat mengunduh aplikasi baris perintah dan melakukan root pada ponsel dan sekarang Anda memiliki komputer Linux biasa dan baris perintah yang melakukan semua hal normal. Jalankan skrip, Anda bahkan dapat ssh untuk itu (trik OpenVPN)
sumber
Terima kasih @notz jawabannya bekerja untuk saya di Lolillop. Agar kode ini tetap berfungsi dengan SDK Android lama, Anda dapat melakukan kode ini:
if (Build.VERSION.SDK_INT >= 21) { Intent answerCalintent = new Intent(context, AcceptCallActivity.class); answerCalintent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); context.startActivity(answerCalintent); } else { if (telephonyService != null) { try { telephonyService.answerRingingCall(); } catch (Exception e) { answerPhoneHeadsethook(); } } }
sumber
Cara mengaktifkan Speaker Telepon setelah menjawab panggilan secara otomatis.
Saya menyelesaikan masalah saya di atas dengan setSpeakerphoneOn. Saya pikir ini layak untuk diposting di sini, karena kasus penggunaan untuk menjawab panggilan telepon secara otomatis seringkali juga membutuhkan speakerphone agar berguna. Terima kasih sekali lagi untuk semua orang di utas ini, pekerjaan yang luar biasa.
Ini berfungsi untuk saya di Android 5.1.1 di Nexus 4 saya tanpa ROOT. ;)
Izin diperlukan:
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
Kode Java:
// this means the phone has answered if(state==TelephonyManager.CALL_STATE_OFFHOOK) { // try and turn on speaker phone final Handler mHandler = new Handler(); mHandler.postDelayed(new Runnable() { @Override public void run() { AudioManager audioManager = (AudioManager) localContext.getSystemService(Context.AUDIO_SERVICE); // this doesnt work without android.permission.MODIFY_PHONE_STATE // audioManager.setMode(AudioManager.MODE_IN_CALL); // weirdly this works audioManager.setMode(AudioManager.MODE_NORMAL); // this is important audioManager.setSpeakerphoneOn(true); // note the phone interface won't show speaker phone is enabled // but the phone speaker will be on // remember to turn it back off when your done ;) } }, 500); // half a second delay is important or it might fail }
sumber
Jalankan perintah berikut sebagai root:
input keyevent 5
Detail selengkapnya tentang simulasi peristiwa penting di sini .
Anda dapat menggunakan kelas dasar yang saya buat ini untuk menjalankan perintah sebagai root dari aplikasi Anda.
sumber
uji ini: pertama tambahkan izin lalu gunakan killCall () untuk menutup, gunakan answerCall () untuk menjawab panggilan
<uses-permission android:name="android.permission.READ_PHONE_STATE"></uses-permission> <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"></uses-permission> public void killCall() { try { TelephonyManager telephonyManager = (TelephonyManager) getContext().getSystemService(Context.TELEPHONY_SERVICE); Class classTelephony = Class.forName(telephonyManager.getClass().getName()); Method methodGetITelephony = classTelephony.getDeclaredMethod("getITelephony"); methodGetITelephony.setAccessible(true); Object telephonyInterface = methodGetITelephony.invoke(telephonyManager); Class telephonyInterfaceClass = Class.forName(telephonyInterface.getClass().getName()); Method methodEndCall = telephonyInterfaceClass.getDeclaredMethod("endCall"); methodEndCall.invoke(telephonyInterface); } catch (Exception ex) { Log.d(TAG, "PhoneStateReceiver **" + ex.toString()); } } public void answerCall() { try { Runtime.getRuntime().exec("input keyevent " + Integer.toString(KeyEvent.KEYCODE_HEADSETHOOK)); } catch (IOException e) { answerRingingCallWithIntent(); } } public void answerRingingCallWithIntent() { try { Intent localIntent1 = new Intent(Intent.ACTION_HEADSET_PLUG); localIntent1.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); localIntent1.putExtra("state", 1); localIntent1.putExtra("microphone", 1); localIntent1.putExtra("name", "Headset"); getContext().sendOrderedBroadcast(localIntent1, "android.permission.CALL_PRIVILEGED"); Intent localIntent2 = new Intent(Intent.ACTION_MEDIA_BUTTON); KeyEvent localKeyEvent1 = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_HEADSETHOOK); localIntent2.putExtra(Intent.EXTRA_KEY_EVENT, localKeyEvent1); getContext().sendOrderedBroadcast(localIntent2, "android.permission.CALL_PRIVILEGED"); Intent localIntent3 = new Intent(Intent.ACTION_MEDIA_BUTTON); KeyEvent localKeyEvent2 = new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_HEADSETHOOK); localIntent3.putExtra(Intent.EXTRA_KEY_EVENT, localKeyEvent2); getContext().sendOrderedBroadcast(localIntent3, "android.permission.CALL_PRIVILEGED"); Intent localIntent4 = new Intent(Intent.ACTION_HEADSET_PLUG); localIntent4.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); localIntent4.putExtra("state", 0); localIntent4.putExtra("microphone", 1); localIntent4.putExtra("name", "Headset"); getContext().sendOrderedBroadcast(localIntent4, "android.permission.CALL_PRIVILEGED"); } catch (Exception e2) { e2.printStackTrace(); } }
sumber
FYI jika Anda tertarik pada cara MENGAKHIRI panggilan yang sedang berlangsung di Android O, Valter
Method 1: TelephonyManager.answerRingingCall()
bekerja jika Anda mengubah metode yang Anda panggilendCall
.Ini hanya membutuhkan
android.permission.CALL_PHONE
izin.Berikut kodenya:
// set the logging tag constant; you probably want to change this final String LOG_TAG = "TelephonyAnswer"; public void endCall() { TelephonyManager tm = (TelephonyManager) mContext .getSystemService(Context.TELEPHONY_SERVICE); try { if (tm == null) { // this will be easier for debugging later on throw new NullPointerException("tm == null"); } // do reflection magic tm.getClass().getMethod("endCall").invoke(tm); } catch (Exception e) { // we catch it all as the following things could happen: // NoSuchMethodException, if the answerRingingCall() is missing // SecurityException, if the security manager is not happy // IllegalAccessException, if the method is not accessible // IllegalArgumentException, if the method expected other arguments // InvocationTargetException, if the method threw itself // NullPointerException, if something was a null value along the way // ExceptionInInitializerError, if initialization failed // something more crazy, if anything else breaks // TODO decide how to handle this state // you probably want to set some failure state/go to fallback Log.e(LOG_TAG, "Unable to use the Telephony Manager directly.", e); } }
sumber