1

I can intercept incoming and outgoing calls. My problem is that it does not work in version 2.3.x

here my code:

public class PhoneCallReceiver extends BroadcastReceiver {

/**
 * Call back which fires off when the phone changes state.  
 */
@Override
public void onReceive(Context context, Intent intent) {


    //TODO
    //===========
    //here i need to chack the number

    //      Bundle b = intent.getExtras();
    //      String incommingNumber = b.getString(TelephonyManager.EXTRA_INCOMING_NUMBER);
    // Additional Step
    // Check whether this number matches with your defined Block List
    // If yes, Reject the Call
    //===========


    /* examine the state of the phone that caused this receiver to fire off */
    String phone_state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
    if (phone_state.equals(TelephonyManager.EXTRA_STATE_RINGING)) 
    {
        //                    if (context.getSharedPreferences(Hc.PREFSNAME,0).getBoolean(Hc.PREF_CALL_ANSWER_TOOLS_KEY, true))
        //                    {
        logMe("Phone Ringing: the phone is ringing, scheduling creation call answer screen activity");
        Intent i = new Intent(context, CallAnswerIntentService.class);
        i.putExtra("delay", 100L);
        context.startService(i);
        logMe("Phone Ringing: started, time to go back to listening");
        //                    } else {
        //                            logMe("Phone Ringing: Call Answer tools disabled by user");
        //                    }
    }
    if (phone_state.equals(TelephonyManager.EXTRA_STATE_OFFHOOK))
    {
        //                    if (context.getSharedPreferences(Hc.PREFSNAME,0).getBoolean(Hc.PREF_SCREEN_GUARD_TOOLS_KEY, true))
        //                    {

        //TODO
        Intent i = new Intent(context,InCallScreenGuardService.class);
        i.putExtra("delay", 100L);
        logMe("Phone Offhook: starting screen guard service");
        context.startService(i);
        //                    } else {
        //                            logMe("Phone Offhook: In-Call Screen Guard disabled by user");
        //                    }
    }
    if (phone_state.equals(TelephonyManager.EXTRA_STATE_IDLE))
    {
        //                    if (context.getSharedPreferences(Hc.PREFSNAME,0).getBoolean(Hc.PREF_SCREEN_GUARD_TOOLS_KEY, true))
        //                    {
        //TODO
        Intent i = new Intent(context,InCallScreenGuardService.class);
        logMe("Phone Idle: stopping screen guard service");
        context.stopService(i);
        //                    } else {
        //                            logMe("Phone Idle: In-Call Screen Guard disabled by user");
        //                    }
    }

    return;
}

this send intent to service and servic send intent to the following activity:

public class CallAnswerActivity extends Activity {
/**
 * whether or not to use the AIDL technique or 
 * the HEADSET_HOOK/package restart techniques
 */
private static final boolean USE_ITELEPHONY = true;

/**
 * internal phone state broadcast receiver
 */
protected BroadcastReceiver r;

/**
 * TelephonyManager instance used by this activity
 */
private TelephonyManager tm;

/**
 * AIDL access to the telephony service process
 */
private com.android.internal.telephony.ITelephony telephonyService;

// ------------------------------------------------------------------------
// primary life cycle call backs
// ------------------------------------------------------------------------

/**
 * main() :)
 */
@Override protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    debugLog("onCreate called");
    setContentView(R.layout.callanswerscreen);

    // grab an instance of telephony manager
    tm = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);

    // connect to the underlying Android telephony system
    if (USE_ITELEPHONY)
        connectToTelephonyService();

    // turn our idle phone state receiver on
    registerReciever();



    // touch screen return button
    Button returnToCallScreen = (Button) findViewById(R.id.returnToCallScreen);
    returnToCallScreen.setOnClickListener(new ReturnButtonOnClickListener());

    // touch screen reject/ignore call button
    Button rejectCall = (Button) findViewById(R.id.rejectCallButton);
    //            if (getSharedPreferences(Hc.PREFSNAME,0).getBoolean(Hc.PREF_ALLOW_REJECT_KEY, true)) 
    rejectCall.setOnLongClickListener(new RejectCallOnLongClickListener());
    //            else 
    //                    rejectCall.setVisibility(View.GONE);                    

