Home | History | Annotate | Download | only in map
      1 /*
      2 * Copyright (C) 2013 Samsung System LSI
      3 * Licensed under the Apache License, Version 2.0 (the "License");
      4 * you may not use this file except in compliance with the License.
      5 * You may obtain a copy of the License at
      6 *
      7 *      http://www.apache.org/licenses/LICENSE-2.0
      8 *
      9 * Unless required by applicable law or agreed to in writing, software
     10 * distributed under the License is distributed on an "AS IS" BASIS,
     11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 * See the License for the specific language governing permissions and
     13 * limitations under the License.
     14 */
     15 package com.android.bluetooth.map;
     16 
     17 import java.io.ByteArrayOutputStream;
     18 import java.io.IOException;
     19 import java.io.InputStream;
     20 import java.io.UnsupportedEncodingException;
     21 import java.text.ParseException;
     22 
     23 import org.apache.http.util.ByteArrayBuffer;
     24 
     25 import android.content.ContentResolver;
     26 import android.content.ContentValues;
     27 import android.content.Context;
     28 import android.database.Cursor;
     29 import android.net.Uri;
     30 import android.provider.BaseColumns;
     31 import android.provider.ContactsContract;
     32 import android.provider.ContactsContract.Contacts;
     33 import android.provider.ContactsContract.PhoneLookup;
     34 import android.provider.Telephony.Mms;
     35 import android.provider.Telephony.Sms;
     36 import android.telephony.TelephonyManager;
     37 import android.util.Log;
     38 
     39 import com.android.bluetooth.map.BluetoothMapUtils.TYPE;
     40 import com.google.android.mms.pdu.CharacterSets;
     41 
     42 public class BluetoothMapContent {
     43     private static final String TAG = "BluetoothMapContent";
     44 
     45     private static final boolean D = false;
     46     private static final boolean V = false;
     47 
     48     private static final int MASK_SUBJECT = 0x1;
     49     private static final int MASK_DATETIME = 0x2;
     50     private static final int MASK_SENDER_NAME = 0x4;
     51     private static final int MASK_SENDER_ADDRESSING = 0x8;
     52 
     53     private static final int MASK_RECIPIENT_NAME = 0x10;
     54     private static final int MASK_RECIPIENT_ADDRESSING = 0x20;
     55     private static final int MASK_TYPE = 0x40;
     56     private static final int MASK_SIZE = 0x80;
     57 
     58     private static final int MASK_RECEPTION_STATUS = 0x100;
     59     private static final int MASK_TEXT = 0x200;
     60     private static final int MASK_ATTACHMENT_SIZE = 0x400;
     61     private static final int MASK_PRIORITY = 0x800;
     62 
     63     private static final int MASK_READ = 0x1000;
     64     private static final int MASK_SENT = 0x2000;
     65     private static final int MASK_PROTECTED = 0x4000;
     66     private static final int MASK_REPLYTO_ADDRESSING = 0x8000;
     67 
     68     /* Type of MMS address. From Telephony.java it must be one of PduHeaders.BCC, */
     69     /* PduHeaders.CC, PduHeaders.FROM, PduHeaders.TO. These are from PduHeaders.java */
     70     public static final int MMS_FROM = 0x89;
     71     public static final int MMS_TO = 0x97;
     72     public static final int MMS_BCC = 0x81;
     73     public static final int MMS_CC = 0x82;
     74 
     75     private Context mContext;
     76     private ContentResolver mResolver;
     77 
     78     static final String[] SMS_PROJECTION = new String[] {
     79         BaseColumns._ID,
     80         Sms.THREAD_ID,
     81         Sms.ADDRESS,
     82         Sms.BODY,
     83         Sms.DATE,
     84         Sms.READ,
     85         Sms.TYPE,
     86         Sms.STATUS,
     87         Sms.LOCKED,
     88         Sms.ERROR_CODE,
     89     };
     90 
     91     static final String[] MMS_PROJECTION = new String[] {
     92         BaseColumns._ID,
     93         Mms.THREAD_ID,
     94         Mms.MESSAGE_ID,
     95         Mms.MESSAGE_SIZE,
     96         Mms.SUBJECT,
     97         Mms.CONTENT_TYPE,
     98         Mms.TEXT_ONLY,
     99         Mms.DATE,
    100         Mms.DATE_SENT,
    101         Mms.READ,
    102         Mms.MESSAGE_BOX,
    103         Mms.STATUS,
    104     };
    105 
    106     private class FilterInfo {
    107         public static final int TYPE_SMS = 0;
    108         public static final int TYPE_MMS = 1;
    109 
    110         int msgType = TYPE_SMS;
    111         int phoneType = 0;
    112         String phoneNum = null;
    113         String phoneAlphaTag = null;
    114     }
    115 
    116     public BluetoothMapContent(final Context context) {
    117         mContext = context;
    118         mResolver = mContext.getContentResolver();
    119         if (mResolver == null) {
    120             Log.d(TAG, "getContentResolver failed");
    121         }
    122     }
    123 
    124     private void addSmsEntry() {
    125         if (D) Log.d(TAG, "*** Adding dummy sms ***");
    126 
    127         ContentValues mVal = new ContentValues();
    128         mVal.put(Sms.ADDRESS, "1234");
    129         mVal.put(Sms.BODY, "Hello!!!");
    130         mVal.put(Sms.DATE, System.currentTimeMillis());
    131         mVal.put(Sms.READ, "0");
    132 
    133         Uri mUri = mResolver.insert(Sms.CONTENT_URI, mVal);
    134     }
    135 
    136     private BluetoothMapAppParams buildAppParams() {
    137         BluetoothMapAppParams ap = new BluetoothMapAppParams();
    138         try {
    139             int paramMask = (MASK_SUBJECT
    140                 | MASK_DATETIME
    141                 | MASK_SENDER_NAME
    142                 | MASK_SENDER_ADDRESSING
    143                 | MASK_RECIPIENT_NAME
    144                 | MASK_RECIPIENT_ADDRESSING
    145                 | MASK_TYPE
    146                 | MASK_SIZE
    147                 | MASK_RECEPTION_STATUS
    148                 | MASK_TEXT
    149                 | MASK_ATTACHMENT_SIZE
    150                 | MASK_PRIORITY
    151                 | MASK_READ
    152                 | MASK_SENT
    153                 | MASK_PROTECTED
    154                 );
    155             ap.setMaxListCount(5);
    156             ap.setStartOffset(0);
    157             ap.setFilterMessageType(0);
    158             ap.setFilterPeriodBegin("20130101T000000");
    159             ap.setFilterPeriodEnd("20131230T000000");
    160             ap.setFilterReadStatus(0);
    161             ap.setParameterMask(paramMask);
    162             ap.setSubjectLength(10);
    163             /* ap.setFilterOriginator("Sms*"); */
    164             /* ap.setFilterRecipient("41*"); */
    165         } catch (ParseException e) {
    166             return null;
    167         }
    168         return ap;
    169     }
    170 
    171     private void printSms(Cursor c) {
    172         String body = c.getString(c.getColumnIndex(Sms.BODY));
    173         if (D) Log.d(TAG, "printSms " + BaseColumns._ID + ": " + c.getLong(c.getColumnIndex(BaseColumns._ID)) +
    174                 " " + Sms.THREAD_ID + " : " + c.getLong(c.getColumnIndex(Sms.THREAD_ID)) +
    175                 " " + Sms.ADDRESS + " : " + c.getString(c.getColumnIndex(Sms.ADDRESS)) +
    176                 " " + Sms.BODY + " : " + body.substring(0, Math.min(body.length(), 8)) +
    177                 " " + Sms.DATE + " : " + c.getLong(c.getColumnIndex(Sms.DATE)) +
    178                 " " + Sms.TYPE + " : " + c.getInt(c.getColumnIndex(Sms.TYPE)));
    179     }
    180 
    181     private void printMms(Cursor c) {
    182         if (D) Log.d(TAG, "printMms " + BaseColumns._ID + ": " + c.getLong(c.getColumnIndex(BaseColumns._ID)) +
    183                 "\n   " + Mms.THREAD_ID + " : " + c.getLong(c.getColumnIndex(Mms.THREAD_ID)) +
    184                 "\n   " + Mms.MESSAGE_ID + " : " + c.getString(c.getColumnIndex(Mms.MESSAGE_ID)) +
    185                 "\n   " + Mms.SUBJECT + " : " + c.getString(c.getColumnIndex(Mms.SUBJECT)) +
    186                 "\n   " + Mms.CONTENT_TYPE + " : " + c.getString(c.getColumnIndex(Mms.CONTENT_TYPE)) +
    187                 "\n   " + Mms.TEXT_ONLY + " : " + c.getInt(c.getColumnIndex(Mms.TEXT_ONLY)) +
    188                 "\n   " + Mms.DATE + " : " + c.getLong(c.getColumnIndex(Mms.DATE)) +
    189                 "\n   " + Mms.DATE_SENT + " : " + c.getLong(c.getColumnIndex(Mms.DATE_SENT)) +
    190                 "\n   " + Mms.READ + " : " + c.getInt(c.getColumnIndex(Mms.READ)) +
    191                 "\n   " + Mms.MESSAGE_BOX + " : " + c.getInt(c.getColumnIndex(Mms.MESSAGE_BOX)) +
    192                 "\n   " + Mms.STATUS + " : " + c.getInt(c.getColumnIndex(Mms.STATUS)));
    193     }
    194 
    195     private void printMmsAddr(long id) {
    196         final String[] projection = null;
    197         String selection = new String("msg_id=" + id);
    198         String uriStr = String.format("content://mms/%d/addr", id);
    199         Uri uriAddress = Uri.parse(uriStr);
    200         Cursor c = mResolver.query(
    201             uriAddress,
    202             projection,
    203             selection,
    204             null, null);
    205 
    206         if (c.moveToFirst()) {
    207             do {
    208                 String add = c.getString(c.getColumnIndex("address"));
    209                 Integer type = c.getInt(c.getColumnIndex("type"));
    210                 if (type == MMS_TO) {
    211                     if (D) Log.d(TAG, "   recipient: " + add + " (type: " + type + ")");
    212                 } else if (type == MMS_FROM) {
    213                     if (D) Log.d(TAG, "   originator: " + add + " (type: " + type + ")");
    214                 } else {
    215                     if (D) Log.d(TAG, "   address other: " + add + " (type: " + type + ")");
    216                 }
    217 
    218             } while(c.moveToNext());
    219         }
    220     }
    221 
    222     private void printMmsPartImage(long partid) {
    223         String uriStr = String.format("content://mms/part/%d", partid);
    224         Uri uriAddress = Uri.parse(uriStr);
    225         int ch;
    226         StringBuffer sb = new StringBuffer("");
    227         InputStream is = null;
    228 
    229         try {
    230             is = mResolver.openInputStream(uriAddress);
    231 
    232             while ((ch = is.read()) != -1) {
    233                 sb.append((char)ch);
    234             }
    235             if (D) Log.d(TAG, sb.toString());
    236 
    237         } catch (IOException e) {
    238             // do nothing for now
    239             e.printStackTrace();
    240         }
    241     }
    242 
    243     private void printMmsParts(long id) {
    244         final String[] projection = null;
    245         String selection = new String("mid=" + id);
    246         String uriStr = String.format("content://mms/%d/part", id);
    247         Uri uriAddress = Uri.parse(uriStr);
    248         Cursor c = mResolver.query(
    249             uriAddress,
    250             projection,
    251             selection,
    252             null, null);
    253 
    254         if (D) Log.d(TAG, "   parts:");
    255         if (c.moveToFirst()) {
    256             do {
    257                 Long partid = c.getLong(c.getColumnIndex(BaseColumns._ID));
    258                 String ct = c.getString(c.getColumnIndex("ct"));
    259                 String name = c.getString(c.getColumnIndex("name"));
    260                 String charset = c.getString(c.getColumnIndex("chset"));
    261                 String filename = c.getString(c.getColumnIndex("fn"));
    262                 String text = c.getString(c.getColumnIndex("text"));
    263                 Integer fd = c.getInt(c.getColumnIndex("_data"));
    264                 String cid = c.getString(c.getColumnIndex("cid"));
    265                 String cl = c.getString(c.getColumnIndex("cl"));
    266                 String cdisp = c.getString(c.getColumnIndex("cd"));
    267 
    268                 if (D) Log.d(TAG, "     _id : " + partid +
    269                     "\n     ct : " + ct +
    270                     "\n     partname : " + name +
    271                     "\n     charset : " + charset +
    272                     "\n     filename : " + filename +
    273                     "\n     text : " + text +
    274                     "\n     fd : " + fd +
    275                     "\n     cid : " + cid +
    276                     "\n     cl : " + cl +
    277                     "\n     cdisp : " + cdisp);
    278 
    279                 /* if (ct.equals("image/jpeg")) { */
    280                 /*     printMmsPartImage(partid); */
    281                 /* } */
    282             } while(c.moveToNext());
    283         }
    284     }
    285 
    286     public void dumpMmsTable() {
    287         if (D) Log.d(TAG, "**** Dump of mms table ****");
    288         Cursor c = mResolver.query(Mms.CONTENT_URI,
    289                 MMS_PROJECTION, null, null, "_id DESC");
    290         if (c != null) {
    291             if (D) Log.d(TAG, "c.getCount() = " + c.getCount());
    292             c.moveToPosition(-1);
    293             while (c.moveToNext()) {
    294                 printMms(c);
    295                 long id = c.getLong(c.getColumnIndex(BaseColumns._ID));
    296                 printMmsAddr(id);
    297                 printMmsParts(id);
    298             }
    299         } else {
    300             Log.d(TAG, "query failed");
    301             c.close();
    302         }
    303     }
    304 
    305     public void dumpSmsTable() {
    306         addSmsEntry();
    307         if (D) Log.d(TAG, "**** Dump of sms table ****");
    308         Cursor c = mResolver.query(Sms.CONTENT_URI,
    309                 SMS_PROJECTION, null, null, "_id DESC");
    310         if (c != null) {
    311             if (D) Log.d(TAG, "c.getCount() = " + c.getCount());
    312             c.moveToPosition(-1);
    313             while (c.moveToNext()) {
    314                 printSms(c);
    315             }
    316         } else {
    317             Log.d(TAG, "query failed");
    318             c.close();
    319         }
    320 
    321     }
    322 
    323     public void dumpMessages() {
    324         dumpSmsTable();
    325         dumpMmsTable();
    326 
    327         BluetoothMapAppParams ap = buildAppParams();
    328         if (D) Log.d(TAG, "message listing size = " + msgListingSize("inbox", ap));
    329         BluetoothMapMessageListing mList = msgListing("inbox", ap);
    330         try {
    331             mList.encode();
    332         } catch (UnsupportedEncodingException ex) {
    333             /* do nothing */
    334         }
    335         mList = msgListing("sent", ap);
    336         try {
    337             mList.encode();
    338         } catch (UnsupportedEncodingException ex) {
    339             /* do nothing */
    340         }
    341     }
    342 
    343     private void setProtected(BluetoothMapMessageListingElement e, Cursor c,
    344         FilterInfo fi, BluetoothMapAppParams ap) {
    345         if ((ap.getParameterMask() & MASK_PROTECTED) != 0) {
    346             String protect = "no";
    347             if (D) Log.d(TAG, "setProtected: " + protect);
    348             e.setProtect(protect);
    349         }
    350     }
    351 
    352     private void setSent(BluetoothMapMessageListingElement e, Cursor c,
    353         FilterInfo fi, BluetoothMapAppParams ap) {
    354         if ((ap.getParameterMask() & MASK_SENT) != 0) {
    355             int msgType = 0;
    356             if (fi.msgType == FilterInfo.TYPE_SMS) {
    357                 msgType = c.getInt(c.getColumnIndex(Sms.TYPE));
    358             } else if (fi.msgType == FilterInfo.TYPE_MMS) {
    359                 msgType = c.getInt(c.getColumnIndex(Mms.MESSAGE_BOX));
    360             }
    361             String sent = null;
    362             if (msgType == 2) {
    363                 sent = "yes";
    364             } else {
    365                 sent = "no";
    366             }
    367             if (D) Log.d(TAG, "setSent: " + sent);
    368             e.setSent(sent);
    369         }
    370     }
    371 
    372     private void setRead(BluetoothMapMessageListingElement e, Cursor c,
    373         FilterInfo fi, BluetoothMapAppParams ap) {
    374         int read = 0;
    375         if (fi.msgType == FilterInfo.TYPE_SMS) {
    376             read = c.getInt(c.getColumnIndex(Sms.READ));
    377         } else if (fi.msgType == FilterInfo.TYPE_MMS) {
    378             read = c.getInt(c.getColumnIndex(Mms.READ));
    379         }
    380         String setread = null;
    381         if (read == 1) {
    382             setread = "yes";
    383         } else {
    384             setread = "no";
    385         }
    386         if (D) Log.d(TAG, "setRead: " + setread);
    387         e.setRead(setread, ((ap.getParameterMask() & MASK_READ) != 0));
    388     }
    389 
    390     private void setPriority(BluetoothMapMessageListingElement e, Cursor c,
    391         FilterInfo fi, BluetoothMapAppParams ap) {
    392         if ((ap.getParameterMask() & MASK_PRIORITY) != 0) {
    393             String priority = "no";
    394             if (D) Log.d(TAG, "setPriority: " + priority);
    395             e.setPriority(priority);
    396         }
    397     }
    398 
    399     /**
    400      * For SMS we set the attachment size to 0, as all data will be text data, hence
    401      * attachments for SMS is not possible.
    402      * For MMS all data is actually attachments, hence we do set the attachment size to
    403      * the total message size. To provide a more accurate attachment size, one could
    404      * extract the length (in bytes) of the text parts.
    405      */
    406     private void setAttachmentSize(BluetoothMapMessageListingElement e, Cursor c,
    407         FilterInfo fi, BluetoothMapAppParams ap) {
    408         if ((ap.getParameterMask() & MASK_ATTACHMENT_SIZE) != 0) {
    409             int size = 0;
    410             if (fi.msgType == FilterInfo.TYPE_MMS) {
    411                 size = c.getInt(c.getColumnIndex(Mms.MESSAGE_SIZE));
    412             }
    413             if (D) Log.d(TAG, "setAttachmentSize: " + size);
    414             e.setAttachmentSize(size);
    415         }
    416     }
    417 
    418     private void setText(BluetoothMapMessageListingElement e, Cursor c,
    419         FilterInfo fi, BluetoothMapAppParams ap) {
    420         if ((ap.getParameterMask() & MASK_TEXT) != 0) {
    421             String hasText = "";
    422             if (fi.msgType == FilterInfo.TYPE_SMS) {
    423                 hasText = "yes";
    424             } else if (fi.msgType == FilterInfo.TYPE_MMS) {
    425                 int textOnly = c.getInt(c.getColumnIndex(Mms.TEXT_ONLY));
    426                 if (textOnly == 1) {
    427                     hasText = "yes";
    428                 } else {
    429                     long id = c.getLong(c.getColumnIndex(BaseColumns._ID));
    430                     String text = getTextPartsMms(id);
    431                     if (text != null && text.length() > 0) {
    432                         hasText = "yes";
    433                     } else {
    434                         hasText = "no";
    435                     }
    436                 }
    437             }
    438             if (D) Log.d(TAG, "setText: " + hasText);
    439             e.setText(hasText);
    440         }
    441     }
    442 
    443     private void setReceptionStatus(BluetoothMapMessageListingElement e, Cursor c,
    444         FilterInfo fi, BluetoothMapAppParams ap) {
    445         if ((ap.getParameterMask() & MASK_RECEPTION_STATUS) != 0) {
    446             String status = "complete";
    447             if (D) Log.d(TAG, "setReceptionStatus: " + status);
    448             e.setReceptionStatus(status);
    449         }
    450     }
    451 
    452     private void setSize(BluetoothMapMessageListingElement e, Cursor c,
    453         FilterInfo fi, BluetoothMapAppParams ap) {
    454         if ((ap.getParameterMask() & MASK_SIZE) != 0) {
    455             int size = 0;
    456             if (fi.msgType == FilterInfo.TYPE_SMS) {
    457                 String subject = c.getString(c.getColumnIndex(Sms.BODY));
    458                 size = subject.length();
    459             } else if (fi.msgType == FilterInfo.TYPE_MMS) {
    460                 size = c.getInt(c.getColumnIndex(Mms.MESSAGE_SIZE));
    461             }
    462             if (D) Log.d(TAG, "setSize: " + size);
    463             e.setSize(size);
    464         }
    465     }
    466 
    467     private void setType(BluetoothMapMessageListingElement e, Cursor c,
    468         FilterInfo fi, BluetoothMapAppParams ap) {
    469         if ((ap.getParameterMask() & MASK_TYPE) != 0) {
    470             TYPE type = null;
    471             if (fi.msgType == FilterInfo.TYPE_SMS) {
    472                 if (fi.phoneType == TelephonyManager.PHONE_TYPE_GSM) {
    473                     type = TYPE.SMS_GSM;
    474                 } else if (fi.phoneType == TelephonyManager.PHONE_TYPE_CDMA) {
    475                     type = TYPE.SMS_CDMA;
    476                 }
    477             } else if (fi.msgType == FilterInfo.TYPE_MMS) {
    478                 type = TYPE.MMS;
    479             }
    480             if (D) Log.d(TAG, "setType: " + type);
    481             e.setType(type);
    482         }
    483     }
    484 
    485     private void setRecipientAddressing(BluetoothMapMessageListingElement e, Cursor c,
    486         FilterInfo fi, BluetoothMapAppParams ap) {
    487         if ((ap.getParameterMask() & MASK_RECIPIENT_ADDRESSING) != 0) {
    488             String address = null;
    489             if (fi.msgType == FilterInfo.TYPE_SMS) {
    490                 int msgType = c.getInt(c.getColumnIndex(Sms.TYPE));
    491                 if (msgType == 1) {
    492                     address = fi.phoneNum;
    493                 } else {
    494                     address = c.getString(c.getColumnIndex(Sms.ADDRESS));
    495                 }
    496             } else if (fi.msgType == FilterInfo.TYPE_MMS) {
    497                 long id = c.getLong(c.getColumnIndex(BaseColumns._ID));
    498                 address = getAddressMms(mResolver, id, MMS_TO);
    499             }
    500             if (D) Log.d(TAG, "setRecipientAddressing: " + address);
    501             e.setRecipientAddressing(address);
    502         }
    503     }
    504 
    505     private void setRecipientName(BluetoothMapMessageListingElement e, Cursor c,
    506         FilterInfo fi, BluetoothMapAppParams ap) {
    507         if ((ap.getParameterMask() & MASK_RECIPIENT_NAME) != 0) {
    508             String name = null;
    509             if (fi.msgType == FilterInfo.TYPE_SMS) {
    510                 int msgType = c.getInt(c.getColumnIndex(Sms.TYPE));
    511                 if (msgType != 1) {
    512                     String phone = c.getString(c.getColumnIndex(Sms.ADDRESS));
    513                     name = getContactNameFromPhone(phone);
    514                 } else {
    515                     name = fi.phoneAlphaTag;
    516                 }
    517             } else if (fi.msgType == FilterInfo.TYPE_MMS) {
    518                 long id = c.getLong(c.getColumnIndex(BaseColumns._ID));
    519                 String phone = getAddressMms(mResolver, id, MMS_TO);
    520                 name = getContactNameFromPhone(phone);
    521             }
    522             if (D) Log.d(TAG, "setRecipientName: " + name);
    523             e.setRecipientName(name);
    524         }
    525     }
    526 
    527     private void setSenderAddressing(BluetoothMapMessageListingElement e, Cursor c,
    528         FilterInfo fi, BluetoothMapAppParams ap) {
    529         if ((ap.getParameterMask() & MASK_SENDER_ADDRESSING) != 0) {
    530             String address = null;
    531             if (fi.msgType == FilterInfo.TYPE_SMS) {
    532                 int msgType = c.getInt(c.getColumnIndex(Sms.TYPE));
    533                 if (msgType == 1) {
    534                     address = c.getString(c.getColumnIndex(Sms.ADDRESS));
    535                 } else {
    536                     address = fi.phoneNum;
    537                 }
    538             } else if (fi.msgType == FilterInfo.TYPE_MMS) {
    539                 long id = c.getLong(c.getColumnIndex(BaseColumns._ID));
    540                 address = getAddressMms(mResolver, id, MMS_FROM);
    541             }
    542             if (D) Log.d(TAG, "setSenderAddressing: " + address);
    543             e.setSenderAddressing(address);
    544         }
    545     }
    546 
    547     private void setSenderName(BluetoothMapMessageListingElement e, Cursor c,
    548         FilterInfo fi, BluetoothMapAppParams ap) {
    549         if ((ap.getParameterMask() & MASK_SENDER_NAME) != 0) {
    550             String name = null;
    551             if (fi.msgType == FilterInfo.TYPE_SMS) {
    552                 int msgType = c.getInt(c.getColumnIndex(Sms.TYPE));
    553                 if (msgType == 1) {
    554                     String phone = c.getString(c.getColumnIndex(Sms.ADDRESS));
    555                     name = getContactNameFromPhone(phone);
    556                 } else {
    557                     name = fi.phoneAlphaTag;
    558                 }
    559             } else if (fi.msgType == FilterInfo.TYPE_MMS) {
    560                 long id = c.getLong(c.getColumnIndex(BaseColumns._ID));
    561                 String phone = getAddressMms(mResolver, id, MMS_FROM);
    562                 name = getContactNameFromPhone(phone);
    563             }
    564             if (D) Log.d(TAG, "setSenderName: " + name);
    565             e.setSenderName(name);
    566         }
    567     }
    568 
    569     private void setDateTime(BluetoothMapMessageListingElement e, Cursor c,
    570         FilterInfo fi, BluetoothMapAppParams ap) {
    571         long date = 0;
    572 
    573         if (fi.msgType == FilterInfo.TYPE_SMS) {
    574             date = c.getLong(c.getColumnIndex(Sms.DATE));
    575         } else if (fi.msgType == FilterInfo.TYPE_MMS) {
    576             /* Use Mms.DATE for all messages. Although contract class states */
    577             /* Mms.DATE_SENT are for outgoing messages. But that is not working. */
    578             date = c.getLong(c.getColumnIndex(Mms.DATE)) * 1000L;
    579 
    580             /* int msgBox = c.getInt(c.getColumnIndex(Mms.MESSAGE_BOX)); */
    581             /* if (msgBox == Mms.MESSAGE_BOX_INBOX) { */
    582             /*     date = c.getLong(c.getColumnIndex(Mms.DATE)) * 1000L; */
    583             /* } else { */
    584             /*     date = c.getLong(c.getColumnIndex(Mms.DATE_SENT)) * 1000L; */
    585             /* } */
    586         }
    587         e.setDateTime(date);
    588     }
    589 
    590     private String getTextPartsMms(long id) {
    591         String text = "";
    592         String selection = new String("mid=" + id);
    593         String uriStr = String.format("content://mms/%d/part", id);
    594         Uri uriAddress = Uri.parse(uriStr);
    595         Cursor c = mResolver.query(uriAddress, null, selection,
    596             null, null);
    597 
    598         if (c != null && c.moveToFirst()) {
    599             do {
    600                 String ct = c.getString(c.getColumnIndex("ct"));
    601                 if (ct.equals("text/plain")) {
    602                     text += c.getString(c.getColumnIndex("text"));
    603                 }
    604             } while(c.moveToNext());
    605         }
    606         if (c != null) {
    607             c.close();
    608         }
    609         return text;
    610     }
    611 
    612     private void setSubject(BluetoothMapMessageListingElement e, Cursor c,
    613         FilterInfo fi, BluetoothMapAppParams ap) {
    614         String subject = "";
    615         int subLength = ap.getSubjectLength();
    616         if(subLength == BluetoothMapAppParams.INVALID_VALUE_PARAMETER)
    617             subLength = 256;
    618 
    619         if ((ap.getParameterMask() & MASK_SUBJECT) != 0) {
    620             if (fi.msgType == FilterInfo.TYPE_SMS) {
    621                 subject = c.getString(c.getColumnIndex(Sms.BODY));
    622             } else if (fi.msgType == FilterInfo.TYPE_MMS) {
    623                 subject = c.getString(c.getColumnIndex(Mms.SUBJECT));
    624                 if (subject == null || subject.length() == 0) {
    625                     /* Get subject from mms text body parts - if any exists */
    626                     long id = c.getLong(c.getColumnIndex(BaseColumns._ID));
    627                     subject = getTextPartsMms(id);
    628                 }
    629             }
    630             if (subject != null) {
    631                 subject = subject.substring(0, Math.min(subject.length(),
    632                     subLength));
    633             }
    634             if (D) Log.d(TAG, "setSubject: " + subject);
    635             e.setSubject(subject);
    636         }
    637     }
    638 
    639     private void setHandle(BluetoothMapMessageListingElement e, Cursor c,
    640         FilterInfo fi, BluetoothMapAppParams ap) {
    641         long handle = c.getLong(c.getColumnIndex(BaseColumns._ID));
    642         TYPE type = null;
    643         if (fi.msgType == FilterInfo.TYPE_SMS) {
    644             if (fi.phoneType == TelephonyManager.PHONE_TYPE_GSM) {
    645                 type = TYPE.SMS_GSM;
    646             } else if (fi.phoneType == TelephonyManager.PHONE_TYPE_CDMA) {
    647                 type = TYPE.SMS_CDMA;
    648             }
    649         } else if (fi.msgType == FilterInfo.TYPE_MMS) {
    650             type = TYPE.MMS;
    651         }
    652         if (D) Log.d(TAG, "setHandle: " + handle + " - Type: " + type.name());
    653         e.setHandle(handle, type);
    654     }
    655 
    656     private BluetoothMapMessageListingElement element(Cursor c, FilterInfo fi,
    657         BluetoothMapAppParams ap) {
    658         BluetoothMapMessageListingElement e = new BluetoothMapMessageListingElement();
    659 
    660         setHandle(e, c, fi, ap);
    661         setSubject(e, c, fi, ap);
    662         setDateTime(e, c, fi, ap);
    663         setSenderName(e, c, fi, ap);
    664         setSenderAddressing(e, c, fi, ap);
    665         setRecipientName(e, c, fi, ap);
    666         setRecipientAddressing(e, c, fi, ap);
    667         setType(e, c, fi, ap);
    668         setSize(e, c, fi, ap);
    669         setReceptionStatus(e, c, fi, ap);
    670         setText(e, c, fi, ap);
    671         setAttachmentSize(e, c, fi, ap);
    672         setPriority(e, c, fi, ap);
    673         setRead(e, c, fi, ap);
    674         setSent(e, c, fi, ap);
    675         setProtected(e, c, fi, ap);
    676         return e;
    677     }
    678 
    679     private String getContactNameFromPhone(String phone) {
    680         String name = null;
    681 
    682         Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI,
    683             Uri.encode(phone));
    684 
    685         String[] projection = {Contacts._ID, Contacts.DISPLAY_NAME};
    686         String selection = Contacts.IN_VISIBLE_GROUP + "=1";
    687         String orderBy = Contacts.DISPLAY_NAME + " ASC";
    688 
    689         Cursor c = mResolver.query(uri, projection, selection, null, orderBy);
    690 
    691         if (c != null && c.getCount() >= 1) {
    692             c.moveToFirst();
    693             name = c.getString(c.getColumnIndex(Contacts.DISPLAY_NAME));
    694         }
    695 
    696         c.close();
    697         return name;
    698     }
    699 
    700     static public String getAddressMms(ContentResolver r, long id, int type) {
    701         String selection = new String("msg_id=" + id + " AND type=" + type);
    702         String uriStr = String.format("content://mms/%d/addr", id);
    703         Uri uriAddress = Uri.parse(uriStr);
    704         String addr = null;
    705         Cursor c = r.query(uriAddress, null, selection, null, null);
    706 
    707         if (c != null && c.moveToFirst()) {
    708             addr = c.getString(c.getColumnIndex("address"));
    709         }
    710 
    711         if (c != null) {
    712             c.close();
    713         }
    714         return addr;
    715     }
    716 
    717     private boolean matchRecipientMms(Cursor c, FilterInfo fi, String recip) {
    718         boolean res;
    719         long id = c.getLong(c.getColumnIndex(BaseColumns._ID));
    720         String phone = getAddressMms(mResolver, id, MMS_TO);
    721         if (phone != null && phone.length() > 0) {
    722             if (phone.matches(recip)) {
    723                 if (D) Log.d(TAG, "match recipient phone = " + phone);
    724                 res = true;
    725             } else {
    726                 String name = getContactNameFromPhone(phone);
    727                 if (name != null && name.length() > 0 && name.matches(recip)) {
    728                     if (D) Log.d(TAG, "match recipient name = " + name);
    729                     res = true;
    730                 } else {
    731                     res = false;
    732                 }
    733             }
    734         } else {
    735             res = false;
    736         }
    737         return res;
    738     }
    739 
    740     private boolean matchRecipientSms(Cursor c, FilterInfo fi, String recip) {
    741         boolean res;
    742         int msgType = c.getInt(c.getColumnIndex(Sms.TYPE));
    743         if (msgType == 1) {
    744             String phone = fi.phoneNum;
    745             String name = fi.phoneAlphaTag;
    746             if (phone != null && phone.length() > 0 && phone.matches(recip)) {
    747                 if (D) Log.d(TAG, "match recipient phone = " + phone);
    748                 res = true;
    749             } else if (name != null && name.length() > 0 && name.matches(recip)) {
    750                 if (D) Log.d(TAG, "match recipient name = " + name);
    751                 res = true;
    752             } else {
    753                 res = false;
    754             }
    755         }
    756         else {
    757             String phone = c.getString(c.getColumnIndex(Sms.ADDRESS));
    758             if (phone != null && phone.length() > 0) {
    759                 if (phone.matches(recip)) {
    760                     if (D) Log.d(TAG, "match recipient phone = " + phone);
    761                     res = true;
    762                 } else {
    763                     String name = getContactNameFromPhone(phone);
    764                     if (name != null && name.length() > 0 && name.matches(recip)) {
    765                         if (D) Log.d(TAG, "match recipient name = " + name);
    766                         res = true;
    767                     } else {
    768                         res = false;
    769                     }
    770                 }
    771             } else {
    772                 res = false;
    773             }
    774         }
    775         return res;
    776     }
    777 
    778     private boolean matchRecipient(Cursor c, FilterInfo fi, BluetoothMapAppParams ap) {
    779         boolean res;
    780         String recip = ap.getFilterRecipient();
    781         if (recip != null && recip.length() > 0) {
    782             recip = recip.replace("*", ".*");
    783             recip = ".*" + recip + ".*";
    784             if (fi.msgType == FilterInfo.TYPE_SMS) {
    785                 res = matchRecipientSms(c, fi, recip);
    786             } else if (fi.msgType == FilterInfo.TYPE_MMS) {
    787                 res = matchRecipientMms(c, fi, recip);
    788             } else {
    789                 if (D) Log.d(TAG, "Unknown msg type: " + fi.msgType);
    790                 res = false;
    791             }
    792         } else {
    793             res = true;
    794         }
    795         return res;
    796     }
    797 
    798     private boolean matchOriginatorMms(Cursor c, FilterInfo fi, String orig) {
    799         boolean res;
    800         long id = c.getLong(c.getColumnIndex(BaseColumns._ID));
    801         String phone = getAddressMms(mResolver, id, MMS_FROM);
    802         if (phone != null && phone.length() > 0) {
    803             if (phone.matches(orig)) {
    804                 if (D) Log.d(TAG, "match originator phone = " + phone);
    805                 res = true;
    806             } else {
    807                 String name = getContactNameFromPhone(phone);
    808                 if (name != null && name.length() > 0 && name.matches(orig)) {
    809                     if (D) Log.d(TAG, "match originator name = " + name);
    810                     res = true;
    811                 } else {
    812                     res = false;
    813                 }
    814             }
    815         } else {
    816             res = false;
    817         }
    818         return res;
    819     }
    820 
    821     private boolean matchOriginatorSms(Cursor c, FilterInfo fi, String orig) {
    822         boolean res;
    823         int msgType = c.getInt(c.getColumnIndex(Sms.TYPE));
    824         if (msgType == 1) {
    825             String phone = c.getString(c.getColumnIndex(Sms.ADDRESS));
    826             if (phone !=null && phone.length() > 0) {
    827                 if (phone.matches(orig)) {
    828                     if (D) Log.d(TAG, "match originator phone = " + phone);
    829                     res = true;
    830                 } else {
    831                     String name = getContactNameFromPhone(phone);
    832                     if (name != null && name.length() > 0 && name.matches(orig)) {
    833                         if (D) Log.d(TAG, "match originator name = " + name);
    834                         res = true;
    835                     } else {
    836                         res = false;
    837                     }
    838                 }
    839             } else {
    840                 res = false;
    841             }
    842         }
    843         else {
    844             String phone = fi.phoneNum;
    845             String name = fi.phoneAlphaTag;
    846             if (phone != null && phone.length() > 0 && phone.matches(orig)) {
    847                 if (D) Log.d(TAG, "match originator phone = " + phone);
    848                 res = true;
    849             } else if (name != null && name.length() > 0 && name.matches(orig)) {
    850                 if (D) Log.d(TAG, "match originator name = " + name);
    851                 res = true;
    852             } else {
    853                 res = false;
    854             }
    855         }
    856         return res;
    857     }
    858 
    859    private boolean matchOriginator(Cursor c, FilterInfo fi, BluetoothMapAppParams ap) {
    860         boolean res;
    861         String orig = ap.getFilterOriginator();
    862         if (orig != null && orig.length() > 0) {
    863             orig = orig.replace("*", ".*");
    864             orig = ".*" + orig + ".*";
    865             if (fi.msgType == FilterInfo.TYPE_SMS) {
    866                 res = matchOriginatorSms(c, fi, orig);
    867             } else if (fi.msgType == FilterInfo.TYPE_MMS) {
    868                 res = matchOriginatorMms(c, fi, orig);
    869             } else {
    870                 Log.d(TAG, "Unknown msg type: " + fi.msgType);
    871                 res = false;
    872             }
    873         } else {
    874             res = true;
    875         }
    876         return res;
    877     }
    878 
    879     private boolean matchAddresses(Cursor c, FilterInfo fi, BluetoothMapAppParams ap) {
    880         if (matchOriginator(c, fi, ap) && matchRecipient(c, fi, ap)) {
    881             return true;
    882         } else {
    883             return false;
    884         }
    885     }
    886 
    887     private String setWhereFilterFolderTypeSms(String folder) {
    888         String where = "";
    889         if ("inbox".equalsIgnoreCase(folder)) {
    890             where = "type = 1 AND thread_id <> -1";
    891         }
    892         else if ("outbox".equalsIgnoreCase(folder)) {
    893             where = "(type = 4 OR type = 5 OR type = 6) AND thread_id <> -1";
    894         }
    895         else if ("sent".equalsIgnoreCase(folder)) {
    896             where = "type = 2 AND thread_id <> -1";
    897         }
    898         else if ("draft".equalsIgnoreCase(folder)) {
    899             where = "type = 3 AND thread_id <> -1";
    900         }
    901         else if ("deleted".equalsIgnoreCase(folder)) {
    902             where = "thread_id = -1";
    903         }
    904 
    905         return where;
    906     }
    907 
    908     private String setWhereFilterFolderTypeMms(String folder) {
    909         String where = "";
    910         if ("inbox".equalsIgnoreCase(folder)) {
    911             where = "msg_box = 1 AND thread_id <> -1";
    912         }
    913         else if ("outbox".equalsIgnoreCase(folder)) {
    914             where = "msg_box = 4 AND thread_id <> -1";
    915         }
    916         else if ("sent".equalsIgnoreCase(folder)) {
    917             where = "msg_box = 2 AND thread_id <> -1";
    918         }
    919         else if ("draft".equalsIgnoreCase(folder)) {
    920             where = "msg_box = 3 AND thread_id <> -1";
    921         }
    922         else if ("deleted".equalsIgnoreCase(folder)) {
    923             where = "thread_id = -1";
    924         }
    925 
    926         return where;
    927     }
    928 
    929     private String setWhereFilterFolderType(String folder, FilterInfo fi) {
    930         String where = "";
    931         if (fi.msgType == FilterInfo.TYPE_SMS) {
    932             where = setWhereFilterFolderTypeSms(folder);
    933         } else if (fi.msgType == FilterInfo.TYPE_MMS) {
    934             where = setWhereFilterFolderTypeMms(folder);
    935         }
    936 
    937         return where;
    938     }
    939 
    940     private String setWhereFilterReadStatus(BluetoothMapAppParams ap) {
    941         String where = "";
    942         if (ap.getFilterReadStatus() != -1) {
    943             if ((ap.getFilterReadStatus() & 0x01) != 0) {
    944                 where = " AND read=0 ";
    945             }
    946 
    947             if ((ap.getFilterReadStatus() & 0x02) != 0) {
    948                 where = " AND read=1 ";
    949             }
    950         }
    951 
    952         return where;
    953     }
    954 
    955     private String setWhereFilterPeriod(BluetoothMapAppParams ap, FilterInfo fi) {
    956         String where = "";
    957         if ((ap.getFilterPeriodBegin() != -1)) {
    958             if (fi.msgType == FilterInfo.TYPE_SMS) {
    959             where = " AND date >= " + ap.getFilterPeriodBegin();
    960             } else if (fi.msgType == FilterInfo.TYPE_MMS) {
    961                 where = " AND date >= " + (ap.getFilterPeriodBegin() / 1000L);
    962             }
    963         }
    964 
    965         if ((ap.getFilterPeriodEnd() != -1)) {
    966             if (fi.msgType == FilterInfo.TYPE_SMS) {
    967             where += " AND date < " + ap.getFilterPeriodEnd();
    968             } else if (fi.msgType == FilterInfo.TYPE_MMS) {
    969                 where += " AND date < " + (ap.getFilterPeriodEnd() / 1000L);
    970             }
    971         }
    972 
    973         return where;
    974     }
    975 
    976     private String setWhereFilterPhones(String str) {
    977         String where = "";
    978         str = str.replace("*", "%");
    979 
    980         Cursor c = mResolver.query(ContactsContract.Contacts.CONTENT_URI, null,
    981             ContactsContract.Contacts.DISPLAY_NAME + " like ?",
    982             new String[]{str},
    983             ContactsContract.Contacts.DISPLAY_NAME + " ASC");
    984 
    985         while (c != null && c.moveToNext()) {
    986             String contactId = c.getString(c.getColumnIndex(ContactsContract.Contacts._ID));
    987 
    988             Cursor p = mResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null,
    989                 ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?",
    990                 new String[]{contactId},
    991                 null);
    992 
    993             while (p != null && p.moveToNext()) {
    994                 String number = p.getString(
    995                     p.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
    996 
    997                 where += " address = " + "'" + number + "'";
    998                 if (!p.isLast()) {
    999                     where += " OR ";
   1000                 }
   1001             }
   1002             if (!c.isLast()) {
   1003                 where += " OR ";
   1004             }
   1005             p.close();
   1006         }
   1007         c.close();
   1008 
   1009         if (str != null && str.length() > 0) {
   1010             if (where.length() > 0) {
   1011                 where += " OR ";
   1012             }
   1013             where += " address like " + "'" + str + "'";
   1014         }
   1015 
   1016         return where;
   1017     }
   1018 
   1019     private String setWhereFilterOriginator(BluetoothMapAppParams ap,
   1020         FilterInfo fi) {
   1021         String where = "";
   1022         String orig = ap.getFilterOriginator();
   1023 
   1024         if (orig != null && orig.length() > 0) {
   1025             String phones = setWhereFilterPhones(orig);
   1026 
   1027             if (phones.length() > 0) {
   1028                 where = " AND ((type <> 1) OR ( " + phones + " ))";
   1029             } else {
   1030                 where = " AND (type <> 1)";
   1031             }
   1032 
   1033             orig = orig.replace("*", ".*");
   1034             orig = ".*" + orig + ".*";
   1035 
   1036             boolean localPhoneMatchOrig = false;
   1037             if (fi.phoneNum != null && fi.phoneNum.length() > 0
   1038                 && fi.phoneNum.matches(orig)) {
   1039                 localPhoneMatchOrig = true;
   1040             }
   1041 
   1042             if (fi.phoneAlphaTag != null && fi.phoneAlphaTag.length() > 0
   1043                 && fi.phoneAlphaTag.matches(orig)) {
   1044                 localPhoneMatchOrig = true;
   1045             }
   1046 
   1047             if (!localPhoneMatchOrig) {
   1048                 where += " AND (type = 1)";
   1049             }
   1050         }
   1051 
   1052         return where;
   1053     }
   1054 
   1055     private String setWhereFilterRecipient(BluetoothMapAppParams ap,
   1056         FilterInfo fi) {
   1057         String where = "";
   1058         String recip = ap.getFilterRecipient();
   1059 
   1060         if (recip != null && recip.length() > 0) {
   1061             String phones = setWhereFilterPhones(recip);
   1062 
   1063             if (phones.length() > 0) {
   1064                 where = " AND ((type = 1) OR ( " + phones + " ))";
   1065             } else {
   1066                 where = " AND (type = 1)";
   1067             }
   1068 
   1069             recip = recip.replace("*", ".*");
   1070             recip = ".*" + recip + ".*";
   1071 
   1072             boolean localPhoneMatchOrig = false;
   1073             if (fi.phoneNum != null && fi.phoneNum.length() > 0
   1074                 && fi.phoneNum.matches(recip)) {
   1075                 localPhoneMatchOrig = true;
   1076             }
   1077 
   1078             if (fi.phoneAlphaTag != null && fi.phoneAlphaTag.length() > 0
   1079                 && fi.phoneAlphaTag.matches(recip)) {
   1080                 localPhoneMatchOrig = true;
   1081             }
   1082 
   1083             if (!localPhoneMatchOrig) {
   1084                 where += " AND (type <> 1)";
   1085             }
   1086         }
   1087 
   1088         return where;
   1089     }
   1090 
   1091     private String setWhereFilter(String folder, FilterInfo fi, BluetoothMapAppParams ap) {
   1092         String where = "";
   1093 
   1094         where += setWhereFilterFolderType(folder, fi);
   1095         where += setWhereFilterReadStatus(ap);
   1096         where += setWhereFilterPeriod(ap, fi);
   1097         /* where += setWhereFilterOriginator(ap, fi); */
   1098         /* where += setWhereFilterRecipient(ap, fi); */
   1099 
   1100         if (D) Log.d(TAG, "where: " + where);
   1101 
   1102         return where;
   1103     }
   1104 
   1105     private boolean smsSelected(FilterInfo fi, BluetoothMapAppParams ap) {
   1106         int msgType = ap.getFilterMessageType();
   1107         int phoneType = fi.phoneType;
   1108 
   1109         if (msgType == -1)
   1110             return true;
   1111         if ((msgType & 0x03) == 0)
   1112             return true;
   1113 
   1114         if (((msgType & 0x01) == 0) && (phoneType == TelephonyManager.PHONE_TYPE_GSM))
   1115             return true;
   1116 
   1117         if (((msgType & 0x02) == 0) && (phoneType == TelephonyManager.PHONE_TYPE_CDMA))
   1118             return true;
   1119 
   1120         return false;
   1121     }
   1122 
   1123     private boolean mmsSelected(FilterInfo fi, BluetoothMapAppParams ap) {
   1124         int msgType = ap.getFilterMessageType();
   1125 
   1126         if (msgType == -1)
   1127             return true;
   1128 
   1129         if ((msgType & 0x08) == 0)
   1130             return true;
   1131 
   1132         return false;
   1133     }
   1134 
   1135     private void setFilterInfo(FilterInfo fi) {
   1136         TelephonyManager tm = (TelephonyManager)mContext.getSystemService(Context.TELEPHONY_SERVICE);
   1137         if (tm != null) {
   1138             fi.phoneType = tm.getPhoneType();
   1139             fi.phoneNum = tm.getLine1Number();
   1140             fi.phoneAlphaTag = tm.getLine1AlphaTag();
   1141             if (D) Log.d(TAG, "phone type = " + fi.phoneType +
   1142                 " phone num = " + fi.phoneNum +
   1143                 " phone alpha tag = " + fi.phoneAlphaTag);
   1144         }
   1145     }
   1146 
   1147     public BluetoothMapMessageListing msgListing(String folder, BluetoothMapAppParams ap) {
   1148         Log.d(TAG, "msgListing: folder = " + folder);
   1149         BluetoothMapMessageListing bmList = new BluetoothMapMessageListing();
   1150         BluetoothMapMessageListingElement e = null;
   1151 
   1152         /* Cache some info used throughout filtering */
   1153         FilterInfo fi = new FilterInfo();
   1154         setFilterInfo(fi);
   1155 
   1156         if (smsSelected(fi, ap)) {
   1157             fi.msgType = FilterInfo.TYPE_SMS;
   1158 
   1159             String where = setWhereFilter(folder, fi, ap);
   1160 
   1161             Cursor c = mResolver.query(Sms.CONTENT_URI,
   1162                 SMS_PROJECTION, where, null, "date DESC");
   1163 
   1164             if (c != null) {
   1165                 while (c.moveToNext()) {
   1166                     if (matchAddresses(c, fi, ap)) {
   1167                         printSms(c);
   1168                         e = element(c, fi, ap);
   1169                         bmList.add(e);
   1170                     }
   1171                 }
   1172                 c.close();
   1173             }
   1174         }
   1175 
   1176         if (mmsSelected(fi, ap)) {
   1177             fi.msgType = FilterInfo.TYPE_MMS;
   1178 
   1179             String where = setWhereFilter(folder, fi, ap);
   1180 
   1181             Cursor c = mResolver.query(Mms.CONTENT_URI,
   1182                 MMS_PROJECTION, where, null, "date DESC");
   1183 
   1184             if (c != null) {
   1185                 int cnt = 0;
   1186                 while (c.moveToNext()) {
   1187                     if (matchAddresses(c, fi, ap)) {
   1188                         printMms(c);
   1189                         e = element(c, fi, ap);
   1190                         bmList.add(e);
   1191                     }
   1192                 }
   1193                 c.close();
   1194             }
   1195         }
   1196 
   1197         /* Enable this if post sorting and segmenting needed */
   1198         bmList.sort();
   1199         bmList.segment(ap.getMaxListCount(), ap.getStartOffset());
   1200 
   1201         return bmList;
   1202     }
   1203 
   1204     public int msgListingSize(String folder, BluetoothMapAppParams ap) {
   1205         if (D) Log.d(TAG, "msgListingSize: folder = " + folder);
   1206         int cnt = 0;
   1207 
   1208         /* Cache some info used throughout filtering */
   1209         FilterInfo fi = new FilterInfo();
   1210         setFilterInfo(fi);
   1211 
   1212         if (smsSelected(fi, ap)) {
   1213             fi.msgType = FilterInfo.TYPE_SMS;
   1214             String where = setWhereFilter(folder, fi, ap);
   1215             Cursor c = mResolver.query(Sms.CONTENT_URI,
   1216                 SMS_PROJECTION, where, null, "date DESC");
   1217 
   1218             if (c != null) {
   1219                 cnt = c.getCount();
   1220                 c.close();
   1221             }
   1222         }
   1223 
   1224         if (mmsSelected(fi, ap)) {
   1225             fi.msgType = FilterInfo.TYPE_MMS;
   1226             String where = setWhereFilter(folder, fi, ap);
   1227             Cursor c = mResolver.query(Mms.CONTENT_URI,
   1228                 MMS_PROJECTION, where, null, "date DESC");
   1229 
   1230             if (c != null) {
   1231                 cnt += c.getCount();
   1232                 c.close();
   1233             }
   1234         }
   1235 
   1236         if (D) Log.d(TAG, "msgListingSize: size = " + cnt);
   1237         return cnt;
   1238     }
   1239     /**
   1240      * Return true if there are unread messages in the requested list of messages
   1241      * @param folder folder where the message listing should come from
   1242      * @param ap application parameter object
   1243      * @return true if unread messages are in the list, else false
   1244      */
   1245     public boolean msgListingHasUnread(String folder, BluetoothMapAppParams ap) {
   1246         if (D) Log.d(TAG, "msgListingHasUnread: folder = " + folder);
   1247         int cnt = 0;
   1248 
   1249         /* Cache some info used throughout filtering */
   1250         FilterInfo fi = new FilterInfo();
   1251         setFilterInfo(fi);
   1252 
   1253         if (smsSelected(fi, ap)) {
   1254             fi.msgType = FilterInfo.TYPE_SMS;
   1255             String where = setWhereFilterFolderType(folder, fi);
   1256             where += " AND read=0 ";
   1257             where += setWhereFilterPeriod(ap, fi);
   1258             Cursor c = mResolver.query(Sms.CONTENT_URI,
   1259                 SMS_PROJECTION, where, null, "date DESC");
   1260 
   1261             if (c != null) {
   1262                 cnt = c.getCount();
   1263                 c.close();
   1264             }
   1265         }
   1266 
   1267         if (mmsSelected(fi, ap)) {
   1268             fi.msgType = FilterInfo.TYPE_MMS;
   1269             String where = setWhereFilterFolderType(folder, fi);
   1270             where += " AND read=0 ";
   1271             where += setWhereFilterPeriod(ap, fi);
   1272             Cursor c = mResolver.query(Mms.CONTENT_URI,
   1273                 MMS_PROJECTION, where, null, "date DESC");
   1274 
   1275             if (c != null) {
   1276                 cnt += c.getCount();
   1277                 c.close();
   1278             }
   1279         }
   1280 
   1281         if (D) Log.d(TAG, "msgListingHasUnread: numUnread = " + cnt);
   1282         return (cnt>0)?true:false;
   1283     }
   1284 
   1285     /**
   1286      * Get the folder name of an SMS message or MMS message.
   1287      * @param c the cursor pointing at the message
   1288      * @return the folder name.
   1289      */
   1290     private String getFolderName(int type, int threadId) {
   1291 
   1292         if(threadId == -1)
   1293             return "deleted";
   1294 
   1295         switch(type) {
   1296         case 1:
   1297             return "inbox";
   1298         case 2:
   1299             return "sent";
   1300         case 3:
   1301             return "draft";
   1302         case 4: // Just name outbox, failed and queued "outbox"
   1303         case 5:
   1304         case 6:
   1305             return "outbox";
   1306         }
   1307         return "";
   1308     }
   1309 
   1310     public byte[] getMessage(String handle, BluetoothMapAppParams appParams) throws UnsupportedEncodingException{
   1311         TYPE type = BluetoothMapUtils.getMsgTypeFromHandle(handle);
   1312         long id = BluetoothMapUtils.getCpHandle(handle);
   1313         switch(type) {
   1314         case SMS_GSM:
   1315         case SMS_CDMA:
   1316             return getSmsMessage(id, appParams.getCharset());
   1317         case MMS:
   1318             return getMmsMessage(id, appParams);
   1319         case EMAIL:
   1320             throw new IllegalArgumentException("Email not implemented - invalid message handle.");
   1321         }
   1322         throw new IllegalArgumentException("Invalid message handle.");
   1323     }
   1324 
   1325     private void setVCardFromPhoneNumber(BluetoothMapbMessage message, String phone, boolean incoming) {
   1326         String contactId = null, contactName = null;
   1327         String[] phoneNumbers = null;
   1328         String[] emailAddresses = null;
   1329         Cursor p;
   1330 
   1331         Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI,
   1332                 Uri.encode(phone));
   1333 
   1334         String[] projection = {Contacts._ID, Contacts.DISPLAY_NAME};
   1335         String selection = Contacts.IN_VISIBLE_GROUP + "=1";
   1336         String orderBy = Contacts._ID + " ASC";
   1337 
   1338         // Get the contact _ID and name
   1339         p = mResolver.query(uri, projection, selection, null, orderBy);
   1340         if (p != null && p.getCount() >= 1) {
   1341             p.moveToFirst();
   1342             contactId = p.getString(p.getColumnIndex(Contacts._ID));
   1343             contactName = p.getString(p.getColumnIndex(Contacts.DISPLAY_NAME));
   1344         }
   1345         p.close();
   1346 
   1347         // Bail out if we are unable to find a contact, based on the phone number
   1348         if(contactId == null) {
   1349             phoneNumbers = new String[1];
   1350             phoneNumbers[0] = phone;
   1351         }
   1352         else {
   1353             // Fetch all contact phone numbers
   1354             p = mResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null,
   1355                 ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?",
   1356                 new String[]{contactId},
   1357                 null);
   1358             if(p != null) {
   1359                 int i = 0;
   1360                 phoneNumbers = new String[p.getCount()];
   1361                 while (p != null && p.moveToNext()) {
   1362                     String number = p.getString(
   1363                         p.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
   1364                     phoneNumbers[i++] = number;
   1365                 }
   1366                 p.close();
   1367             }
   1368 
   1369             // Fetch contact e-mail addresses
   1370             p = mResolver.query(ContactsContract.CommonDataKinds.Email.CONTENT_URI, null,
   1371                     ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?",
   1372                     new String[]{contactId},
   1373                     null);
   1374             if(p != null) {
   1375                 int i = 0;
   1376                 emailAddresses = new String[p.getCount()];
   1377                 while (p != null && p.moveToNext()) {
   1378                     String emailAddress = p.getString(
   1379                         p.getColumnIndex(ContactsContract.CommonDataKinds.Email.ADDRESS));
   1380                     emailAddresses[i++] = emailAddress;
   1381                 }
   1382                 p.close();
   1383             }
   1384         }
   1385         if(incoming == true)
   1386             message.addOriginator(contactName, contactName, phoneNumbers, emailAddresses); // Use version 3.0 as we only have a formatted name
   1387         else
   1388             message.addRecipient(contactName, contactName, phoneNumbers, emailAddresses); // Use version 3.0 as we only have a formatted name
   1389     }
   1390 
   1391     public static final int MAP_MESSAGE_CHARSET_NATIVE = 0;
   1392     public static final int MAP_MESSAGE_CHARSET_UTF8 = 1;
   1393 
   1394     public byte[] getSmsMessage(long id, int charset) throws UnsupportedEncodingException{
   1395         int type, threadId;
   1396         long time = -1;
   1397         String msgBody;
   1398         BluetoothMapbMessageSms message = new BluetoothMapbMessageSms();
   1399         TelephonyManager tm = (TelephonyManager)mContext.getSystemService(Context.TELEPHONY_SERVICE);
   1400         Cursor c = mResolver.query(Sms.CONTENT_URI, SMS_PROJECTION, "_ID = " + id, null, null);
   1401 
   1402         if(c != null && c.moveToFirst())
   1403         {
   1404 
   1405             if(V) Log.d(TAG,"c.count: " + c.getCount());
   1406 
   1407             if (tm.getPhoneType() == TelephonyManager.PHONE_TYPE_GSM) {
   1408                 message.setType(TYPE.SMS_GSM);
   1409             } else if (tm.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA) {
   1410                 message.setType(TYPE.SMS_CDMA);
   1411             }
   1412 
   1413             String read = c.getString(c.getColumnIndex(Sms.READ));
   1414             if (read.equalsIgnoreCase("1"))
   1415                 message.setStatus(true);
   1416             else
   1417                 message.setStatus(false);
   1418 
   1419             type = c.getInt(c.getColumnIndex(Sms.TYPE));
   1420             threadId = c.getInt(c.getColumnIndex(Sms.THREAD_ID));
   1421             message.setFolder(getFolderName(type, threadId));
   1422 
   1423             msgBody = c.getString(c.getColumnIndex(Sms.BODY));
   1424 
   1425             String phone = c.getString(c.getColumnIndex(Sms.ADDRESS));
   1426 
   1427             time = c.getLong(c.getColumnIndex(Sms.DATE));
   1428             if(type == 1) // Inbox message needs to set the vCard as originator
   1429                 setVCardFromPhoneNumber(message, phone, true);
   1430             else          // Other messages sets the vCard as the recipient
   1431                 setVCardFromPhoneNumber(message, phone, false);
   1432 
   1433             if(charset == MAP_MESSAGE_CHARSET_NATIVE) {
   1434                 if(type == 1) //Inbox
   1435                     message.setSmsBodyPdus(BluetoothMapSmsPdu.getDeliverPdus(msgBody, phone, time));
   1436                 else
   1437                     message.setSmsBodyPdus(BluetoothMapSmsPdu.getSubmitPdus(msgBody, phone));
   1438             } else /*if (charset == MAP_MESSAGE_CHARSET_UTF8)*/ {
   1439                 message.setSmsBody(msgBody);
   1440             }
   1441 
   1442             c.close();
   1443 
   1444             return message.encode();
   1445         }
   1446         throw new IllegalArgumentException("SMS handle not found");
   1447     }
   1448 
   1449     private void extractMmsAddresses(long id, BluetoothMapbMessageMmsEmail message) {
   1450         final String[] projection = null;
   1451         String selection = new String("msg_id=" + id);
   1452         String uriStr = String.format("content://mms/%d/addr", id);
   1453         Uri uriAddress = Uri.parse(uriStr);
   1454         Cursor c = mResolver.query(
   1455             uriAddress,
   1456             projection,
   1457             selection,
   1458             null, null);
   1459         /* TODO: Change the setVCard...() to return the vCard, and use the name in message.addXxx() */
   1460         if (c.moveToFirst()) {
   1461             do {
   1462                 String address = c.getString(c.getColumnIndex("address"));
   1463                 Integer type = c.getInt(c.getColumnIndex("type"));
   1464                 switch(type) {
   1465                 case MMS_FROM:
   1466                     setVCardFromPhoneNumber(message, address, true);
   1467                     message.addFrom(null, address);
   1468                     break;
   1469                 case MMS_TO:
   1470                     setVCardFromPhoneNumber(message, address, false);
   1471                     message.addTo(null, address);
   1472                     break;
   1473                 case MMS_CC:
   1474                     setVCardFromPhoneNumber(message, address, false);
   1475                     message.addCc(null, address);
   1476                     break;
   1477                 case MMS_BCC:
   1478                     setVCardFromPhoneNumber(message, address, false);
   1479                     message.addBcc(null, address);
   1480                 default:
   1481                     break;
   1482                 }
   1483             } while(c.moveToNext());
   1484         }
   1485     }
   1486 
   1487     /**
   1488      * Read out a mms data part and return the data in a byte array.
   1489      * @param partid the content provider id of the mms.
   1490      * @return
   1491      */
   1492     private byte[] readMmsDataPart(long partid) {
   1493         String uriStr = String.format("content://mms/part/%d", partid);
   1494         Uri uriAddress = Uri.parse(uriStr);
   1495         InputStream is = null;
   1496         ByteArrayOutputStream os = new ByteArrayOutputStream();
   1497         int bufferSize = 8192;
   1498         byte[] buffer = new byte[bufferSize];
   1499         byte[] retVal = null;
   1500 
   1501         try {
   1502             is = mResolver.openInputStream(uriAddress);
   1503             int len = 0;
   1504             while ((len = is.read(buffer)) != -1) {
   1505               os.write(buffer, 0, len); // We need to specify the len, as it can be != bufferSize
   1506             }
   1507             retVal = os.toByteArray();
   1508         } catch (IOException e) {
   1509             // do nothing for now
   1510             Log.w(TAG,"Error reading part data",e);
   1511         } finally {
   1512             try {
   1513                 os.close();
   1514                 is.close();
   1515             } catch (IOException e) {
   1516             }
   1517         }
   1518         return retVal;
   1519     }
   1520 
   1521     /**
   1522      * Read out the mms parts and update the bMessage object provided i {@linkplain message}
   1523      * @param id the content provider ID of the message
   1524      * @param message the bMessage object to add the information to
   1525      */
   1526     private void extractMmsParts(long id, BluetoothMapbMessageMmsEmail message)
   1527     {
   1528         /* TODO: If the attachment appParam is set to "no", only add the text parts.
   1529          * (content type contains "text" - case insensitive) */
   1530         final String[] projection = null;
   1531         String selection = new String("mid=" + id);
   1532         String uriStr = String.format("content://mms/%d/part", id);
   1533         Uri uriAddress = Uri.parse(uriStr);
   1534         BluetoothMapbMessageMmsEmail.MimePart part;
   1535         Cursor c = mResolver.query(
   1536             uriAddress,
   1537             projection,
   1538             selection,
   1539             null, null);
   1540 
   1541         if (c.moveToFirst()) {
   1542             do {
   1543                 Long partId = c.getLong(c.getColumnIndex(BaseColumns._ID));
   1544                 String contentType = c.getString(c.getColumnIndex("ct"));
   1545                 String name = c.getString(c.getColumnIndex("name"));
   1546                 String charset = c.getString(c.getColumnIndex("chset"));
   1547                 String filename = c.getString(c.getColumnIndex("fn"));
   1548                 String text = c.getString(c.getColumnIndex("text"));
   1549                 Integer fd = c.getInt(c.getColumnIndex("_data"));
   1550                 String cid = c.getString(c.getColumnIndex("cid"));
   1551                 String cl = c.getString(c.getColumnIndex("cl"));
   1552                 String cdisp = c.getString(c.getColumnIndex("cd"));
   1553 
   1554                 if(D) Log.d(TAG, "     _id : " + partId +
   1555                         "\n     ct : " + contentType +
   1556                         "\n     partname : " + name +
   1557                         "\n     charset : " + charset +
   1558                         "\n     filename : " + filename +
   1559                         "\n     text : " + text +
   1560                         "\n     fd : " + fd +
   1561                         "\n     cid : " + cid +
   1562                         "\n     cl : " + cl +
   1563                         "\n     cdisp : " + cdisp);
   1564 
   1565                 part = message.addMimePart();
   1566                 part.contentType = contentType;
   1567                 part.partName = name;
   1568                 part.contentId = cid;
   1569                 part.contentLocation = cl;
   1570                 part.contentDisposition = cdisp;
   1571 
   1572                 try {
   1573                     if(text != null) {
   1574                         part.data = text.getBytes("UTF-8");
   1575                         part.charsetName = "utf-8";
   1576                     }
   1577                     else {
   1578                         part.data = readMmsDataPart(partId);
   1579                         if(charset != null)
   1580                             part.charsetName = CharacterSets.getMimeName(Integer.parseInt(charset));
   1581                     }
   1582                 } catch (NumberFormatException e) {
   1583                     Log.d(TAG,"extractMmsParts",e);
   1584                     part.data = null;
   1585                     part.charsetName = null;
   1586                 } catch (UnsupportedEncodingException e) {
   1587                     Log.d(TAG,"extractMmsParts",e);
   1588                     part.data = null;
   1589                     part.charsetName = null;
   1590                 } finally {
   1591                 }
   1592                 part.fileName = filename;
   1593             } while(c.moveToNext());
   1594         }
   1595         message.updateCharset();
   1596     }
   1597 
   1598     /**
   1599      *
   1600      * @param id the content provider id for the message to fetch.
   1601      * @param appParams The application parameter object received from the client.
   1602      * @return a byte[] containing the utf-8 encoded bMessage to send to the client.
   1603      * @throws UnsupportedEncodingException if UTF-8 is not supported,
   1604      * which is guaranteed to be supported on an android device
   1605      */
   1606     public byte[] getMmsMessage(long id, BluetoothMapAppParams appParams) throws UnsupportedEncodingException {
   1607         int msgBox, threadId;
   1608         BluetoothMapbMessageMmsEmail message = new BluetoothMapbMessageMmsEmail();
   1609         Cursor c = mResolver.query(Mms.CONTENT_URI, MMS_PROJECTION, "_ID = " + id, null, null);
   1610         if(c != null && c.moveToFirst())
   1611         {
   1612             message.setType(TYPE.MMS);
   1613 
   1614             // The MMS info:
   1615             String read = c.getString(c.getColumnIndex(Mms.READ));
   1616             if (read.equalsIgnoreCase("1"))
   1617                 message.setStatus(true);
   1618             else
   1619                 message.setStatus(false);
   1620 
   1621             msgBox = c.getInt(c.getColumnIndex(Mms.MESSAGE_BOX));
   1622             threadId = c.getInt(c.getColumnIndex(Mms.THREAD_ID));
   1623             message.setFolder(getFolderName(msgBox, threadId));
   1624 
   1625             message.setSubject(c.getString(c.getColumnIndex(Mms.SUBJECT)));
   1626             message.setMessageId(c.getString(c.getColumnIndex(Mms.MESSAGE_ID)));
   1627             message.setContentType(c.getString(c.getColumnIndex(Mms.CONTENT_TYPE)));
   1628             message.setDate(c.getLong(c.getColumnIndex(Mms.DATE)) * 1000L);
   1629             message.setTextOnly(c.getInt(c.getColumnIndex(Mms.TEXT_ONLY)) == 0 ? false : true); // - TODO: Do we need this - yes, if we have only text, we should not make this a multipart message
   1630             message.setIncludeAttachments(appParams.getAttachment() == 0 ? false : true);
   1631             // c.getLong(c.getColumnIndex(Mms.DATE_SENT)); - this is never used
   1632             // c.getInt(c.getColumnIndex(Mms.STATUS)); - don't know what this is
   1633 
   1634             // The parts
   1635             extractMmsParts(id, message);
   1636 
   1637             // The addresses
   1638             extractMmsAddresses(id, message);
   1639 
   1640             c.close();
   1641 
   1642             return message.encode();
   1643         }
   1644         else if(c != null) {
   1645             c.close();
   1646         }
   1647 
   1648         throw new IllegalArgumentException("MMS handle not found");
   1649     }
   1650 
   1651 }
   1652