7

Android Q has introduced CallRedirectionService API - seems like one of the way 3rd party apps can use it is to cancel calls and reroute them over VoIP - essentially intercepting the phone calls.

I was trying to implement this class as below

public class CallMonitorService extends CallRedirectionService {
    private static final String TAG = "CallMonitorService";

    public CallMonitorService() {
    }

    @Override
    public void onPlaceCall(Uri uri, PhoneAccountHandle phoneAccountHandle, boolean b) {
        Log.e(TAG, "onPlaceCall:### ");
    }


}

As you can see I am overriding onPlaceCall which is abstract method in the CallRedirectionService and simply keeping a log statement to check/test if this callback method hook is invoked by Android framework.

I added this service in my Manifest.xml as well as below, which is what is documented in the Source code of CallRedirectionService class

<service
                android:name=".CallMonitorService"
                android:permission="android.permission.BIND_REDIRECTION_SERVICE"
                android:enabled="true"
                android:exported="true">
            <intent-filter>
                <action android:name="android.telecom.CallRedirectionService"/>
            </intent-filter>
        </service>

My Guess is that when Android system places a outgoing call this onPlaceCall will be invoked and then we can write our custom code to do further action on the outgoing call. Am not 100% sure if this is how CallRedirectionService is supposed to work - By the way there is no example available in developer.android.com for how to implement CallRedirectionService as of this writing. I set both minSdkVersion 29 targetSdkVersion 29 in my build.gradle

However when the call is made - the onPlaceCall inside my Service is not getting invoked.

I am using Android Q Emulator for testing as i do not have Android phone running Android Q to test this - can this be not tested on Android emulator by placing a emulated phone call or What else Am I missing?

AADProgramming
  • 6,077
  • 11
  • 38
  • 58
  • For Android version after 10 CallRedirectionService is used. But Do you know how to achieve this for below 10 version? – Sandhiya Feb 17 '21 at 14:02
  • Exactly in which method inside onPlaceCall() can get user dialed number? Because I have tried all methods inside that but only get null – Sandhiya Feb 17 '21 at 14:43

2 Answers2

12

The problem is that at the moment documentation is not full and sample in documentation is not correct. To fix this, in your AndroidManifest it is needed to change

android:permission="android.permission.BIND_REDIRECTION_SERVICE"

to

android:permission="android.permission.BIND_CALL_REDIRECTION_SERVICE"

This permission is incorrectly written inside the google documentation for https://developer.android.com/reference/android/telecom/CallRedirectionService.html currently.

Also, it is required to ask user to allow your application play this role. In google documentation this part missing at the moment :

 RoleManager roleManager = null;
    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
        roleManager = (RoleManager) getSystemService(Context.ROLE_SERVICE);

        Intent intent = roleManager.createRequestRoleIntent(RoleManager.ROLE_CALL_REDIRECTION);
        startActivityForResult(intent, 1);}
Alex Kutsko
  • 1,129
  • 11
  • 9
  • 1
    Ok - i will try out above changes – AADProgramming Aug 12 '19 at 16:32
  • Is this still supported? I see it marked in grey here: https://developer.android.com/reference/android/telecom/CallRedirectionService – Gabriel Apr 11 '20 at 15:50
  • For an application targeting version before and after android Q what would be the best way to do this ? – MisterJack49 Feb 04 '21 at 14:29
  • @MisterJack49 Me too have the same doubt. Do you able to find anything? – Sandhiya Feb 17 '21 at 13:16
  • @Sandhiya for now I use the deprecated way it's not ideal but for now it does the job https://android-developers.googleblog.com/2013/05/handling-phone-call-requests-right-way.html – MisterJack49 Feb 18 '21 at 15:15
  • @MisterJack49 Thank you so much!!! Will refer the link – Sandhiya Feb 19 '21 at 05:45
  • @MisterJack49 I have two doubts in using that broadcast. 1) Not able to start an activity from BroadcastReceiver 2) If My app cleared from background, then Broadcast Receiver not triggered(For this I have tried Background Service But No use).. Do you have any idea about this. – Sandhiya Feb 24 '21 at 11:59
  • Are you sure about this permission, @Alek Kutsko, because after applying this permission, I am not able to make any outgoing calls. android:permission="android.permission.BIND_CALL_REDIRECTION_SERVICE" – Partha Chakraborty Apr 20 '21 at 13:54
4

Got it working (looking here). The next code will block all outgoing calls (it's just a sample...) :

MainActivity.kt

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        if (!isRedirection())
            roleAcquire(RoleManager.ROLE_CALL_REDIRECTION)
    }

    private fun isRedirection(): Boolean {
        return isRoleHeldByApp(RoleManager.ROLE_CALL_REDIRECTION)
    }

    private fun isRoleHeldByApp(roleName: String): Boolean {
        val roleManager: RoleManager?
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            roleManager = getSystemService(RoleManager::class.java)
            return roleManager.isRoleHeld(roleName)
        }
        return false
    }

    private fun roleAcquire(roleName: String) {
        val roleManager: RoleManager?
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            if (roleAvailable(roleName)) {
                roleManager = getSystemService(RoleManager::class.java)
                val intent = roleManager.createRequestRoleIntent(roleName)
                startActivityForResult(intent, ROLE_ACQUIRE_REQUEST_CODE)
            } else {
                Toast.makeText(this, "Redirection call with role in not available", Toast.LENGTH_SHORT).show()
            }
        }
    }

    private fun roleAvailable(roleName: String): Boolean {
        val roleManager: RoleManager?
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            roleManager = getSystemService(RoleManager::class.java)
            return roleManager.isRoleAvailable(roleName)
        }
        return false
    }

    companion object {
        private const val ROLE_ACQUIRE_REQUEST_CODE = 4378
    }
}

MyCallRedirectionService.kt

class MyCallRedirectionService : CallRedirectionService() {

    override fun onPlaceCall(handle: Uri, initialPhoneAccount: PhoneAccountHandle, allowInteractiveResponse: Boolean) {
        Log.d("AppLog", "handle:$handle , initialPhoneAccount:$initialPhoneAccount , allowInteractiveResponse:$allowInteractiveResponse")
        cancelCall()
    }
}

AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.callredirectionservicesample">

    <application
        android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <service
            android:name=".MyCallRedirectionService"
            android:permission="android.permission.BIND_CALL_REDIRECTION_SERVICE">
            <intent-filter>
                <action android:name="android.telecom.CallRedirectionService" />
            </intent-filter>
        </service>
    </application>

</manifest>
android developer
  • 114,585
  • 152
  • 739
  • 1,270