    // touch screen answer button
    Button answerButton = (Button) findViewById(R.id.answerCallButton);
    //            if (getSharedPreferences(Hc.PREFSNAME,0).getBoolean(Hc.PREF_ANSWER_WITH_BUTTON_KEY, true)) 
    answerButton.setOnLongClickListener(new AnswerCallOnLongClickListener());
    //            else 
    //                    answerButton.setVisibility(View.GONE);                  
}

/** 
 * (re)register phone state receiver on resume, exit if the phone is idle 
 */
@Override protected void onResume() {
    super.onResume();

    registerReciever();

    if (tm.getCallState() == TelephonyManager.CALL_STATE_IDLE) {
        debugLog("phone is idle, stopping.");
        exitCleanly();
    }
}

/** 
 * unregister phone state receiver, schedule restart if not exiting at the users request 
 */
@Override protected void onPause() {
    super.onPause();

    unHookReceiver();

    if (!isFinishing()) 
    {
        debugLog("system forced pause occured, scheduling delayed restart");
        Intent i = new Intent(getApplicationContext(), CallAnswerIntentService.class);
        i.putExtra("delay", Hc.RESTART_DELAY);
        startService(i);
    }
}

// ------------------------------------------------------------------------
// Input event handler call backs
// ------------------------------------------------------------------------

/** 
 * Track ball press event handler that will answer a call 
 */
@Override public boolean onTrackballEvent(MotionEvent event) {

    switch(event.getAction())
    {
    case MotionEvent.ACTION_MOVE: return true;
    case MotionEvent.ACTION_DOWN: answerCall(); return true;
    default: debugLog("trackball event: "+event);
    }
    return super.dispatchTrackballEvent(event);
}

/**
 * Camera button press event handler that will answer a call
 */
@Override public boolean dispatchKeyEvent(KeyEvent event) {

    switch (event.getKeyCode()) 
    {
    case KeyEvent.KEYCODE_FOCUS: return true;
    case KeyEvent.KEYCODE_CAMERA: answerCall(); return true;
    default: debugLog("Unknown key event: "+event);
    }
    return super.dispatchKeyEvent(event);
}

/**
 * Return button click listener will exit back to the
 * phones stock answer call application.
 */
private class ReturnButtonOnClickListener implements OnClickListener {
    public void onClick(View v) {
        debugLog("returnToCallScreen onClick event");
        exitCleanly();
    }
}

/**
 * Reject button long click listener will reject the
 * incoming call. 
 */
private class RejectCallOnLongClickListener implements OnLongClickListener {
    public boolean onLongClick(View v){
        debugLog("touch screen ignore call button onClick event");
        ignoreCall();
        exitCleanly();
        return true;
    }               
}

/**
 * Answer button long click listener will answer the
 * incoming call.
 */
private class AnswerCallOnLongClickListener implements OnLongClickListener {
    public boolean onLongClick(View v){
        debugLog("touch screen answer button onClick event");
        answerCall();
        return true;
    }               
}

// ------------------------------------------------------------------------
// broadcast receivers
// ------------------------------------------------------------------------

/**
 * register phone state receiver 
 */
private void registerReciever() {
    if (r != null) return;

    r = new BroadcastReceiver() {
        @Override
        public void onReceive(Context c, Intent i)      {
            String phone_state = i.getStringExtra(TelephonyManager.EXTRA_STATE);
            if (!phone_state.equals(TelephonyManager.EXTRA_STATE_RINGING)) 
            {
                debugLog("received "+phone_state+", time to go bye bye, thanks for playing!");
                        exitCleanly();
            }
        } 
    };

    registerReceiver(r, new IntentFilter("android.intent.action.PHONE_STATE"));
}

/**
 * unregister phone state receiver 
 */
private void unHookReceiver() {
    if (r != null) 
    {
        unregisterReceiver(r);
        r = null;
    }
}

// ------------------------------------------------------------------------
// application methods
// ------------------------------------------------------------------------

/** 
 * get an instance of ITelephony to talk handle calls with 
 */
@SuppressWarnings("unchecked") private void connectToTelephonyService() {
    try 
    {
        // "cheat" with Java reflection to gain access to TelephonyManager's ITelephony getter
        Class c = Class.forName(tm.getClass().getName());
        Method m = c.getDeclaredMethod("getITelephony");
        m.setAccessible(true);
          telephonyService = (com.android.internal.telephony.ITelephony) m.invoke(tm);
    } catch (Exception e) {
        e.printStackTrace();
        debugLog("FATAL ERROR: could not connect to telephony subsystem");
        debugLog("Exception object: "+e);
        finish();
    }               
}

