Wednesday, September 8, 2010

Android Contacts DB Internal


Important Contact DB Tables:
This article describes few of the important tables of Contact Application Database- raw_contacts, contacts and data. Following section will describe usage of few of the important columns of these tables-


raw_contacts- stores entries for all contacts. So if you have 100 contacts, there will be 100 records in this table. For the purpose of data normalization and reduce data redundancy,  this table stores basic contacts details and foreign keys of other tables (e.g. contacts tables) which in turn stores particular contact details like count of links, whether contact has email id etc.
Column Name
Purpose
Id
unique ID for each Contact
display_name
Contact name (first and last name are clubbed together)
display_name_reverse
. This column is read when you set display order “reverse” instead of display_name
account_type
Source of the account- SIM, Phone, Google, T-Mobile etc. There can be one entry for the owner profile vnd.sec.contact.my_profile
contact_id
Foreign key for contacts table. This field is important when contacts are linked.

Contacts- this table is directly referred while creating Contact List. If a contact is linked with another, its entry will be deleted from this table and will not be displayed in the Contact List.
Column Name
Purpose
Id
unique ID for each Contact
display_name
Contact name (first and last name are clubbed together). Same as raw_contacts table
display_name_reverse
Same as raw_contacts_table
link_type1
Same as account_type of raw_contacts
link_count
How many contacts are linked to this contact
has_email
Probably it helps to quickly check whether a contact has email id rather than reading entry from data table.

Data- this table stores all possible contact details e.g. multiple phone numbers, emails, icons, birthday, company name etc.
Column Name
Purpose
mimetype
Type of data. 5- display name, 6-phone _number, 1- email, 7-contact icon etc.
raw_contact_id
Foreign key for contacts table. This field is important to find out primary entries of a particular contact
data1
Stores contact details row wise. So for 3 details e.g. , and , there will be 3 rows.
data2
. (Fix me: some other numerical code also present.)
data3
. (Fix me: some other numerical code also present.)
data15
Stores contacts icons in binary format (PNG)


Important Contact URI and Sample Usage

content://com.android.contacts/raw_contacts_with_photo

Authority: com.android.contacts (It identifies the ContactsProvider class. So in all URI it will be a constant part.)
Authority Information defined in ContactsProvider Manifest-
<provider android:name="ContactsProvider2"
android:authorities="contacts;com.android.contacts"
../>

Path: raw_contacts_with_photo

Following is a list of important URI to access 3 different tables of Contact Application i.e. contacts, raw_contacts and data- 
Accessing Contacts Table
content://com.android.contacts/contacts
content://com.android.contacts/contacts/#
content://com.android.contacts/contacts/#/data
Accessing Raw_Contacts Table
content://com.android.contacts/raw_contacts
content://com.android.contacts/raw_contacts/#
content://com.android.contacts/raw_contacts/#/data
Accessing Data Table
content://com.android.contacts/data
content://com.android.contacts/data/#
content://com.android.contacts/data/phones
content://com.android.contacts/data/phones/#
 

Friday, September 3, 2010

Call Control in Android

This tutorial is for those who want to control Phone Call in Android OS. Programmatic approach to Accept or Reject call without user intervention.
Kindly note, this approach uses Java Reflection to call methods of an internal class of Android Telephony Framework and might not work with all versions of Android OS. The core concept has been explained in this open code
1st thing 1st, Give the permission...
You need to define 3 User Permissions to handle call related functionality- (due to limitation of Blogger XML Tag marking has been replaced with “[“ and “]”)
[uses-permission android:name="android.permission.READ_PHONE_STATE"/]
[uses-permission android:name="android.permission.MODIFY_PHONE_STATE"/] (For answerRingingCall() method)
[uses-permission android:name="android.permission.CALL_PHONE"/] (For endCall() method)
Define a Receiver...
Create a Receiver which accepts broadcasts with intent action android.intent.action.PHONE_STATE, define following in the Manifest,
 [receiver android:name=".PhoneCall"]
        [intent-filter]
            [action android:name="android.intent.action.PHONE_STATE"/]  
        [/intent-filter]
[/receiver]

Handle Broadcast inside your Receiver...
Override onReceive() method and using Java Reflection try to get the Instance of one of hidden class of Android Telephony Framework- com.android.internal.telephony.ITelephony
ITelephony defines a set of useful methods to control the Call state, which are hidden from general Android API.
private void getTeleService() {
        TelephonyManager tm = (TelephonyManager) context
                .getSystemService(Context.TELEPHONY_SERVICE);
        try {
            // Java reflection to gain access to TelephonyManager's
            // ITelephony getter
            Log.v(TAG, "Get getTeleService...");
            Class c = Class.forName(tm.getClass().getName());
            Method m = c.getDeclaredMethod("getITelephony");
            m.setAccessible(true);
            com.android.internal.telephony.ITelephony telephonyService = (ITelephony) m.invoke(tm);
        } catch (Exception e) {
            e.printStackTrace();
            Log.e(TAG,
                    "FATAL ERROR: could not connect to telephony subsystem");
            Log.e(TAG, "Exception object: " + e);
        }
}
Now, you can use telephonyService (which is an instance of com.android.internal.telephony.ITelephony) to "Accept"/"Reject" call and many other operations.
// Accept Call
telephonyService.answerRingingCall();
// Reject Call
telephonyService.endCall();

Sample Flow for Call Barring Application
1.     Define a set of numbers to be blocked-
String[] blockedNumbers = {"+919811284018", "+919811284012"};
2.     Read incoming phone number
String incommingNumber;
@Override
public void onReceive(Context context, Intent intent) {
Bundle b = intent.getExtras();
incommingNumber = b.getString(TelephonyManager.EXTRA_INCOMING_NUMBER);
// Additional Step
// Check whether this number matches with your defined Block List
// If yes, Reject the Call
}
3.     Reject this Call
telephonyService.endCall();

Hope it helps! 
I have developed one Widget PhoneAway and it uses the above explained concept. Feel free to download its source (through SVN) and APK.

Update: 
To mute an ongoing call, you can use com.android.internal.telephony.ITelephony.setMute(boolean ). You need to make sure the Call State is CALL_STATE_OFFHOOK
e.g.
continuing above code base-
if(tm.getCallState() == TelephonyManager.CALL_STATE_OFFHOOK){
  // mute the call
  telephonyService.setMute(true);
}


Kindly note: I couldn't test properly the above code. Please let me know if the Mute functionality works.