Professional Documents
Culture Documents
Retrieve Incoming Call
Retrieve Incoming Call
java
Email ThisBlogThis!Share to TwitterShare to Facebook Reverse engineering of android java app using apktool, dex2jar, jd-gui to convert .apk file to .java. By reverse engineering of android app (.apk file) we can get following : understand how a particular UI in an App is constructed reading AndroidManifest.xml - permissions, activities, intents etc in the App native libraries and images used in that App obsfucated code ( android SDK, by default, uses ProGuard tool which shrinks, optimizes, and obfuscates your code by removing unused code and renaming classes, fields, and methods with semantically obscure names.
Required Tools : Download the followings first. Dex2jar from http://code.google.com/p/dex2jar/ JD-GUI from http://java.decompiler.free.fr/?q=jdgui ApkTool from http://code.google.com/p/android-apktool/
Using ApkTool - to extract AndroidManifest.xml and everything in res folder(layout xml files, images, htmls used on
webview etc..) Run the following command : >apktool.bat d sampleApp.apk It also extracts the .smali file of all .class files, but which is difficult to read. ##You can achieve this by using zip utility like 7-zip.
Using dex2jar - to generate .jar file from .apk file, we need JD-GUI to view the source code from this .jar.
Run the following command : >dex2jar sampleApp.apk
Decompiling .jar JD-GUI - it decompiles the .class files (obsfucated- in case of android app, but readable original code is obtained
in case of other .jar file). i.e., we get .java back from the application. Just Run the jd-gui.exe and File->Open to view java code from .jar or .class file.
I would like to retrieve the incoming call's phonenumber and do something with it like the do in Caller ID by WhitePages: A New Android App that Puts Telemarketers on Alert! Currently my code looks like below. When I place the call the CustomBroadcastReceiver catches it and the log message is printed out. I can retrieve the telephone number from the bundle. But! I can't get the CustomPhoneStateListener to work. As you can see I have registered my customPhoneState listener to the receiver but the log message never get's printed out from the CustomPhoneStateListener class. What am I my missing here? Is my thinking correct?
Code:
<receiver android:name=".CustomBroadcastReceiver"> <intent-filter> <action android:name="android.intent.action.PHONE_STATE" /> </intent-filter> </receiver> </application> <uses-sdk android:minSdkVersion="5" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.WRITE_CONTACTS" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" />
Code:
public class CustomPhoneStateListener extends PhoneStateListener { private static final String TAG = "CustomPhoneStateListener"; public void onCallStateChange @Override public void onCallStateChanged (int state, String incomingNumber){ Log.v(TAG, "WE ARE INSIDE!!!!!!!!!!!"); Log.v(TAG, incomingNumber); switch(state){ case TelephonyManager.CALL_STATE_RINGING: Log.d(TAG, "RINGING"); break; } }
Code:
public class CustomBroadcastReceiver extends BroadcastReceiver { private static final String TAG = "CustomBroadcastReceiver"; @Override public void onReceive(Context context, Intent intent) { Log.v(TAG, "WE ARE INSIDE!!!!!!!!!!!"); TelephonyManager telephony = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE); CustomPhoneStateListener customPhoneListener = new CustomPhoneStateListener(); telephony.listen(customPhoneListener, PhoneStateListener.LISTEN_CALL_STATE);
Bundle bundle = intent.getExtras(); String phoneNr= bundle.getString("incoming_number"); Log.v(TAG, "phoneNr: "+phoneNr); } In your CustomPhoneStateListener class you have: public void onCallStateChange Try changing to: @Override public void onCallStateChanged I'm trying the same functionality myself. If I can get it to work, I'll post an update.
drewashby
New Member Join Date: Sep 2010 Posts: 2 Device(s): Thanks: 0 Thanked 0 Times in 0 Posts
Works for me now. Your original code, with the modification from my last post, and I now see the log messages from the CustomPhoneStateListener.
What jakob does not mention is that you have to register the PhoneStateListener prior to use it. Put the following code somewhere before you want to intercept the incoming calls number (e.g. in the onCreate method of your activity): TelephonyManager manager=(TelephonyManager)getSystemService(TELEPHONY_SERVICE); manager.listen(new CustomPhoneStateListener(), PhoneStateListener.LISTEN_CALL_STATE); Then the onCallStateChanged method should be triggered. I didn't know that this method is deprecated in 4.0.3. But I think as a quick solution this should still be fine.
I'm pretty sure that in the CallBroadcastReceiver class, that jakob made, he uses:CustomPhoneStateListener customPhoneListener = new CustomPhoneStateListener(); telephony.listen(customPhoneListener, PhoneStateListener.LISTEN_CALL_STATE); However, it's still not reaching this point. I want to ultimately have this in a Service and send a text message to the incoming number, still having no luck.. DizzyThermal Jan 16 at 22:08 Yes, but he registers the PhoneStateListener in the onReceive Method of the BroadcastReceiver. This means, it is registered when the BroadcastReceiver is triggered. As it is triggered when the call arrives, the PhoneStateListener is registered after the phone call has arriven. And then the PhoneStateListener is not triggered, because it only listens to state changes and not if there is an ongoing call. Maybe it is triggered again when the call is hung up. But better register the PhoneStateListener in the onCreate Method or somewhere similar, and it should work fine. Dude Jan 16 at 22:33
When I try to register the PhoneStateListener in the onStart(Intent intent, int startId) portion of my service it crashes the app.. It is defined exactly as you said and it is also defined in the Manifest.. DizzyThermal Jan 16 at 23:17 Hmm...what is the error in logcat? Dude Jan 17 at 9:46 There's actually a ton of them.. However, I feel like the Intent for the BroadcastReceiver might be wrong.. It never enters the BroadcastReceiver.. Therefore I would think that it is not properly being triggered, no? Here are my receivers: registerReceiver(smsReceiver, new IntentFilter("android.provider.Telephony.SMS_RECEIVED")); registerReceiver(resend, new IntentFilter("SMS_SENT")); registerReceiver(callReceiver, new IntentFilter("android.intent.action.PHONE_STATE")); *Sorry for the awkward line breaks.. >.> I ended up getting it.. I needed to drop my minSdkVersion from 7 to 3 and it worked.. So clearly it is deprecated in newer versions of Android.. :-\ Thanks for the help @Dude.. DizzyThermal Jan 17 at 23:35 Good to know.:-) Dude Jan 19 at 22:21 thanks for the help though.. I figured after awhile I was coming off as a pest, but it was a problem that completely halted development.. DizzyThermal Jan 21 at 10:21
n order to get this to work in newer versions of Android (4.0.3 etc) you need to make sure that yourminSdkVersion is 3.. The issue with my code was that my minSdkVersion was 7..
=========================================================
As suggested by Krutix, the call LOG, is a LOG of finished phone calls which is written to by the dialer app after a call is finished. As such you will not find which number is currently dialing in the content provider. Here is an implementation which will allow you to retrieve the phone number if it is an incoming phone call as incomingNumber and also when the call is FINISHED - note the Handler() code. private class PhoneCallListener extends PhoneStateListener { private boolean isPhoneCalling = false; @Override public void onCallStateChanged(int state, String incomingNumber) { if (TelephonyManager.CALL_STATE_RINGING == state) { // phone ringing Log.i(LOG_TAG, "RINGING, number: " + incomingNumber); } if (TelephonyManager.CALL_STATE_OFFHOOK == state) { // active Log.i(LOG_TAG, "OFFHOOK"); isPhoneCalling = true; } if (TelephonyManager.CALL_STATE_IDLE == state) { // run when class initial and phone call ended, need detect flag // from CALL_STATE_OFFHOOK Log.i(LOG_TAG, "IDLE number");
if (isPhoneCalling) { Handler handler = new Handler(); //Put in delay because call log is not updated immediately when state changed // The dialler takes a little bit of time to write to it 500ms seems to be enough handler.postDelayed(new Runnable() { @Override public void run() { // get start of cursor Log.i("CallLogDetailsActivity", "Getting Log activity..."); String[] projection = new String[]{Calls.NUMBER}; Cursor cur = getContentResolver().query(Calls.CONTENT_URI, projection, null, null, Calls.DATE +" desc"); cur.moveToFirst(); String lastCallnumber = cur.getString(0); } },500); isPhoneCalling = false; } } } } And then add and initialise the listener in your onCreate or onStartCommand code: PhoneCallListener phoneListener = new PhoneCallListener(); TelephonyManager telephonyManager = (TelephonyManager) this .getSystemService(Context.TELEPHONY_SERVICE); telephonyManager.listen(phoneListener, PhoneStateListener.LISTEN_CALL_STATE);
String callNumber = cur.getString(cur.getColumnIndex(android.provider.CallLog.Calls.NUMBER)); String callName = cur.getString(cur.getColumnIndex(android.provider.CallLog.Calls.CACHED_NAME )); String callDate = cur.getString(cur.getColumnIndex(android.provider.CallLog.Calls.DATE)); String callType = cur.getString(cur.getColumnIndex(android.provider.CallLog.Calls.TYPE)); String isCallNew = cur.getString(cur.getColumnIndex(android.provider.CallLog.Calls.NEW)); String duration = cur.getString(cur.getColumnIndex(android.provider.CallLog.Calls.DURATION)); // process log data... } }
ZipEntry entry = null; while ((entry = zin.getNextEntry()) != null) { String dirName = workingDir; int endIndex = entry.getName().lastIndexOf(File.separatorChar); if (endIndex != -1) { dirName += entry.getName().substring(0, endIndex); } File newDir = new File(dirName); // If the directory that this entry should be inflated under does // not exist, create it. if (!newDir.exists() && !newDir.mkdir()) { throw new ZipException("Could Not Create Directory " + dirName + "."); } FileOutputStream fos = new FileOutputStream(workingDir + entry.getName()); // Copy Data From the Zip Entry to a File while ((bytesRead = zin.read(buffer)) != -1) { fos.write(buffer, 0, bytesRead); } fos.close();
} zin.close();
public static void main(String[] args) { try { unzipFile(new File("test.zip")); System.out.println("Success"); } catch (Exception e) { e.printStackTrace(); System.out.println("Error - " + e.getMessage()); } } }
String displayName = cur.getString(cur.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME)); /** Phone Numbers **/ Cursor pCur = cr.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?", new String[] { id }, null); while (pCur.moveToNext()) { String number = pCur.getString(pCur.getColumnIndex(ContactsContract.CommonDataKinds.Phone.N UMBER)); String typeStr = pCur.getString(pCur.getColumnIndex(ContactsContract.CommonDataKinds.Phone.T YPE)); } pCur.close(); /** EMAIL **/ Cursor emailCur = cr.query(ContactsContract.CommonDataKinds.Email.CONTENT_URI, null, ContactsContract.CommonDataKinds.Email.CONTACT_ID + " = ?", new String[] { id }, null); while (emailCur.moveToNext()) { String email = emailCur.getString(emailCur.getColumnIndex(ContactsContract.CommonDataKinds .Email.DATA)); String emailType = emailCur.getString(emailCur.getColumnIndex(ContactsContract.CommonDataKinds .Email.TYPE)); } emailCur.close(); } } } Permission in AndroidManifest.xml: Add the READ_CONTACTS permission to your AndroidManifest.xml: <uses-permission android:name="android.permission.READ_CONTACTS"></usespermission>
1.
If you are confused about how to call this function : the code presented above is static function which takes instance of ContentResolver as parameter. You can call it YourClassName.getContacts(instace_of_ContentResolver as : ).
If you want to read the contact details of a particular person You can add parameter to this function and compare with the results to get more information. Thanks. Reply 2.
AnonymousJanuary 6, 2012 1:16 AM I understand but need example to test, can u provide please? Reply 3. Ganesh TiwariJanuary 6, 2012 5:41 AM you can paste this code on any basic 'HelloWorld' type project class and call it from there.
Setup The Android Manifest With The Permission And The Intent Receiver
01 <manifest xmlns:android="http://schemas.android.com/apk/res/android" 02 package="org.apache.sms"> 03 <uses-permission id="android.permission.RECEIVE_SMS" /> 04 <application> 05 <receiver class="SMSApp"> 06 <intent-filter> 07 <action android:value="android.provider.Telephony.SMS_RECEI VED" /> 08 </intent-filter> 09 </receiver> 10 </application> 11 </manifest>
14 15 16 17 18 19 20 21 22 /* package */ static final String ACTION = "android.provider.Telephony.SMS_RECEIVED"; public void onReceiveIntent(Context context, Intent intent) { if (intent.getAction().equals(ACTION)) { StringBuilder buf = new StringBuilder(); Bundle bundle = intent.getExtras();
if (bundle != null) { SmsMessage[] messages = 23 Telephony.Sms.Intents.getMessagesFromIntent(intent); 24 for (int i = 0; i < messages.length; i++) { 25 SmsMessage message = messages[i]; 26 buf.append("Received SMS from "); 27 buf.append(message.getDisplayOriginatingAddress()); 28 buf.append(" - "); 29 buf.append(message.getDisplayMessageBody()); 30 } 31 } 32 Log.i(LOG_TAG, "[SMSApp] onReceiveIntent: " + buf); NotificationManager nm = (NotificationManager) 33 context.getSystemService( 34 Context.NOTIFICATION_SERVICE); 35 36 nm.notifyWithText(123, buf.toString(), 37 NotificationManager.LENGTH_LONG, null); 38 39 } 40 } 41 42 43 44 45 46 47 48 } private void appendData(StringBuilder buf, String key, String value) { buf.append(", "); buf.append(key); buf.append('='); buf.append(value); }
Open a telnet session to localhost at port 5554 and type sms send +5085551212 hello to simulate an sms message