//
// answer call
//

/** 
 * answer incoming calls
 */
private void answerCall() {
    if (USE_ITELEPHONY)
        try {
            answerCallAidl();
        } catch (RemoteException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    else
        answerCallHeadsetHook();

    exitCleanly();
}

/**
 * ACTION_MEDIA_BUTTON broadcast technique for answering the phone
 */
private void answerCallHeadsetHook() {
    KeyEvent headsetHook =  new KeyEvent(KeyEvent.ACTION_DOWN,KeyEvent.KEYCODE_HEADSETHOOK);
    Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
    mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, headsetHook);
    sendOrderedBroadcast(mediaButtonIntent, null);
}

/**
 * AIDL/ITelephony technique for answering the phone
 */
private void answerCallAidl() throws RemoteException {
    telephonyService.silenceRinger();
    telephonyService.answerRingingCall();               
}

//
// ignore call
//

/**
 * ignore incoming calls 
 */
private void ignoreCall() {
    if (USE_ITELEPHONY)
        try {
            ignoreCallAidl();
        } catch (RemoteException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    else
        ignoreCallPackageRestart();
}

/**
 * package restart technique for ignoring calls 
 */
private void ignoreCallPackageRestart() {
    ActivityManager am = (ActivityManager)getSystemService(ACTIVITY_SERVICE);
    am.restartPackage("com.android.providers.telephony");
    am.restartPackage("com.android.phone");
}

/** 
 * AIDL/ITelephony technique for ignoring calls
 */
private void ignoreCallAidl() throws RemoteException {
    telephonyService.silenceRinger();
    telephonyService.endCall();
}       

/** 
 * cleanup and exit routine
 */
private void exitCleanly() {
    unHookReceiver();
    moveTaskToBack(true);           
    finish();
}

I need help to make it work on 2.3
thanks

this exeptions:

java.lang.SecurityException: Neither user 10156 nor current process has android.permission.MODIFY_PHONE_STATE.
at android.os.Parcel.readException(Parcel.java:1322)
at android.os.Parcel.readException(Parcel.java:1276)
at com.android.internal.telephony.ITelephony$Stub$Proxy.silenceRinger(ITelephony.java:1231)
at com.y_y_full.photo_dailer.CallAnswerActivity.answerCallAidl(CallAnswerActivity.java:281)
at com.y_y_full.photo_dailer.CallAnswerActivity.answerCall(CallAnswerActivity.java:256)
at com.y_y_full.photo_dailer.CallAnswerActivity.access$3(CallAnswerActivity.java:253)
at com.y_y_full.photo_dailer.CallAnswerActivity$AnswerCallOnLongClickListener.onLongClick(CallAnswerActivity.java:181)
at android.view.View.performLongClick(View.java:2556)
at android.widget.TextView.performLongClick(TextView.java:8358)
at android.view.View$CheckForLongPress.run(View.java:9128)
at android.os.Handler.handleCallback(Handler.java:587)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:130)
at android.app.ActivityThread.main(ActivityThread.java:3691)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:507)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:907)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:665)
at dalvik.system.NativeStart.main(Native Method)
user1458530
  • 21
  • 1
  • 5

2 Answers2

3

The reason it does not work in Gingerbread 2.3.x is this 15031 issue, and 15872 issue Google changed the framework surrounding the permissions for the telephony, much to the annoyance of developers. Although it is not guaranteed, some phone makers leave the permission in place (Sony is one that comes to mind!), others have pulled down the code from Google's AOSP and built their own variants accordingly to their manufacture process, with the changed framework permission in place.

Simply put, the alternative is to have the handset rooted and create a MITM on the telephony socket layer and do the interception before the Android Stack ever sees the events... but on the face of it, its too complicated and not worth doing.

Now that ICS is out now, and the permission is still unchanged so that's something to bear in mind. Meanwhile GB source is left to gather dust as Google moves forward and very little can be done despite "Please fix this" in the 15031 issue page...

t0mm13b
  • 34,087
  • 8
  • 78
  • 110
0

MODIFY_PHONE_STATE permission is reserved for system use only, And ITelephony.silenceRinger() need MODIFY_PHONE_STATE permission. Use AudioManager if need to silent your phone at the time of call. But EndCall() will work fine.

dreamcrash
  • 47,137
  • 25
  • 94
  • 117