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