Update for Android O
Although the question was originally asked to support Android L, people still seem to be confronted with this question and answer, so the latest improvements in Android O are worth mentioning. Backward compatibility methods are still described below.
What changed?
Starting with Android O , the PHONE permission group also contains the ANSWER_PHONE_CALLS permission . As the name of the permission implies, using it allows your application to programmatically accept incoming calls through a proper API call without any hacking into the system using reflection or imitation of the user.
How do we use this change?
You should check the version of the system at run time if you support older versions of Android so that you can encapsulate this new API call while supporting support for those older versions of Android. You must follow to request permissions at runtime to get this new permission at runtime, as is standard for new versions of Android.
After getting permission, your application simply needs to just call the TelecomManager acceptRingingCall method. The main call is as follows:
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();`
Method 1: TelephonyManager.answerRingingCall ()
If you have unlimited control over your device.
What is it?
There is TelephonyManager.answerRingingCall (), which is a hidden internal method. It works like a bridge for ITelephony.answerRingingCall (), which was discussed on interwebs sites and seems promising from the start. This is not available on 4.4.2_r1 because it was only introduced in commit 83da75d for Android 4.4 KitKat ( line 1537 on 4.4.3_r1 ) and then "reactroduced" in commit f1e1e77 for Lollipop ( line 3138 on 5.0.0_r1 ) because of that how the git tree was structured. This means that if you do not only support devices with Lollipop, which is probably a poor solution based on a tiny market share at the moment, you still need to provide backup methods if you go along this route.
How will we use this?
Since this method is hidden from using SDK applications, you need to use reflection to dynamically learn and use the method at runtime. If you are not familiar with reflection, you can quickly read What is reflection and why is it useful? . You can also delve deeper into the Trail: Reflection API specification if you are interested in this.
And what does it look like in code?
// 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); }
This is too good to be true!
Actually, there is one small problem. This method should be fully functional, but the security manager wants callers to contain android.permission.MODIFY_PHONE_STATE . This permission applies only to the partially documented functions of the system, as third parties should not touch it (as you can see from the documentation for it). You can try adding <uses-permission> , but it will not be useful, because the level of protection for this permission is the signature | system ( see line 1201 of the kernel / AndroidManifest at 5.0.0_r1 ).
You can read Problem 34785: update the documentation on android: protectionLevel , which was created in 2012, to see that we lack information on specific "pipe syntax", but, experimenting around, it seems that it should function as an "AND" , that is, all specified flags must be executed for permission. Working in accordance with this assumption, this means that you must have your application:
Installed as a system application.
This should be good and can be achieved by asking users to install using ZIP in recovery, for example, by rooting or installing Google applications on custom ROMs that no longer have them.
A signature with the same signature as frameworks / base aka the system, as well as ROM.
This is where the problems arise. To do this, you need to use the keys used to sign the framework / database. You will not only need to access Google keys for Nexus factory images, but you will also need to access all the keys of developers from other OEMs and ROMs. This does not seem plausible, so you can subscribe to system keys using a custom ROM and ask your users to switch to it (which can be difficult) or find an exploit that can bypass the protection level (which can also be difficult).
In addition, this behavior seems to be related to Problem 34792: Android Jelly Bean / 4.1: android.permission.READ_LOGS no longer works , which uses the same level of protection along with the undocumented development flag.
Working with TelephonyManager sounds good, but will not work if you do not get the appropriate permission, which is not so easy to do in practice.
How about using TelephonyManager in other ways?
Unfortunately, it seems you need to hold on to android.permission.MODIFY_PHONE_STATE to use cool tools, which in turn means that you go to access these methods with difficulty.
Method 2: service call SERVICE CODE
If you can check whether the work performed on the device will work with the specified code.
Without the ability to interact with TelephonyManager, there is also the possibility of interacting with the service through the service executable.
How it works?
It's pretty simple, but there is even less documentation on this route than others. We know for sure that the executable takes two arguments - the name of the service and the code.
the name of the service we want to use is the phone.
This can be seen by running the service list .
the code we want to use was apparently 6, but now it is 5.
It looks like it was based on IBinder.FIRST_CALL_TRANSACTION + 5 for many versions now (from 1.5_r4 to 4.4.4_r1 ), but during local testing, code 5 worked to answer an incoming call. Since Lollipo is a massive update around the world, the intrinsic elements are also changing.
This is the result of the service call phone 5 command.
How do we use this programmatically?
Java
The following code is a crude implementation designed to function as a proof of concept. If you really want to go further and use this method, you will probably want to check out recommendations on how to use su smoothly and perhaps switch to a more complete 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) { // 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(); }
manifest
<uses-permission android:name="android.permission.ACCESS_SUPERUSER"/>
Does it really require root access?
Unfortunately it is so. You can try using Runtime.exec , but I could not get luck with this route.
How stable is it?
I'm glad you asked. Due to the fact that the documentation is not documented, this can violate different versions, as evidenced by the above code difference. The service name should probably remain on the phone in different assemblies, but as far as we know, the code value can change on several lines of the same version (internal modifications, for example, using the OEM shell), which in turn violates the method used . Therefore, it is worth mentioning that testing was conducted on the Nexus 4 (mako / occam). I personally advise you not to use this method, but since I cannot find a more stable method, I think this is the best shot.
Original Method: Intent of Headset Key Encoding
In those days when you have to be content.
The next section was greatly influenced by this Riley C answer .
The simulated headset wash method published in the original question seems to translate as you would expect, but it doesn't seem to reach the goal of answering the call. Despite the fact that there seems to be code that should handle these intentions, they simply are not interested, which should mean that there should be some new countermeasures for this method. The magazine also does not show anything interesting, and I personally do not think that you need to dig through the Android source, because it will be useful only because Google can make a small change that will easily violate the method used.
Is there anything we can do right now?
The behavior can be sequentially reproduced using an executable input file. It takes a keycode argument, for which we simply pass KeyEvent.KEYCODE_HEADSETHOOK . This method does not even require root access, which makes it suitable for common cases for the general public, but there is a slight flaw in the method - the headset button press event that requires permission cannot be specified, that is, it works like a real button press and bubbles up the whole chain, which, in turn, means that you should be careful when simulating a button press, since this can lead, for example, to starting a music player if none of the higher priority is ready to handle the event s.
The code?
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, before Android O
- There is no public API.
- Internal APIs are disabled or simply without documentation.
- You must be careful.