Why it is impossible to intercept incoming calls on Android

For last several weeks, I’ve been struggling to intercept (not only get notice of) incoming calls on Android, but finally I have to admit that I failed.

Actually, I’m doomed to fail, because it is just Mission Impossible.

For the time being, a BroadcastReceiver for android.intent.action.PHONE_STATE is the only chance for application developers to generally probe for incoming calls. However, it comes too late.

Here is why.

com.android.internal.telephony.gsm.RIL.RILReceiver.run():                                               +---+ 
                                       android.net.LocalSocket.getInputStream() <-----------------------|   |
                                                                  /                                     |   |
                                                                 |                                      |   |
                                                                 v                                      | R |
  com.android.internal.telephony.gsm.RIL.readRilMessage(InputStream, byte[])                            | A |
  com.android.internal.telephony.gsm.RIL.processResponse(Parcel):                                       | D |
  Case 1: Unsolicited Commands - Incoming Call                                                          | I |
    com.android.internal.telephony.gsm.RIL.processUnsolicited(Parcel)                                   | O |
    com.android.internal.telephony.BaseCommands.mCallStateRegistrants.notifyRegistrants(AsyncResult)    |   |
    com.android.internal.telephony.gsm.CallTracker.sendMessage(Message)                                 |   |
                                                        |                                               |   |
                                                        |                                               | S |
                                                        v                                               | Y |
    com.android.internal.telephony.gsm.CallTracker.handleMessage(Message)                               | S |
    com.android.internal.telephony.gsm.CallTracker.pollCallsWhenSafe()                                  | T |
    com.android.internal.telephony.gsm.RIL.getCurrentCalls(Message)                                     | E |
    com.android.internal.telephony.gsm.RIL.send(RILRequest)                                             | M |
                                                     |                                                  |   |
                                                     |                                                  |   |
                                                     v                                                  |   |
    com.android.internal.telephony.gsm.RIL.RILSender.handleMessage(Message)                             |   |
                               android.net.LocalSocket.getOutputStream().write(byte[]) ---------------->|   |
                                                                                                        +---+    
  Case 2: Solicited Commands - Request Call Info
    com.android.internal.telephony.gsm.RIL.processSolicited(Parcel)
    com.android.internal.telephony.gsm.RIL.findAndRemoveRequestFromList(int)
    com.android.internal.telephony.gsm.RILRequest.mResult.sendToTarget()
                                                           |
                                                           |
                                                           v
    com.android.internal.telephony.gsm.CallTracker.handleMessage(Message)
    com.android.internal.telephony.gsm.CallTracker.handlePollCalls(AsyncResult)
    com.android.internal.telephony.gsm.GSMPhone.notifyNewRingingConnection(Connection)
    com.android.internal.telephony.PhoneBase.notifyNewRingingConnectionP(Connection):
      com.android.phone.CallNotifier.sendMessage(Message)
                                           |
                                           |
                                           v
      com.android.phone.CallNotifier.handleMessage(Message)
      com.android.phone.CallNotifier.onNewRingingConnection(AsyncResult)
      com.android.phone.PhoneUtils.showIncomingCallUi() <---------------- Users see incoming call

    com.android.internal.telephony.gsm.CallTracker.updatePhoneState()
    com.android.internal.telephony.gsm.GSMPhone.notifyPhoneStateChanged()
    com.android.internal.telephony.DefaultPhoneNotifier.notifyPhoneState(Phone)
    com.android.server.TelephonyRegistry.notifyCallState(int, String)
    com.android.server.TelephonyRegistry.broadcastCallStateChanged(int, String) <--- Too late to interfere