After searching a lot to do it, I publish the code I use in Orasi. Hope this will help many programmers... Note this code is extracted from a soft with other functions, so some code may be not used in all softwares.
First, the function to answer, on all Android version, and to end call (without AIDL !):
/////////////////////////////////////////////////////////////////////////////////////
// Controle le téléphone en utilisant des primitives selon les versions d'OS
private void PhoneControl(int nControl) {
if(nControl == PHONE_END_CALL) { // End call, all Android version
try {
TelephonyManager tm = (TelephonyManager) this.getSystemService(Context.TELEPHONY_SERVICE);
if(tm == null)
return;
tm.getClass().getMethod("endCall").invoke(tm);
bIsEnding = true;
} catch (Exception e) { /* Do Nothing */ }
}
if(nControl == PHONE_ACCEPT_CALL) { // Accept phone call
if(!bCallAccepted) { // Call déjà accepté => pas d'action (évite double action)
bCallAccepted = true;
if(Build.VERSION.SDK_INT >= 26) { // Pris en charge Android >= 8.0
if(checkSelfPermission("android.permission.ANSWER_PHONE_CALLS") == PackageManager.PERMISSION_GRANTED) {
TelecomManager tm = (TelecomManager) this.getSystemService(Context.TELECOM_SERVICE);
if(tm != null)
tm.acceptRingingCall();
}
}
if(Build.VERSION.SDK_INT >= 23 && Build.VERSION.SDK_INT < 26) { // Hangup in Android 6.x and 7.x
MediaSessionManager mediaSessionManager = (MediaSessionManager) getApplicationContext().getSystemService(Context.MEDIA_SESSION_SERVICE);
if(mediaSessionManager != null) {
try {
List<android.media.session.MediaController> mediaControllerList = mediaSessionManager.getActiveSessions
(new ComponentName(getApplicationContext(), NotificationReceiverService.class));
for (android.media.session.MediaController m : mediaControllerList) {
if ("com.android.server.telecom".equals(m.getPackageName())) {
m.dispatchMediaButtonEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_HEADSETHOOK));
m.dispatchMediaButtonEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_HEADSETHOOK));
break;
}
}
} catch (Exception e) { /* Do Nothing */ }
}
}
if(Build.VERSION.SDK_INT < 23) { // Prend en charge jusqu'à Android 5.1
try {
if(Build.MANUFACTURER.equalsIgnoreCase("HTC")) { // Uniquement pour HTC
AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
if(audioManager!=null && !audioManager.isWiredHeadsetOn()) {
Intent i = new Intent(Intent.ACTION_HEADSET_PLUG);
i.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
i.putExtra("state", 0);
i.putExtra("name", "Orasi");
try {
sendOrderedBroadcast(i, null);
} catch (Exception e) { /* Do Nothing */ }
}
}
Runtime.getRuntime().exec("input keyevent " +
Integer.toString(KeyEvent.KEYCODE_HEADSETHOOK));
} catch (Exception 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));
this.sendOrderedBroadcast(btnDown, enforcedPerm);
this.sendOrderedBroadcast(btnUp, enforcedPerm);
}
}
}
}
}
Second, the permissions:
if(Build.VERSION.SDK_INT >= 26) { // Permission necessaire
if(checkSelfPermission("android.permission.ANSWER_PHONE_CALLS") != PackageManager.PERMISSION_GRANTED) {
String szPermissions[] = {"android.permission.ANSWER_PHONE_CALLS"};
requestPermissions(szPermissions, 0);
}
}
if(Build.VERSION.SDK_INT >= 23 && bUseScreen ) { // Permission necessaire
if(checkSelfPermission("android.permission.SYSTEM_ALERT_WINDOW") != PackageManager.PERMISSION_GRANTED) {
Intent myIntent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
startActivity(myIntent);
}
if(Build.VERSION.SDK_INT < 26) { // Permission pour Android 6.x et 7.x
ContentResolver contentResolver = getContentResolver();
String enabledNotificationListeners = Settings.Secure.getString(contentResolver, "enabled_notification_listeners");
String packageName = getPackageName();
if (enabledNotificationListeners == null || !enabledNotificationListeners.contains(packageName)) {
Intent intent2 = new Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS);
startActivity(intent2);
}
}
}
Note that there is a permission for Overlay, not directly to answer phone call.
Notification receiver service:
package mss.micromega.pmignard.orasi;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.os.Build;
import android.service.notification.NotificationListenerService;
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
@SuppressLint("OverrideAbstract")
public class NotificationReceiverService extends NotificationListenerService {
public NotificationReceiverService() {
}
}
Manifest:
<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>
</service>
<uses-permission android:name="android.permission.ANSWER_PHONE_CALLS"/>
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>
Regards.
Edited, I didn't use the good account