Home | History | Annotate | Download | only in map
      1 /*
      2 * Copyright (C) 2014 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 org.apache.http.util.ByteArrayBuffer;
     18 
     19 import android.content.ContentResolver;
     20 import android.content.ContentValues;
     21 import android.content.Context;
     22 import android.database.Cursor;
     23 import android.net.Uri;
     24 import android.os.Debug;
     25 import android.os.ParcelFileDescriptor;
     26 import android.provider.BaseColumns;
     27 import com.android.bluetooth.mapapi.BluetoothMapContract;
     28 import com.android.bluetooth.mapapi.BluetoothMapContract.MessageColumns;
     29 import android.provider.ContactsContract;
     30 import android.provider.ContactsContract.Contacts;
     31 import android.provider.ContactsContract.PhoneLookup;
     32 import android.provider.Telephony.Mms;
     33 import android.provider.Telephony.Sms;
     34 import android.telephony.PhoneNumberUtils;
     35 import android.telephony.TelephonyManager;
     36 import android.text.util.Rfc822Token;
     37 import android.text.util.Rfc822Tokenizer;
     38 import android.util.Log;
     39 
     40 import com.android.bluetooth.map.BluetoothMapSmsPdu.SmsPdu;
     41 import com.android.bluetooth.map.BluetoothMapUtils.TYPE;
     42 import com.google.android.mms.pdu.CharacterSets;
     43 import com.google.android.mms.pdu.PduHeaders;
     44 import com.android.bluetooth.map.BluetoothMapAppParams;
     45 
     46 import java.io.ByteArrayOutputStream;
     47 import java.io.FileInputStream;
     48 import java.io.FileNotFoundException;
     49 import java.io.IOException;
     50 import java.io.InputStream;
     51 import java.io.UnsupportedEncodingException;
     52 import java.text.ParseException;
     53 import java.text.SimpleDateFormat;
     54 import java.util.ArrayList;
     55 import java.util.Arrays;
     56 import java.util.Date;
     57 import java.util.List;
     58 
     59 public class BluetoothMapContent {
     60     private static final String TAG = "BluetoothMapContent";
     61 
     62     private static final boolean D = BluetoothMapService.DEBUG;
     63     private static final boolean V = BluetoothMapService.VERBOSE;
     64 
     65     private static final int MASK_SUBJECT = 0x1;
     66     private static final int MASK_DATETIME = 0x2;
     67     private static final int MASK_SENDER_NAME = 0x4;
     68     private static final int MASK_SENDER_ADDRESSING = 0x8;
     69 
     70     private static final int MASK_RECIPIENT_NAME = 0x10;
     71     private static final int MASK_RECIPIENT_ADDRESSING = 0x20;
     72     private static final int MASK_TYPE = 0x40;
     73     private static final int MASK_SIZE = 0x80;
     74 
     75     private static final int MASK_RECEPTION_STATUS = 0x100;
     76     private static final int MASK_TEXT = 0x200;
     77     private static final int MASK_ATTACHMENT_SIZE = 0x400;
     78     private static final int MASK_PRIORITY = 0x800;
     79 
     80     private static final int MASK_READ = 0x1000;
     81     private static final int MASK_SENT = 0x2000;
     82     private static final int MASK_PROTECTED = 0x4000;
     83     private static final int MASK_REPLYTO_ADDRESSING = 0x8000;
     84 
     85     /* Type of MMS address. From Telephony.java it must be one of PduHeaders.BCC, */
     86     /* PduHeaders.CC, PduHeaders.FROM, PduHeaders.TO. These are from PduHeaders.java */
     87     public static final int MMS_FROM = 0x89;
     88     public static final int MMS_TO = 0x97;
     89     public static final int MMS_BCC = 0x81;
     90     public static final int MMS_CC = 0x82;
     91 
     92     public static final String INSERT_ADDRES_TOKEN = "insert-address-token";
     93 
     94     private Context mContext;
     95     private ContentResolver mResolver;
     96     private String mBaseEmailUri = null;
     97 
     98     static final String[] SMS_PROJECTION = new String[] {
     99         BaseColumns._ID,
    100         Sms.THREAD_ID,
    101         Sms.ADDRESS,
    102         Sms.BODY,
    103         Sms.DATE,
    104         Sms.READ,
    105         Sms.TYPE,
    106         Sms.STATUS,
    107         Sms.LOCKED,
    108         Sms.ERROR_CODE
    109     };
    110 
    111     static final String[] MMS_PROJECTION = new String[] {
    112         BaseColumns._ID,
    113         Mms.THREAD_ID,
    114         Mms.MESSAGE_ID,
    115         Mms.MESSAGE_SIZE,
    116         Mms.SUBJECT,
    117         Mms.CONTENT_TYPE,
    118         Mms.TEXT_ONLY,
    119         Mms.DATE,
    120         Mms.DATE_SENT,
    121         Mms.READ,
    122         Mms.MESSAGE_BOX,
    123         Mms.STATUS,
    124         Mms.PRIORITY
    125     };
    126 
    127     private class FilterInfo {
    128         public static final int TYPE_SMS = 0;
    129         public static final int TYPE_MMS = 1;
    130         public static final int TYPE_EMAIL = 2;
    131 
    132         int mMsgType = TYPE_SMS;
    133         int mPhoneType = 0;
    134         String mPhoneNum = null;
    135         String mPhoneAlphaTag = null;
    136         /*column indices used to optimize queries */
    137         public int mEmailColThreadId        = -1;
    138         public int mEmailColProtected       = -1;
    139         public int mEmailColFolder          = -1;
    140         public int mMmsColFolder            = -1;
    141         public int mSmsColFolder            = -1;
    142         public int mEmailColRead            = -1;
    143         public int mSmsColRead              = -1;
    144         public int mMmsColRead              = -1;
    145         public int mEmailColPriority        = -1;
    146         public int mMmsColAttachmentSize    = -1;
    147         public int mEmailColAttachment      = -1;
    148         public int mEmailColAttachementSize = -1;
    149         public int mMmsColTextOnly          = -1;
    150         public int mMmsColId                = -1;
    151         public int mSmsColId                = -1;
    152         public int mEmailColSize            = -1;
    153         public int mSmsColSubject           = -1;
    154         public int mMmsColSize              = -1;
    155         public int mEmailColToAddress       = -1;
    156         public int mEmailColCcAddress       = -1;
    157         public int mEmailColBccAddress      = -1;
    158         public int mSmsColAddress           = -1;
    159         public int mSmsColDate              = -1;
    160         public int mMmsColDate              = -1;
    161         public int mEmailColDate            = -1;
    162         public int mMmsColSubject           = -1;
    163         public int mEmailColSubject         = -1;
    164         public int mSmsColType              = -1;
    165         public int mEmailColFromAddress     = -1;
    166         public int mEmailColId              = -1;
    167 
    168 
    169         public void setEmailColumns(Cursor c) {
    170             mEmailColThreadId        = c.getColumnIndex(BluetoothMapContract.MessageColumns.THREAD_ID);
    171             mEmailColProtected       = c.getColumnIndex(BluetoothMapContract.MessageColumns.FLAG_PROTECTED);
    172             mEmailColFolder          = c.getColumnIndex(BluetoothMapContract.MessageColumns.FOLDER_ID);
    173             mEmailColRead            = c.getColumnIndex(BluetoothMapContract.MessageColumns.FLAG_READ);
    174             mEmailColPriority        = c.getColumnIndex(BluetoothMapContract.MessageColumns.FLAG_HIGH_PRIORITY);
    175             mEmailColAttachment      = c.getColumnIndex(BluetoothMapContract.MessageColumns.FLAG_ATTACHMENT);
    176             mEmailColAttachementSize = c.getColumnIndex(BluetoothMapContract.MessageColumns.ATTACHMENT_SIZE);
    177             mEmailColSize            = c.getColumnIndex(BluetoothMapContract.MessageColumns.MESSAGE_SIZE);
    178             mEmailColToAddress       = c.getColumnIndex(BluetoothMapContract.MessageColumns.TO_LIST);
    179             mEmailColCcAddress       = c.getColumnIndex(BluetoothMapContract.MessageColumns.CC_LIST);
    180             mEmailColBccAddress      = c.getColumnIndex(BluetoothMapContract.MessageColumns.BCC_LIST);
    181             mEmailColDate            = c.getColumnIndex(BluetoothMapContract.MessageColumns.DATE);
    182             mEmailColSubject         = c.getColumnIndex(BluetoothMapContract.MessageColumns.SUBJECT);
    183             mEmailColFromAddress     = c.getColumnIndex(BluetoothMapContract.MessageColumns.FROM_LIST);
    184             mEmailColId              = c.getColumnIndex(BluetoothMapContract.MessageColumns._ID);
    185         }
    186 
    187         public void setSmsColumns(Cursor c) {
    188             mSmsColId      = c.getColumnIndex(BaseColumns._ID);
    189             mSmsColFolder  = c.getColumnIndex(Sms.TYPE);
    190             mSmsColRead    = c.getColumnIndex(Sms.READ);
    191             mSmsColSubject = c.getColumnIndex(Sms.BODY);
    192             mSmsColAddress = c.getColumnIndex(Sms.ADDRESS);
    193             mSmsColDate      = c.getColumnIndex(Sms.DATE);
    194             mSmsColType      = c.getColumnIndex(Sms.TYPE);
    195         }
    196 
    197         public void setMmsColumns(Cursor c) {
    198             mMmsColId              = c.getColumnIndex(BaseColumns._ID);
    199             mMmsColFolder          = c.getColumnIndex(Mms.MESSAGE_BOX);
    200             mMmsColRead            = c.getColumnIndex(Mms.READ);
    201             mMmsColAttachmentSize  = c.getColumnIndex(Mms.MESSAGE_SIZE);
    202             mMmsColTextOnly        = c.getColumnIndex(Mms.TEXT_ONLY);
    203             mMmsColSize            = c.getColumnIndex(Mms.MESSAGE_SIZE);
    204             mMmsColDate            = c.getColumnIndex(Mms.DATE);
    205             mMmsColSubject         = c.getColumnIndex(Mms.SUBJECT);
    206 
    207         }
    208     }
    209 
    210     public BluetoothMapContent(final Context context, String emailBaseUri) {
    211         mContext = context;
    212         mResolver = mContext.getContentResolver();
    213         if (mResolver == null) {
    214             if (D) Log.d(TAG, "getContentResolver failed");
    215         }
    216         mBaseEmailUri = emailBaseUri;
    217     }
    218 
    219 
    220 
    221     private void printSms(Cursor c) {
    222         String body = c.getString(c.getColumnIndex(Sms.BODY));
    223         if (V) Log.v(TAG, "printSms " + BaseColumns._ID + ": " + c.getLong(c.getColumnIndex(BaseColumns._ID)) +
    224                 "\n " + Sms.THREAD_ID + " : " + c.getLong(c.getColumnIndex(Sms.THREAD_ID)) +
    225                 "\n " + Sms.ADDRESS + " : " + c.getString(c.getColumnIndex(Sms.ADDRESS)) +
    226                 "\n " + Sms.BODY + " : " + body.substring(0, Math.min(body.length(), 8)) +
    227                 "\n " + Sms.DATE + " : " + c.getLong(c.getColumnIndex(Sms.DATE)) +
    228                 "\n " + Sms.READ + " : " + c.getLong(c.getColumnIndex(Sms.READ)) +
    229                 "\n " + Sms.TYPE + " : " + c.getInt(c.getColumnIndex(Sms.TYPE)) +
    230                 "\n " + Sms.STATUS + " : " + c.getInt(c.getColumnIndex(Sms.STATUS)) +
    231                 "\n " + Sms.LOCKED + " : " + c.getInt(c.getColumnIndex(Sms.LOCKED)) +
    232                 "\n " + Sms.ERROR_CODE + " : " + c.getInt(c.getColumnIndex(Sms.ERROR_CODE)));
    233 
    234 
    235     }
    236 
    237     private void printMms(Cursor c) {
    238         if (V) Log.v(TAG, "printMms " + BaseColumns._ID + ": " + c.getLong(c.getColumnIndex(BaseColumns._ID)) +
    239                 "\n   " + Mms.THREAD_ID + " : " + c.getLong(c.getColumnIndex(Mms.THREAD_ID)) +
    240                 "\n   " + Mms.MESSAGE_ID + " : " + c.getString(c.getColumnIndex(Mms.MESSAGE_ID)) +
    241                 "\n   " + Mms.SUBJECT + " : " + c.getString(c.getColumnIndex(Mms.SUBJECT)) +
    242                 "\n   " + Mms.CONTENT_TYPE + " : " + c.getString(c.getColumnIndex(Mms.CONTENT_TYPE)) +
    243                 "\n   " + Mms.TEXT_ONLY + " : " + c.getInt(c.getColumnIndex(Mms.TEXT_ONLY)) +
    244                 "\n   " + Mms.DATE + " : " + c.getLong(c.getColumnIndex(Mms.DATE)) +
    245                 "\n   " + Mms.DATE_SENT + " : " + c.getLong(c.getColumnIndex(Mms.DATE_SENT)) +
    246                 "\n   " + Mms.READ + " : " + c.getInt(c.getColumnIndex(Mms.READ)) +
    247                 "\n   " + Mms.MESSAGE_BOX + " : " + c.getInt(c.getColumnIndex(Mms.MESSAGE_BOX)) +
    248                 "\n   " + Mms.STATUS + " : " + c.getInt(c.getColumnIndex(Mms.STATUS)) +
    249                 "\n   " + Mms.PRIORITY + " : " + c.getInt(c.getColumnIndex(Mms.PRIORITY)) +
    250                 "\n   " + Mms.MESSAGE_SIZE + " : " + c.getInt(c.getColumnIndex(Mms.MESSAGE_SIZE)));
    251     }
    252 
    253     private String getDateTimeString(long timestamp) {
    254         SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd'T'HHmmss");
    255         Date date = new Date(timestamp);
    256         return format.format(date); // Format to YYYYMMDDTHHMMSS local time
    257     }
    258     private void printEmail(Cursor c) {
    259         if (V) Log.v(TAG, "printEmail " + BaseColumns._ID + ": " + c.getLong(c.getColumnIndex(BaseColumns._ID)) +
    260                 "\n   " + BluetoothMapContract.MessageColumns.DATE + " : " + getDateTimeString(c.getLong(c.getColumnIndex(BluetoothMapContract.MessageColumns.DATE))) +
    261                 "\n   " + BluetoothMapContract.MessageColumns.SUBJECT + " : " + c.getString(c.getColumnIndex(BluetoothMapContract.MessageColumns.SUBJECT)) +
    262                 "\n   " + BluetoothMapContract.MessageColumns.FLAG_READ + " : " + c.getInt(c.getColumnIndex(BluetoothMapContract.MessageColumns.FLAG_READ)) +
    263                 "\n   " + BluetoothMapContract.MessageColumns.FLAG_HIGH_PRIORITY + " : " + c.getInt(c.getColumnIndex(BluetoothMapContract.MessageColumns.FLAG_HIGH_PRIORITY)) +
    264                 "\n   " + BluetoothMapContract.MessageColumns.RECEPTION_STATE + " : " + c.getInt(c.getColumnIndex(BluetoothMapContract.MessageColumns.RECEPTION_STATE)) +
    265                 "\n   " + BluetoothMapContract.MessageColumns.FLAG_ATTACHMENT + " : " + c.getInt(c.getColumnIndex(BluetoothMapContract.MessageColumns.FLAG_ATTACHMENT)) +
    266                 "\n   " + BluetoothMapContract.MessageColumns.MESSAGE_SIZE + " : " + c.getLong(c.getColumnIndex(BluetoothMapContract.MessageColumns.MESSAGE_SIZE)) +
    267                 "\n   " + BluetoothMapContract.MessageColumns.FLAG_PROTECTED + " : " + c.getInt(c.getColumnIndex(BluetoothMapContract.MessageColumns.FLAG_PROTECTED)) +
    268                 "\n   " + BluetoothMapContract.MessageColumns.FROM_LIST + " : " + c.getString(c.getColumnIndex(BluetoothMapContract.MessageColumns.FROM_LIST)) +
    269                 "\n   " + BluetoothMapContract.MessageColumns.TO_LIST + " : " + c.getString(c.getColumnIndex(BluetoothMapContract.MessageColumns.TO_LIST)) +
    270                 "\n   " + BluetoothMapContract.MessageColumns.CC_LIST + " : " + c.getString(c.getColumnIndex(BluetoothMapContract.MessageColumns.CC_LIST)) +
    271                 "\n   " + BluetoothMapContract.MessageColumns.BCC_LIST + " : " + c.getString(c.getColumnIndex(BluetoothMapContract.MessageColumns.BCC_LIST)) +
    272                 "\n   " + BluetoothMapContract.MessageColumns.REPLY_TO_LIST + " : " + c.getString(c.getColumnIndex(BluetoothMapContract.MessageColumns.REPLY_TO_LIST)) +
    273                 "\n   " + BluetoothMapContract.MessageColumns.FOLDER_ID + " : " + c.getInt(c.getColumnIndex(BluetoothMapContract.MessageColumns.FOLDER_ID)) +
    274                 "\n   " + BluetoothMapContract.MessageColumns.ACCOUNT_ID + " : " + c.getInt(c.getColumnIndex(BluetoothMapContract.MessageColumns.ACCOUNT_ID)) );
    275     }
    276 
    277     private void printMmsAddr(long id) {
    278         final String[] projection = null;
    279         String selection = new String("msg_id=" + id);
    280         String uriStr = new String(Mms.CONTENT_URI + "/" + id + "/addr");
    281         Uri uriAddress = Uri.parse(uriStr);
    282         Cursor c = mResolver.query(uriAddress, projection, selection, null, null);
    283 
    284         if (c.moveToFirst()) {
    285             do {
    286                 String add = c.getString(c.getColumnIndex("address"));
    287                 Integer type = c.getInt(c.getColumnIndex("type"));
    288                 if (type == MMS_TO) {
    289                     if (D) Log.d(TAG, "   recipient: " + add + " (type: " + type + ")");
    290                 } else if (type == MMS_FROM) {
    291                     if (D) Log.d(TAG, "   originator: " + add + " (type: " + type + ")");
    292                 } else {
    293                     if (D) Log.d(TAG, "   address other: " + add + " (type: " + type + ")");
    294                 }
    295 
    296             } while(c.moveToNext());
    297         }
    298     }
    299 
    300     private void printMmsPartImage(long partid) {
    301         String uriStr = new String(Mms.CONTENT_URI + "/part/" + partid);
    302         Uri uriAddress = Uri.parse(uriStr);
    303         int ch;
    304         StringBuffer sb = new StringBuffer("");
    305         InputStream is = null;
    306 
    307         try {
    308             is = mResolver.openInputStream(uriAddress);
    309 
    310             while ((ch = is.read()) != -1) {
    311                 sb.append((char)ch);
    312             }
    313             if (D) Log.d(TAG, sb.toString());
    314 
    315         } catch (IOException e) {
    316             // do nothing for now
    317             e.printStackTrace();
    318         }
    319     }
    320 
    321     private void printMmsParts(long id) {
    322         final String[] projection = null;
    323         String selection = new String("mid=" + id);
    324         String uriStr = new String(Mms.CONTENT_URI + "/" + id + "/part");
    325         Uri uriAddress = Uri.parse(uriStr);
    326         Cursor c = mResolver.query(uriAddress, projection, selection, null, null);
    327 
    328         if (D) Log.d(TAG, "   parts:");
    329         if (c.moveToFirst()) {
    330             do {
    331                 Long partid = c.getLong(c.getColumnIndex(BaseColumns._ID));
    332                 String ct = c.getString(c.getColumnIndex("ct"));
    333                 String name = c.getString(c.getColumnIndex("name"));
    334                 String charset = c.getString(c.getColumnIndex("chset"));
    335                 String filename = c.getString(c.getColumnIndex("fn"));
    336                 String text = c.getString(c.getColumnIndex("text"));
    337                 Integer fd = c.getInt(c.getColumnIndex("_data"));
    338                 String cid = c.getString(c.getColumnIndex("cid"));
    339                 String cl = c.getString(c.getColumnIndex("cl"));
    340                 String cdisp = c.getString(c.getColumnIndex("cd"));
    341 
    342                 if (D) Log.d(TAG, "     _id : " + partid +
    343                     "\n     ct : " + ct +
    344                     "\n     partname : " + name +
    345                     "\n     charset : " + charset +
    346                     "\n     filename : " + filename +
    347                     "\n     text : " + text +
    348                     "\n     fd : " + fd +
    349                     "\n     cid : " + cid +
    350                     "\n     cl : " + cl +
    351                     "\n     cdisp : " + cdisp);
    352 
    353                 /* if (ct.equals("image/jpeg")) { */
    354                 /*     printMmsPartImage(partid); */
    355                 /* } */
    356             } while(c.moveToNext());
    357         }
    358     }
    359 
    360 
    361     private void setProtected(BluetoothMapMessageListingElement e, Cursor c,
    362             FilterInfo fi, BluetoothMapAppParams ap) {
    363         if ((ap.getParameterMask() & MASK_PROTECTED) != 0) {
    364             String protect = "no";
    365             if (fi.mMsgType == FilterInfo.TYPE_EMAIL) {
    366                 int flagProtected = c.getInt(fi.mEmailColProtected);
    367                 if (flagProtected == 1) {
    368                     protect = "yes";
    369                 }
    370             }
    371             if (V) Log.d(TAG, "setProtected: " + protect + "\n");
    372             e.setProtect(protect);
    373         }
    374     }
    375 
    376     /**
    377      * Email only
    378      */
    379     private void setThreadId(BluetoothMapMessageListingElement e, Cursor c,
    380             FilterInfo fi, BluetoothMapAppParams ap) {
    381         if (fi.mMsgType == FilterInfo.TYPE_EMAIL) {
    382             long threadId = c.getLong(fi.mEmailColThreadId);
    383             e.setThreadId(threadId);
    384             if (V) Log.d(TAG, "setThreadId: " + threadId + "\n");
    385         }
    386     }
    387 
    388     private void setSent(BluetoothMapMessageListingElement e, Cursor c,
    389             FilterInfo fi, BluetoothMapAppParams ap) {
    390         if ((ap.getParameterMask() & MASK_SENT) != 0) {
    391             int msgType = 0;
    392             if (fi.mMsgType == FilterInfo.TYPE_SMS) {
    393                 msgType = c.getInt(fi.mSmsColFolder);
    394             } else if (fi.mMsgType == FilterInfo.TYPE_MMS) {
    395                 msgType = c.getInt(fi.mMmsColFolder);
    396             } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL) {
    397                 msgType = c.getInt(fi.mEmailColFolder);
    398             }
    399             String sent = null;
    400             if (msgType == 2) {
    401                 sent = "yes";
    402             } else {
    403                 sent = "no";
    404             }
    405             if (V) Log.d(TAG, "setSent: " + sent);
    406             e.setSent(sent);
    407         }
    408     }
    409 
    410     private void setRead(BluetoothMapMessageListingElement e, Cursor c,
    411             FilterInfo fi, BluetoothMapAppParams ap) {
    412         int read = 0;
    413         if (fi.mMsgType == FilterInfo.TYPE_SMS) {
    414             read = c.getInt(fi.mSmsColRead);
    415         } else if (fi.mMsgType == FilterInfo.TYPE_MMS) {
    416             read = c.getInt(fi.mMmsColRead);
    417         } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL) {
    418             read = c.getInt(fi.mEmailColRead);
    419         }
    420         String setread = null;
    421 
    422         if (V) Log.d(TAG, "setRead: " + setread);
    423         e.setRead((read==1?true:false), ((ap.getParameterMask() & MASK_READ) != 0));
    424     }
    425 
    426     private void setPriority(BluetoothMapMessageListingElement e, Cursor c,
    427             FilterInfo fi, BluetoothMapAppParams ap) {
    428         if ((ap.getParameterMask() & MASK_PRIORITY) != 0) {
    429             String priority = "no";
    430             if (fi.mMsgType == FilterInfo.TYPE_EMAIL) {
    431                 int highPriority = c.getInt(fi.mEmailColPriority);
    432                 if (highPriority == 1) {
    433                     priority = "yes";
    434                 }
    435             }
    436             int pri = 0;
    437             if (fi.mMsgType == FilterInfo.TYPE_MMS) {
    438                 pri = c.getInt(c.getColumnIndex(Mms.PRIORITY));
    439             }
    440             if (pri == PduHeaders.PRIORITY_HIGH) {
    441                 priority = "yes";
    442             }
    443             if (V) Log.d(TAG, "setPriority: " + priority);
    444             e.setPriority(priority);
    445         }
    446     }
    447 
    448     /**
    449      * For SMS we set the attachment size to 0, as all data will be text data, hence
    450      * attachments for SMS is not possible.
    451      * For MMS all data is actually attachments, hence we do set the attachment size to
    452      * the total message size. To provide a more accurate attachment size, one could
    453      * extract the length (in bytes) of the text parts.
    454      */
    455     private void setAttachmentSize(BluetoothMapMessageListingElement e, Cursor c,
    456             FilterInfo fi, BluetoothMapAppParams ap) {
    457         if ((ap.getParameterMask() & MASK_ATTACHMENT_SIZE) != 0) {
    458             int size = 0;
    459             if (fi.mMsgType == FilterInfo.TYPE_MMS) {
    460                 if(c.getInt(fi.mMmsColTextOnly) == 0) {
    461                     size = c.getInt(fi.mMmsColAttachmentSize);
    462                     if(size <= 0) {
    463                         // We know there are attachments, since it is not TextOnly
    464                         // Hence the size in the database must be wrong.
    465                         // Set size to 1 to indicate to the client, that attachments are present
    466                         if (D) Log.d(TAG, "Error in message database, size reported as: " + size
    467                                 + " Changing size to 1");
    468                         size = 1;
    469                     }
    470                 }
    471             } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL) {
    472                 int attachment = c.getInt(fi.mEmailColAttachment);
    473                 size = c.getInt(fi.mEmailColAttachementSize);
    474                 if(attachment == 1 && size == 0) {
    475                     if (D) Log.d(TAG, "Error in message database, attachment size reported as: " + size
    476                             + " Changing size to 1");
    477                     size = 1; /* Ensure we indicate we have attachments in the size, if the
    478                                  message has attachments, in case the e-mail client do not
    479                                  report a size */
    480                 }
    481             }
    482             if (V) Log.d(TAG, "setAttachmentSize: " + size);
    483             e.setAttachmentSize(size);
    484         }
    485     }
    486 
    487     private void setText(BluetoothMapMessageListingElement e, Cursor c,
    488             FilterInfo fi, BluetoothMapAppParams ap) {
    489         if ((ap.getParameterMask() & MASK_TEXT) != 0) {
    490             String hasText = "";
    491             if (fi.mMsgType == FilterInfo.TYPE_SMS) {
    492                 hasText = "yes";
    493             } else if (fi.mMsgType == FilterInfo.TYPE_MMS) {
    494                 int textOnly = c.getInt(fi.mMmsColTextOnly);
    495                 if (textOnly == 1) {
    496                     hasText = "yes";
    497                 } else {
    498                     long id = c.getLong(fi.mMmsColId);
    499                     String text = getTextPartsMms(id);
    500                     if (text != null && text.length() > 0) {
    501                         hasText = "yes";
    502                     } else {
    503                         hasText = "no";
    504                     }
    505                 }
    506             } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL) {
    507                 hasText = "yes";
    508             }
    509             if (V) Log.d(TAG, "setText: " + hasText);
    510             e.setText(hasText);
    511         }
    512     }
    513 
    514     private void setReceptionStatus(BluetoothMapMessageListingElement e, Cursor c,
    515         FilterInfo fi, BluetoothMapAppParams ap) {
    516         if ((ap.getParameterMask() & MASK_RECEPTION_STATUS) != 0) {
    517             String status = "complete";
    518             if (V) Log.d(TAG, "setReceptionStatus: " + status);
    519             e.setReceptionStatus(status);
    520         }
    521     }
    522 
    523     private void setSize(BluetoothMapMessageListingElement e, Cursor c,
    524         FilterInfo fi, BluetoothMapAppParams ap) {
    525         if ((ap.getParameterMask() & MASK_SIZE) != 0) {
    526             int size = 0;
    527             if (fi.mMsgType == FilterInfo.TYPE_SMS) {
    528                 String subject = c.getString(fi.mSmsColSubject);
    529                 size = subject.length();
    530             } else if (fi.mMsgType == FilterInfo.TYPE_MMS) {
    531                 size = c.getInt(fi.mMmsColSize);
    532             } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL) {
    533                 size = c.getInt(fi.mEmailColSize);
    534             }
    535             if(size <= 0) {
    536                 // A message cannot have size 0
    537                 // Hence the size in the database must be wrong.
    538                 // Set size to 1 to indicate to the client, that the message has content.
    539                 if (D) Log.d(TAG, "Error in message database, size reported as: " + size
    540                         + " Changing size to 1");
    541                 size = 1;
    542             }
    543             if (V) Log.d(TAG, "setSize: " + size);
    544             e.setSize(size);
    545         }
    546     }
    547 
    548     private void setType(BluetoothMapMessageListingElement e, Cursor c,
    549         FilterInfo fi, BluetoothMapAppParams ap) {
    550         if ((ap.getParameterMask() & MASK_TYPE) != 0) {
    551             TYPE type = null;
    552             if (fi.mMsgType == FilterInfo.TYPE_SMS) {
    553                 if (fi.mPhoneType == TelephonyManager.PHONE_TYPE_GSM) {
    554                     type = TYPE.SMS_GSM;
    555                 } else if (fi.mPhoneType == TelephonyManager.PHONE_TYPE_CDMA) {
    556                     type = TYPE.SMS_CDMA;
    557                 }
    558             } else if (fi.mMsgType == FilterInfo.TYPE_MMS) {
    559                 type = TYPE.MMS;
    560             } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL) {
    561                 type = TYPE.EMAIL;
    562             }
    563             if (V) Log.d(TAG, "setType: " + type);
    564             e.setType(type);
    565         }
    566     }
    567 
    568     private String setRecipientAddressingEmail(BluetoothMapMessageListingElement e, Cursor c, FilterInfo fi) {
    569         String toAddress, ccAddress, bccAddress;
    570         toAddress = c.getString(fi.mEmailColToAddress);
    571         ccAddress = c.getString(fi.mEmailColCcAddress);
    572         bccAddress = c.getString(fi.mEmailColBccAddress);
    573 
    574         String address = "";
    575         if (toAddress != null) {
    576             address += toAddress;
    577             if (ccAddress != null) {
    578                 address += ",";
    579             }
    580         }
    581         if (ccAddress != null) {
    582             address += ccAddress;
    583             if (bccAddress != null) {
    584                 address += ",";
    585             }
    586         }
    587         if (bccAddress != null) {
    588             address += bccAddress;
    589         }
    590         return address;
    591     }
    592 
    593     private void setRecipientAddressing(BluetoothMapMessageListingElement e, Cursor c,
    594         FilterInfo fi, BluetoothMapAppParams ap) {
    595         if ((ap.getParameterMask() & MASK_RECIPIENT_ADDRESSING) != 0) {
    596             String address = null;
    597             if (fi.mMsgType == FilterInfo.TYPE_SMS) {
    598                 int msgType = c.getInt(fi.mSmsColType);
    599                 if (msgType == 1) {
    600                     address = fi.mPhoneNum;
    601                 } else {
    602                     address = c.getString(c.getColumnIndex(Sms.ADDRESS));
    603                 }
    604             } else if (fi.mMsgType == FilterInfo.TYPE_MMS) {
    605                 long id = c.getLong(c.getColumnIndex(BaseColumns._ID));
    606                 address = getAddressMms(mResolver, id, MMS_TO);
    607             } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL) {
    608                 /* Might be another way to handle addresses */
    609                 address = setRecipientAddressingEmail(e, c,fi);
    610             }
    611             if (V) Log.v(TAG, "setRecipientAddressing: " + address);
    612             if(address == null)
    613                 address = "";
    614             e.setRecipientAddressing(address);
    615         }
    616     }
    617 
    618     private void setRecipientName(BluetoothMapMessageListingElement e, Cursor c,
    619         FilterInfo fi, BluetoothMapAppParams ap) {
    620         if ((ap.getParameterMask() & MASK_RECIPIENT_NAME) != 0) {
    621             String name = null;
    622             if (fi.mMsgType == FilterInfo.TYPE_SMS) {
    623                 int msgType = c.getInt(fi.mSmsColType);
    624                 if (msgType != 1) {
    625                     String phone = c.getString(fi.mSmsColAddress);
    626                     if (phone != null && !phone.isEmpty())
    627                         name = getContactNameFromPhone(phone);
    628                 } else {
    629                     name = fi.mPhoneAlphaTag;
    630                 }
    631             } else if (fi.mMsgType == FilterInfo.TYPE_MMS) {
    632                 long id = c.getLong(fi.mMmsColId);
    633                 String phone;
    634                 if(e.getRecipientAddressing() != null){
    635                     phone = getAddressMms(mResolver, id, MMS_TO);
    636                 } else {
    637                     phone = e.getRecipientAddressing();
    638                 }
    639                 if (phone != null && !phone.isEmpty())
    640                     name = getContactNameFromPhone(phone);
    641             } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL) {
    642                 /* Might be another way to handle address and names */
    643                 name = setRecipientAddressingEmail(e,c,fi);
    644             }
    645             if (V) Log.v(TAG, "setRecipientName: " + name);
    646             if(name == null)
    647                 name = "";
    648             e.setRecipientName(name);
    649         }
    650     }
    651 
    652     private void setSenderAddressing(BluetoothMapMessageListingElement e, Cursor c,
    653             FilterInfo fi, BluetoothMapAppParams ap) {
    654         if ((ap.getParameterMask() & MASK_SENDER_ADDRESSING) != 0) {
    655             String address = null;
    656             String tempAddress;
    657             if (fi.mMsgType == FilterInfo.TYPE_SMS) {
    658                 int msgType = c.getInt(fi.mSmsColType);
    659                 if (msgType == 1) { // INBOX
    660                     tempAddress = c.getString(fi.mSmsColAddress);
    661                 } else {
    662                     tempAddress = fi.mPhoneNum;
    663                 }
    664                 if(tempAddress == null) {
    665                     /* This can only happen on devices with no SIM -
    666                        hence will typically not have any SMS messages. */
    667                 } else {
    668                     address = PhoneNumberUtils.extractNetworkPortion(tempAddress);
    669                     /* extractNetworkPortion can return N if the number is a service "number" = a string
    670                      * with the a name in (i.e. "Some-Tele-company" would return N because of the N in compaNy)
    671                      * Hence we need to check if the number is actually a string with alpha chars.
    672                      * */
    673                     Boolean alpha = PhoneNumberUtils.stripSeparators(tempAddress).matches("[0-9]*[a-zA-Z]+[0-9]*");
    674 
    675                     if(address == null || address.length() < 2 || alpha) {
    676                         address = tempAddress; // if the number is a service acsii text just use it
    677                     }
    678                 }
    679             } else if (fi.mMsgType == FilterInfo.TYPE_MMS) {
    680                 long id = c.getLong(fi.mMmsColId);
    681                 tempAddress = getAddressMms(mResolver, id, MMS_FROM);
    682                 address = PhoneNumberUtils.extractNetworkPortion(tempAddress);
    683                 if(address == null || address.length() < 1){
    684                     address = tempAddress; // if the number is a service acsii text just use it
    685                 }
    686             } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL) {
    687                 address = c.getString(fi.mEmailColFromAddress);
    688             }
    689             if (V) Log.v(TAG, "setSenderAddressing: " + address);
    690             if(address == null)
    691                 address = "";
    692             e.setSenderAddressing(address);
    693         }
    694     }
    695 
    696     private void setSenderName(BluetoothMapMessageListingElement e, Cursor c,
    697             FilterInfo fi, BluetoothMapAppParams ap) {
    698         if ((ap.getParameterMask() & MASK_SENDER_NAME) != 0) {
    699             String name = null;
    700             if (fi.mMsgType == FilterInfo.TYPE_SMS) {
    701                 int msgType = c.getInt(c.getColumnIndex(Sms.TYPE));
    702                 if (msgType == 1) {
    703                     String phone = c.getString(fi.mSmsColAddress);
    704                     if (phone != null && !phone.isEmpty())
    705                         name = getContactNameFromPhone(phone);
    706                 } else {
    707                     name = fi.mPhoneAlphaTag;
    708                 }
    709             } else if (fi.mMsgType == FilterInfo.TYPE_MMS) {
    710                 long id = c.getLong(fi.mMmsColId);
    711                 String phone;
    712                 if(e.getSenderAddressing() != null){
    713                     phone = getAddressMms(mResolver, id, MMS_FROM);
    714                 } else {
    715                     phone = e.getSenderAddressing();
    716                 }
    717                 if (phone != null && !phone.isEmpty() )
    718                     name = getContactNameFromPhone(phone);
    719             } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL) {
    720                 name = c.getString(fi.mEmailColFromAddress);
    721             }
    722             if (V) Log.v(TAG, "setSenderName: " + name);
    723             if(name == null)
    724                 name = "";
    725             e.setSenderName(name);
    726         }
    727     }
    728 
    729     private void setDateTime(BluetoothMapMessageListingElement e, Cursor c,
    730             FilterInfo fi, BluetoothMapAppParams ap) {
    731         if ((ap.getParameterMask() & MASK_DATETIME) != 0) {
    732             long date = 0;
    733             if (fi.mMsgType == FilterInfo.TYPE_SMS) {
    734                 date = c.getLong(fi.mSmsColDate);
    735             } else if (fi.mMsgType == FilterInfo.TYPE_MMS) {
    736                 /* Use Mms.DATE for all messages. Although contract class states */
    737                 /* Mms.DATE_SENT are for outgoing messages. But that is not working. */
    738                 date = c.getLong(fi.mMmsColDate) * 1000L;
    739 
    740                 /* int msgBox = c.getInt(c.getColumnIndex(Mms.MESSAGE_BOX)); */
    741                 /* if (msgBox == Mms.MESSAGE_BOX_INBOX) { */
    742                 /*     date = c.getLong(c.getColumnIndex(Mms.DATE)) * 1000L; */
    743                 /* } else { */
    744                 /*     date = c.getLong(c.getColumnIndex(Mms.DATE_SENT)) * 1000L; */
    745                 /* } */
    746             } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL) {
    747                 date = c.getLong(fi.mEmailColDate);
    748             }
    749             e.setDateTime(date);
    750             if (V) Log.v(TAG, "setDateTime: " + e.getDateTimeString());
    751         }
    752     }
    753 
    754     private String getTextPartsMms(long id) {
    755         String text = "";
    756         String selection = new String("mid=" + id);
    757         String uriStr = new String(Mms.CONTENT_URI + "/" + id + "/part");
    758         Uri uriAddress = Uri.parse(uriStr);
    759         // TODO: maybe use a projection with only "ct" and "text"
    760         Cursor c = mResolver.query(uriAddress, null, selection,
    761             null, null);
    762 
    763         if (c != null && c.moveToFirst()) {
    764             do {
    765                 String ct = c.getString(c.getColumnIndex("ct"));
    766                 if (ct.equals("text/plain")) {
    767                     String part = c.getString(c.getColumnIndex("text"));
    768                     if(part != null) {
    769                         text += part;
    770                     }
    771                 }
    772             } while(c.moveToNext());
    773         }
    774         if (c != null) {
    775             c.close();
    776         }
    777         return text;
    778     }
    779 
    780     private void setSubject(BluetoothMapMessageListingElement e, Cursor c,
    781             FilterInfo fi, BluetoothMapAppParams ap) {
    782         String subject = "";
    783         int subLength = ap.getSubjectLength();
    784         if(subLength == BluetoothMapAppParams.INVALID_VALUE_PARAMETER)
    785             subLength = 256;
    786 
    787         if ((ap.getParameterMask() & MASK_SUBJECT) != 0) {
    788             if (fi.mMsgType == FilterInfo.TYPE_SMS) {
    789                 subject = c.getString(fi.mSmsColSubject);
    790             } else if (fi.mMsgType == FilterInfo.TYPE_MMS) {
    791                 subject = c.getString(fi.mMmsColSubject);
    792                 if (subject == null || subject.length() == 0) {
    793                     /* Get subject from mms text body parts - if any exists */
    794                     long id = c.getLong(fi.mMmsColId);
    795                     subject = getTextPartsMms(id);
    796                 }
    797             } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL) {
    798                 subject = c.getString(fi.mEmailColSubject);
    799             }
    800             if (subject != null && subject.length() > subLength) {
    801                 subject = subject.substring(0, subLength);
    802             }
    803             if (V) Log.d(TAG, "setSubject: " + subject);
    804             e.setSubject(subject);
    805         }
    806     }
    807 
    808     private void setHandle(BluetoothMapMessageListingElement e, Cursor c,
    809             FilterInfo fi, BluetoothMapAppParams ap) {
    810         long handle = -1;
    811         if (fi.mMsgType == FilterInfo.TYPE_SMS) {
    812             handle = c.getLong(fi.mSmsColId);
    813         } else if (fi.mMsgType == FilterInfo.TYPE_MMS) {
    814             handle = c.getLong(fi.mMmsColId);
    815         } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL) {
    816             handle = c.getLong(fi.mEmailColId);
    817         }
    818         if (V) Log.d(TAG, "setHandle: " + handle );
    819         e.setHandle(handle);
    820     }
    821 
    822     private BluetoothMapMessageListingElement element(Cursor c, FilterInfo fi,
    823             BluetoothMapAppParams ap) {
    824         BluetoothMapMessageListingElement e = new BluetoothMapMessageListingElement();
    825         setHandle(e, c, fi, ap);
    826         setDateTime(e, c, fi, ap);
    827         setType(e, c, fi, ap);
    828         setRead(e, c, fi, ap);
    829         // we set number and name for sender/recipient later
    830         // they require lookup on contacts so no need to
    831         // do it for all elements unless they are to be used.
    832         e.setCursorIndex(c.getPosition());
    833         return e;
    834     }
    835 
    836     private String getContactNameFromPhone(String phone) {
    837         String name = null;
    838 
    839         Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI,
    840             Uri.encode(phone));
    841 
    842         String[] projection = {Contacts._ID, Contacts.DISPLAY_NAME};
    843         String selection = Contacts.IN_VISIBLE_GROUP + "=1";
    844         String orderBy = Contacts.DISPLAY_NAME + " ASC";
    845 
    846         Cursor c = mResolver.query(uri, projection, selection, null, orderBy);
    847 
    848         if (c != null && c.getCount() >= 1) {
    849             c.moveToFirst();
    850             name = c.getString(c.getColumnIndex(Contacts.DISPLAY_NAME));
    851         }
    852 
    853         c.close();
    854         return name;
    855     }
    856 
    857     static public String getAddressMms(ContentResolver r, long id, int type) {
    858         String selection = new String("msg_id=" + id + " AND type=" + type);
    859         String uriStr = new String(Mms.CONTENT_URI + "/" + id + "/addr");
    860         Uri uriAddress = Uri.parse(uriStr);
    861         String addr = null;
    862         Cursor c = r.query(uriAddress, null, selection, null, null);
    863 
    864         if (c != null && c.moveToFirst()) {
    865             addr = c.getString(c.getColumnIndex(Mms.Addr.ADDRESS));
    866             if(addr.equals(INSERT_ADDRES_TOKEN))
    867                 addr  = "";
    868         }
    869 
    870         if (c != null) {
    871             c.close();
    872         }
    873         return addr;
    874     }
    875 
    876     /**
    877      * Matching functions for originator and recipient for MMS
    878      * @return true if found a match
    879      */
    880     private boolean matchRecipientMms(Cursor c, FilterInfo fi, String recip) {
    881         boolean res;
    882         long id = c.getLong(c.getColumnIndex(BaseColumns._ID));
    883         String phone = getAddressMms(mResolver, id, MMS_TO);
    884         if (phone != null && phone.length() > 0) {
    885             if (phone.matches(recip)) {
    886                 if (V) Log.v(TAG, "matchRecipientMms: match recipient phone = " + phone);
    887                 res = true;
    888             } else {
    889                 String name = getContactNameFromPhone(phone);
    890                 if (name != null && name.length() > 0 && name.matches(recip)) {
    891                     if (V) Log.v(TAG, "matchRecipientMms: match recipient name = " + name);
    892                     res = true;
    893                 } else {
    894                     res = false;
    895                 }
    896             }
    897         } else {
    898             res = false;
    899         }
    900         return res;
    901     }
    902 
    903     private boolean matchRecipientSms(Cursor c, FilterInfo fi, String recip) {
    904         boolean res;
    905         int msgType = c.getInt(c.getColumnIndex(Sms.TYPE));
    906         if (msgType == 1) {
    907             String phone = fi.mPhoneNum;
    908             String name = fi.mPhoneAlphaTag;
    909             if (phone != null && phone.length() > 0 && phone.matches(recip)) {
    910                 if (V) Log.v(TAG, "matchRecipientSms: match recipient phone = " + phone);
    911                 res = true;
    912             } else if (name != null && name.length() > 0 && name.matches(recip)) {
    913                 if (V) Log.v(TAG, "matchRecipientSms: match recipient name = " + name);
    914                 res = true;
    915             } else {
    916                 res = false;
    917             }
    918         } else {
    919             String phone = c.getString(c.getColumnIndex(Sms.ADDRESS));
    920             if (phone != null && phone.length() > 0) {
    921                 if (phone.matches(recip)) {
    922                     if (V) Log.v(TAG, "matchRecipientSms: match recipient phone = " + phone);
    923                     res = true;
    924                 } else {
    925                     String name = getContactNameFromPhone(phone);
    926                     if (name != null && name.length() > 0 && name.matches(recip)) {
    927                         if (V) Log.v(TAG, "matchRecipientSms: match recipient name = " + name);
    928                         res = true;
    929                     } else {
    930                         res = false;
    931                     }
    932                 }
    933             } else {
    934                 res = false;
    935             }
    936         }
    937         return res;
    938     }
    939 
    940     private boolean matchRecipient(Cursor c, FilterInfo fi, BluetoothMapAppParams ap) {
    941         boolean res;
    942         String recip = ap.getFilterRecipient();
    943         if (recip != null && recip.length() > 0) {
    944             recip = recip.replace("*", ".*");
    945             recip = ".*" + recip + ".*";
    946             if (fi.mMsgType == FilterInfo.TYPE_SMS) {
    947                 res = matchRecipientSms(c, fi, recip);
    948             } else if (fi.mMsgType == FilterInfo.TYPE_MMS) {
    949                 res = matchRecipientMms(c, fi, recip);
    950             } else {
    951                 if (D) Log.d(TAG, "matchRecipient: Unknown msg type: " + fi.mMsgType);
    952                 res = false;
    953             }
    954         } else {
    955             res = true;
    956         }
    957         return res;
    958     }
    959 
    960     private boolean matchOriginatorMms(Cursor c, FilterInfo fi, String orig) {
    961         boolean res;
    962         long id = c.getLong(c.getColumnIndex(BaseColumns._ID));
    963         String phone = getAddressMms(mResolver, id, MMS_FROM);
    964         if (phone != null && phone.length() > 0) {
    965             if (phone.matches(orig)) {
    966                 if (V) Log.v(TAG, "matchOriginatorMms: match originator phone = " + phone);
    967                 res = true;
    968             } else {
    969                 String name = getContactNameFromPhone(phone);
    970                 if (name != null && name.length() > 0 && name.matches(orig)) {
    971                     if (V) Log.v(TAG, "matchOriginatorMms: match originator name = " + name);
    972                     res = true;
    973                 } else {
    974                     res = false;
    975                 }
    976             }
    977         } else {
    978             res = false;
    979         }
    980         return res;
    981     }
    982 
    983     private boolean matchOriginatorSms(Cursor c, FilterInfo fi, String orig) {
    984         boolean res;
    985         int msgType = c.getInt(c.getColumnIndex(Sms.TYPE));
    986         if (msgType == 1) {
    987             String phone = c.getString(c.getColumnIndex(Sms.ADDRESS));
    988             if (phone !=null && phone.length() > 0) {
    989                 if (phone.matches(orig)) {
    990                     if (V) Log.v(TAG, "matchOriginatorSms: match originator phone = " + phone);
    991                     res = true;
    992                 } else {
    993                     String name = getContactNameFromPhone(phone);
    994                     if (name != null && name.length() > 0 && name.matches(orig)) {
    995                         if (V) Log.v(TAG, "matchOriginatorSms: match originator name = " + name);
    996                         res = true;
    997                     } else {
    998                         res = false;
    999                     }
   1000                 }
   1001             } else {
   1002                 res = false;
   1003             }
   1004         } else {
   1005             String phone = fi.mPhoneNum;
   1006             String name = fi.mPhoneAlphaTag;
   1007             if (phone != null && phone.length() > 0 && phone.matches(orig)) {
   1008                 if (V) Log.v(TAG, "matchOriginatorSms: match originator phone = " + phone);
   1009                 res = true;
   1010             } else if (name != null && name.length() > 0 && name.matches(orig)) {
   1011                 if (V) Log.v(TAG, "matchOriginatorSms: match originator name = " + name);
   1012                 res = true;
   1013             } else {
   1014                 res = false;
   1015             }
   1016         }
   1017         return res;
   1018     }
   1019 
   1020    private boolean matchOriginator(Cursor c, FilterInfo fi, BluetoothMapAppParams ap) {
   1021         boolean res;
   1022         String orig = ap.getFilterOriginator();
   1023         if (orig != null && orig.length() > 0) {
   1024             orig = orig.replace("*", ".*");
   1025             orig = ".*" + orig + ".*";
   1026             if (fi.mMsgType == FilterInfo.TYPE_SMS) {
   1027                 res = matchOriginatorSms(c, fi, orig);
   1028             } else if (fi.mMsgType == FilterInfo.TYPE_MMS) {
   1029                 res = matchOriginatorMms(c, fi, orig);
   1030             } else {
   1031                 if(D) Log.d(TAG, "matchOriginator: Unknown msg type: " + fi.mMsgType);
   1032                 res = false;
   1033             }
   1034         } else {
   1035             res = true;
   1036         }
   1037         return res;
   1038     }
   1039 
   1040     private boolean matchAddresses(Cursor c, FilterInfo fi, BluetoothMapAppParams ap) {
   1041         if (matchOriginator(c, fi, ap) && matchRecipient(c, fi, ap)) {
   1042             return true;
   1043         } else {
   1044             return false;
   1045         }
   1046     }
   1047 
   1048     /*
   1049      * Where filter functions
   1050      * */
   1051     private String setWhereFilterFolderTypeSms(String folder) {
   1052         String where = "";
   1053         if (BluetoothMapContract.FOLDER_NAME_INBOX.equalsIgnoreCase(folder)) {
   1054             where = Sms.TYPE + " = 1 AND " + Sms.THREAD_ID + " <> -1";
   1055         } else if (BluetoothMapContract.FOLDER_NAME_OUTBOX.equalsIgnoreCase(folder)) {
   1056             where = "(" + Sms.TYPE + " = 4 OR " + Sms.TYPE + " = 5 OR "
   1057                     + Sms.TYPE + " = 6) AND " + Sms.THREAD_ID + " <> -1";
   1058         } else if (BluetoothMapContract.FOLDER_NAME_SENT.equalsIgnoreCase(folder)) {
   1059             where = Sms.TYPE + " = 2 AND " + Sms.THREAD_ID + " <> -1";
   1060         } else if (BluetoothMapContract.FOLDER_NAME_DRAFT.equalsIgnoreCase(folder)) {
   1061             where = Sms.TYPE + " = 3 AND " + Sms.THREAD_ID + " <> -1";
   1062         } else if (BluetoothMapContract.FOLDER_NAME_DELETED.equalsIgnoreCase(folder)) {
   1063             where = Sms.THREAD_ID + " = -1";
   1064         }
   1065 
   1066         return where;
   1067     }
   1068 
   1069     private String setWhereFilterFolderTypeMms(String folder) {
   1070         String where = "";
   1071         if (BluetoothMapContract.FOLDER_NAME_INBOX.equalsIgnoreCase(folder)) {
   1072             where = Mms.MESSAGE_BOX + " = 1 AND " + Mms.THREAD_ID + " <> -1";
   1073         } else if (BluetoothMapContract.FOLDER_NAME_OUTBOX.equalsIgnoreCase(folder)) {
   1074             where = Mms.MESSAGE_BOX + " = 4 AND " + Mms.THREAD_ID + " <> -1";
   1075         } else if (BluetoothMapContract.FOLDER_NAME_SENT.equalsIgnoreCase(folder)) {
   1076             where = Mms.MESSAGE_BOX + " = 2 AND " + Mms.THREAD_ID + " <> -1";
   1077         } else if (BluetoothMapContract.FOLDER_NAME_DRAFT.equalsIgnoreCase(folder)) {
   1078             where = Mms.MESSAGE_BOX + " = 3 AND " + Mms.THREAD_ID + " <> -1";
   1079         } else if (BluetoothMapContract.FOLDER_NAME_DELETED.equalsIgnoreCase(folder)) {
   1080             where = Mms.THREAD_ID + " = -1";
   1081         }
   1082 
   1083         return where;
   1084     }
   1085 
   1086     private String setWhereFilterFolderTypeEmail(long folderId) {
   1087         String where = "";
   1088         if (folderId >= 0) {
   1089             where = BluetoothMapContract.MessageColumns.FOLDER_ID + " = " + folderId;
   1090         } else {
   1091             Log.e(TAG, "setWhereFilterFolderTypeEmail: not valid!" );
   1092             throw new IllegalArgumentException("Invalid folder ID");
   1093         }
   1094         return where;
   1095     }
   1096 
   1097     private String setWhereFilterFolderType(BluetoothMapFolderElement folderElement, FilterInfo fi) {
   1098         String where = "";
   1099         if (fi.mMsgType == FilterInfo.TYPE_SMS) {
   1100             where = setWhereFilterFolderTypeSms(folderElement.getName());
   1101         } else if (fi.mMsgType == FilterInfo.TYPE_MMS) {
   1102             where = setWhereFilterFolderTypeMms(folderElement.getName());
   1103         } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL) {
   1104             where = setWhereFilterFolderTypeEmail(folderElement.getEmailFolderId());
   1105         }
   1106         return where;
   1107     }
   1108 
   1109     private String setWhereFilterReadStatus(BluetoothMapAppParams ap, FilterInfo fi) {
   1110         String where = "";
   1111         if (ap.getFilterReadStatus() != -1) {
   1112             if (fi.mMsgType == FilterInfo.TYPE_SMS) {
   1113                 if ((ap.getFilterReadStatus() & 0x01) != 0) {
   1114                     where = " AND " + Sms.READ + "= 0";
   1115                 }
   1116 
   1117                 if ((ap.getFilterReadStatus() & 0x02) != 0) {
   1118                     where = " AND " + Sms.READ + "= 1";
   1119                 }
   1120             } else if (fi.mMsgType == FilterInfo.TYPE_MMS) {
   1121                 if ((ap.getFilterReadStatus() & 0x01) != 0) {
   1122                     where = " AND " + Mms.READ + "= 0";
   1123                 }
   1124 
   1125                 if ((ap.getFilterReadStatus() & 0x02) != 0) {
   1126                     where = " AND " + Mms.READ + "= 1";
   1127                 }
   1128             } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL) {
   1129                 if ((ap.getFilterReadStatus() & 0x01) != 0) {
   1130                     where = " AND " + BluetoothMapContract.MessageColumns.FLAG_READ + "= 0";
   1131                 }
   1132 
   1133                 if ((ap.getFilterReadStatus() & 0x02) != 0) {
   1134                     where = " AND " + BluetoothMapContract.MessageColumns.FLAG_READ + "= 1";
   1135                 }
   1136             }
   1137         }
   1138         return where;
   1139     }
   1140 
   1141     private String setWhereFilterPeriod(BluetoothMapAppParams ap, FilterInfo fi) {
   1142         String where = "";
   1143         if ((ap.getFilterPeriodBegin() != -1)) {
   1144             if (fi.mMsgType == FilterInfo.TYPE_SMS) {
   1145             where = " AND " + Sms.DATE + " >= " + ap.getFilterPeriodBegin();
   1146             } else if (fi.mMsgType == FilterInfo.TYPE_MMS) {
   1147                 where = " AND " + Mms.DATE + " >= " + (ap.getFilterPeriodBegin() / 1000L);
   1148             } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL) {
   1149                 where = " AND " + BluetoothMapContract.MessageColumns.DATE + " >= " + (ap.getFilterPeriodBegin());
   1150             }
   1151         }
   1152 
   1153         if ((ap.getFilterPeriodEnd() != -1)) {
   1154             if (fi.mMsgType == FilterInfo.TYPE_SMS) {
   1155             where += " AND " + Sms.DATE + " < " + ap.getFilterPeriodEnd();
   1156             } else if (fi.mMsgType == FilterInfo.TYPE_MMS) {
   1157                 where += " AND " + Mms.DATE + " < " + (ap.getFilterPeriodEnd() / 1000L);
   1158             } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL) {
   1159                 where += " AND " + BluetoothMapContract.MessageColumns.DATE + " < " + (ap.getFilterPeriodEnd());
   1160             }
   1161         }
   1162 
   1163 
   1164         return where;
   1165     }
   1166 
   1167     private String setWhereFilterPhones(String str) {
   1168         String where = "";
   1169         str = str.replace("*", "%");
   1170 
   1171         Cursor c = mResolver.query(ContactsContract.Contacts.CONTENT_URI, null,
   1172             ContactsContract.Contacts.DISPLAY_NAME + " like ?",
   1173             new String[]{str},
   1174             ContactsContract.Contacts.DISPLAY_NAME + " ASC");
   1175 
   1176         while (c != null && c.moveToNext()) {
   1177             String contactId = c.getString(c.getColumnIndex(ContactsContract.Contacts._ID));
   1178 
   1179             Cursor p = mResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null,
   1180                 ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?",
   1181                 new String[]{contactId},
   1182                 null);
   1183 
   1184             while (p != null && p.moveToNext()) {
   1185                 String number = p.getString(
   1186                     p.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
   1187 
   1188                 where += " address = " + "'" + number + "'";
   1189                 if (!p.isLast()) {
   1190                     where += " OR ";
   1191                 }
   1192             }
   1193             if (!c.isLast()) {
   1194                 where += " OR ";
   1195             }
   1196             p.close();
   1197         }
   1198         c.close();
   1199 
   1200         if (str != null && str.length() > 0) {
   1201             if (where.length() > 0) {
   1202                 where += " OR ";
   1203             }
   1204             where += " address like " + "'" + str + "'";
   1205         }
   1206 
   1207         return where;
   1208     }
   1209 
   1210     private String setWhereFilterOriginatorEmail(BluetoothMapAppParams ap) {
   1211         String where = "";
   1212         String orig = ap.getFilterOriginator();
   1213 
   1214         /* Be aware of wild cards in the beginning of string, may not be valid? */
   1215         if (orig != null && orig.length() > 0) {
   1216             orig = orig.replace("*", "%");
   1217             where = " AND " + BluetoothMapContract.MessageColumns.FROM_LIST + " LIKE '%" +  orig + "%'";
   1218         }
   1219         return where;
   1220     }
   1221     private String setWhereFilterPriority(BluetoothMapAppParams ap, FilterInfo fi) {
   1222         String where = "";
   1223         int pri = ap.getFilterPriority();
   1224         /*only MMS have priority info */
   1225         if(fi.mMsgType == FilterInfo.TYPE_MMS)
   1226         {
   1227             if(pri == 0x0002)
   1228             {
   1229                 where += " AND " + Mms.PRIORITY + "<=" +
   1230                     Integer.toString(PduHeaders.PRIORITY_NORMAL);
   1231             }else if(pri == 0x0001) {
   1232                 where += " AND " + Mms.PRIORITY + "=" +
   1233                     Integer.toString(PduHeaders.PRIORITY_HIGH);
   1234             }
   1235         }
   1236         return where;
   1237     }
   1238 
   1239     private String setWhereFilterRecipientEmail(BluetoothMapAppParams ap) {
   1240         String where = "";
   1241         String recip = ap.getFilterRecipient();
   1242 
   1243         /* Be aware of wild cards in the beginning of string, may not be valid? */
   1244         if (recip != null && recip.length() > 0) {
   1245             recip = recip.replace("*", "%");
   1246             where = " AND ("
   1247             + BluetoothMapContract.MessageColumns.TO_LIST  + " LIKE '%" + recip + "%' OR "
   1248             + BluetoothMapContract.MessageColumns.CC_LIST  + " LIKE '%" + recip + "%' OR "
   1249             + BluetoothMapContract.MessageColumns.BCC_LIST + " LIKE '%" + recip + "%' )";
   1250         }
   1251         return where;
   1252     }
   1253 
   1254     private String setWhereFilter(BluetoothMapFolderElement folderElement,
   1255             FilterInfo fi, BluetoothMapAppParams ap) {
   1256         String where = "";
   1257 
   1258         where += setWhereFilterFolderType(folderElement, fi);
   1259         if(!where.isEmpty()) {
   1260             where += setWhereFilterReadStatus(ap, fi);
   1261             where += setWhereFilterPeriod(ap, fi);
   1262             where += setWhereFilterPriority(ap,fi);
   1263 
   1264             if (fi.mMsgType == FilterInfo.TYPE_EMAIL) {
   1265                 where += setWhereFilterOriginatorEmail(ap);
   1266                 where += setWhereFilterRecipientEmail(ap);
   1267             }
   1268         }
   1269 
   1270 
   1271         return where;
   1272     }
   1273 
   1274     /**
   1275      * Determine from application parameter if sms should be included.
   1276      * The filter mask is set for message types not selected
   1277      * @param fi
   1278      * @param ap
   1279      * @return boolean true if sms is selected, false if not
   1280      */
   1281     private boolean smsSelected(FilterInfo fi, BluetoothMapAppParams ap) {
   1282         int msgType = ap.getFilterMessageType();
   1283         int phoneType = fi.mPhoneType;
   1284 
   1285         if (D) Log.d(TAG, "smsSelected msgType: " + msgType);
   1286 
   1287         if (msgType == -1)
   1288             return true;
   1289 
   1290         if ((msgType & 0x03) == 0)
   1291             return true;
   1292 
   1293         if (((msgType & 0x01) == 0) && (phoneType == TelephonyManager.PHONE_TYPE_GSM))
   1294             return true;
   1295 
   1296         if (((msgType & 0x02) == 0) && (phoneType == TelephonyManager.PHONE_TYPE_CDMA))
   1297             return true;
   1298 
   1299         return false;
   1300     }
   1301 
   1302     /**
   1303      * Determine from application parameter if mms should be included.
   1304      * The filter mask is set for message types not selected
   1305      * @param fi
   1306      * @param ap
   1307      * @return boolean true if sms is selected, false if not
   1308      */
   1309     private boolean mmsSelected(FilterInfo fi, BluetoothMapAppParams ap) {
   1310         int msgType = ap.getFilterMessageType();
   1311 
   1312         if (D) Log.d(TAG, "mmsSelected msgType: " + msgType);
   1313 
   1314         if (msgType == -1)
   1315             return true;
   1316 
   1317         if ((msgType & 0x08) == 0)
   1318             return true;
   1319 
   1320         return false;
   1321     }
   1322 
   1323     /**
   1324      * Determine from application parameter if email should be included.
   1325      * The filter mask is set for message types not selected
   1326      * @param fi
   1327      * @param ap
   1328      * @return boolean true if sms is selected, false if not
   1329      */
   1330     private boolean emailSelected(FilterInfo fi, BluetoothMapAppParams ap) {
   1331         int msgType = ap.getFilterMessageType();
   1332 
   1333         if (D) Log.d(TAG, "emailSelected msgType: " + msgType);
   1334 
   1335         if (msgType == -1)
   1336             return true;
   1337 
   1338         if ((msgType & 0x04) == 0)
   1339             return true;
   1340 
   1341         return false;
   1342     }
   1343 
   1344     private void setFilterInfo(FilterInfo fi) {
   1345         TelephonyManager tm = (TelephonyManager)mContext.getSystemService(Context.TELEPHONY_SERVICE);
   1346         if (tm != null) {
   1347             fi.mPhoneType = tm.getPhoneType();
   1348             fi.mPhoneNum = tm.getLine1Number();
   1349             fi.mPhoneAlphaTag = tm.getLine1AlphaTag();
   1350             if (D) Log.d(TAG, "phone type = " + fi.mPhoneType +
   1351                 " phone num = " + fi.mPhoneNum +
   1352                 " phone alpha tag = " + fi.mPhoneAlphaTag);
   1353         }
   1354     }
   1355 
   1356     /**
   1357      * Get a listing of message in folder after applying filter.
   1358      * @param folder Must contain a valid folder string != null
   1359      * @param ap Parameters specifying message content and filters
   1360      * @return Listing object containing requested messages
   1361      */
   1362     public BluetoothMapMessageListing msgListing(BluetoothMapFolderElement folderElement,
   1363             BluetoothMapAppParams ap) {
   1364         if (D) Log.d(TAG, "msgListing: folderName = " + folderElement.getName()
   1365                 + " folderId = " + folderElement.getEmailFolderId()
   1366                 + " messageType = " + ap.getFilterMessageType() );
   1367         BluetoothMapMessageListing bmList = new BluetoothMapMessageListing();
   1368 
   1369 
   1370         /* We overwrite the parameter mask here if it is 0 or not present, as this
   1371          * should cause all parameters to be included in the message list. */
   1372         if(ap.getParameterMask() == BluetoothMapAppParams.INVALID_VALUE_PARAMETER ||
   1373                 ap.getParameterMask() == 0) {
   1374             ap.setParameterMask(BluetoothMapAppParams.PARAMETER_MASK_ALL_ENABLED);
   1375             if (V) Log.v(TAG, "msgListing(): appParameterMask is zero or not present, " +
   1376                     "changing to: " + ap.getParameterMask());
   1377         }
   1378 
   1379         /* Cache some info used throughout filtering */
   1380         FilterInfo fi = new FilterInfo();
   1381         setFilterInfo(fi);
   1382         Cursor smsCursor = null;
   1383         Cursor mmsCursor = null;
   1384         Cursor emailCursor = null;
   1385         String limit = "";
   1386         int countNum = ap.getMaxListCount();
   1387         int offsetNum = ap.getStartOffset();
   1388         if(ap.getMaxListCount()>0){
   1389             limit=" LIMIT "+ (ap.getMaxListCount()+ap.getStartOffset());
   1390         }
   1391 
   1392         if (smsSelected(fi, ap) && folderElement.hasSmsMmsContent()) {
   1393             if(ap.getFilterMessageType() == (BluetoothMapAppParams.FILTER_NO_EMAIL|
   1394                                              BluetoothMapAppParams.FILTER_NO_MMS|
   1395                                              BluetoothMapAppParams.FILTER_NO_SMS_GSM)||
   1396                ap.getFilterMessageType() == (BluetoothMapAppParams.FILTER_NO_EMAIL|
   1397                                              BluetoothMapAppParams.FILTER_NO_MMS|
   1398                                              BluetoothMapAppParams.FILTER_NO_SMS_CDMA)){
   1399                 //set real limit and offset if only this type is used (only if offset/limit is used
   1400                 limit = " LIMIT " + ap.getMaxListCount()+" OFFSET "+ ap.getStartOffset();
   1401                 if(D) Log.d(TAG, "SMS Limit => "+limit);
   1402                 offsetNum = 0;
   1403             }
   1404             fi.mMsgType = FilterInfo.TYPE_SMS;
   1405             if(ap.getFilterPriority() != 1){ /*SMS cannot have high priority*/
   1406                 String where = setWhereFilter(folderElement, fi, ap);
   1407                 if(!where.isEmpty()) {
   1408                     if (D) Log.d(TAG, "msgType: " + fi.mMsgType);
   1409                     smsCursor = mResolver.query(Sms.CONTENT_URI,
   1410                             SMS_PROJECTION, where, null, Sms.DATE + " DESC" + limit);
   1411                     if (smsCursor != null) {
   1412                         BluetoothMapMessageListingElement e = null;
   1413                         // store column index so we dont have to look them up anymore (optimization)
   1414                         if(D) Log.d(TAG, "Found " + smsCursor.getCount() + " sms messages.");
   1415                         fi.setSmsColumns(smsCursor);
   1416                         while (smsCursor.moveToNext()) {
   1417                             if (matchAddresses(smsCursor, fi, ap)) {
   1418                                 if(V) printSms(smsCursor);
   1419                                 e = element(smsCursor, fi, ap);
   1420                                 bmList.add(e);
   1421                             }
   1422                         }
   1423                     }
   1424                 }
   1425             }
   1426         }
   1427 
   1428         if (mmsSelected(fi, ap) && folderElement.hasSmsMmsContent()) {
   1429             if(ap.getFilterMessageType() == (BluetoothMapAppParams.FILTER_NO_EMAIL|
   1430                                              BluetoothMapAppParams.FILTER_NO_SMS_CDMA|
   1431                                              BluetoothMapAppParams.FILTER_NO_SMS_GSM)){
   1432                 //set real limit and offset if only this type is used (only if offset/limit is used
   1433                 limit = " LIMIT " + ap.getMaxListCount()+" OFFSET "+ ap.getStartOffset();
   1434                 if(D) Log.d(TAG, "MMS Limit => "+limit);
   1435                 offsetNum = 0;
   1436             }
   1437             fi.mMsgType = FilterInfo.TYPE_MMS;
   1438             String where = setWhereFilter(folderElement, fi, ap);
   1439             if(!where.isEmpty()) {
   1440                 if (D) Log.d(TAG, "msgType: " + fi.mMsgType);
   1441                 mmsCursor = mResolver.query(Mms.CONTENT_URI,
   1442                         MMS_PROJECTION, where, null, Mms.DATE + " DESC" + limit);
   1443                 if (mmsCursor != null) {
   1444                     BluetoothMapMessageListingElement e = null;
   1445                     // store column index so we dont have to look them up anymore (optimization)
   1446                     fi.setMmsColumns(mmsCursor);
   1447                     int cnt = 0;
   1448                     if(D) Log.d(TAG, "Found " + mmsCursor.getCount() + " mms messages.");
   1449                     while (mmsCursor.moveToNext()) {
   1450                         if (matchAddresses(mmsCursor, fi, ap)) {
   1451                             if(V) printMms(mmsCursor);
   1452                             e = element(mmsCursor, fi, ap);
   1453                             bmList.add(e);
   1454                         }
   1455                     }
   1456                 }
   1457             }
   1458         }
   1459 
   1460         if (emailSelected(fi, ap) && folderElement.getEmailFolderId() != -1) {
   1461             if(ap.getFilterMessageType() == (BluetoothMapAppParams.FILTER_NO_MMS|
   1462                                              BluetoothMapAppParams.FILTER_NO_SMS_CDMA|
   1463                                              BluetoothMapAppParams.FILTER_NO_SMS_GSM)){
   1464                 //set real limit and offset if only this type is used (only if offset/limit is used
   1465                 limit = " LIMIT " + ap.getMaxListCount()+" OFFSET "+ ap.getStartOffset();
   1466                 if(D) Log.d(TAG, "Email Limit => "+limit);
   1467                 offsetNum = 0;
   1468             }
   1469             fi.mMsgType = FilterInfo.TYPE_EMAIL;
   1470             String where = setWhereFilter(folderElement, fi, ap);
   1471 
   1472             if(!where.isEmpty()) {
   1473                 if (D) Log.d(TAG, "msgType: " + fi.mMsgType);
   1474                 Uri contentUri = Uri.parse(mBaseEmailUri + BluetoothMapContract.TABLE_MESSAGE);
   1475                 emailCursor = mResolver.query(contentUri, BluetoothMapContract.BT_MESSAGE_PROJECTION,
   1476                         where, null, BluetoothMapContract.MessageColumns.DATE + " DESC" + limit);
   1477                 if (emailCursor != null) {
   1478                     BluetoothMapMessageListingElement e = null;
   1479                     // store column index so we dont have to look them up anymore (optimization)
   1480                     fi.setEmailColumns(emailCursor);
   1481                     int cnt = 0;
   1482                     while (emailCursor.moveToNext()) {
   1483                         if(V) printEmail(emailCursor);
   1484                         if(D) Log.d(TAG, "Found " + emailCursor.getCount() + " email messages.");
   1485                         e = element(emailCursor, fi, ap);
   1486                         bmList.add(e);
   1487                     }
   1488                 //   emailCursor.close();
   1489                 }
   1490             }
   1491         }
   1492 
   1493         /* Enable this if post sorting and segmenting needed */
   1494         bmList.sort();
   1495         bmList.segment(ap.getMaxListCount(), offsetNum);
   1496         List<BluetoothMapMessageListingElement> list = bmList.getList();
   1497         int listSize = list.size();
   1498         Cursor tmpCursor = null;
   1499         for(int x=0;x<listSize;x++){
   1500             BluetoothMapMessageListingElement ele = list.get(x);
   1501             if((ele.getType().equals(TYPE.SMS_GSM)||ele.getType().equals(TYPE.SMS_CDMA)) && smsCursor != null){
   1502                 tmpCursor = smsCursor;
   1503                 fi.mMsgType = FilterInfo.TYPE_SMS;
   1504             }else if(ele.getType().equals(TYPE.MMS) && mmsCursor != null){
   1505                 tmpCursor = mmsCursor;
   1506                 fi.mMsgType = FilterInfo.TYPE_MMS;
   1507             }else if(ele.getType().equals(TYPE.EMAIL) && emailCursor != null){
   1508                 tmpCursor = emailCursor;
   1509                 fi.mMsgType = FilterInfo.TYPE_EMAIL;
   1510             }
   1511             if(tmpCursor != null){
   1512                 tmpCursor.moveToPosition(ele.getCursorIndex());
   1513                 setSenderAddressing(ele, tmpCursor, fi, ap);
   1514                 setSenderName(ele, tmpCursor, fi, ap);
   1515                 setRecipientAddressing(ele, tmpCursor, fi, ap);
   1516                 setRecipientName(ele, tmpCursor, fi, ap);
   1517                 setSubject(ele, tmpCursor, fi, ap);
   1518                 setSize(ele, tmpCursor, fi, ap);
   1519                 setReceptionStatus(ele, tmpCursor, fi, ap);
   1520                 setText(ele, tmpCursor, fi, ap);
   1521                 setAttachmentSize(ele, tmpCursor, fi, ap);
   1522                 setPriority(ele, tmpCursor, fi, ap);
   1523                 setSent(ele, tmpCursor, fi, ap);
   1524                 setProtected(ele, tmpCursor, fi, ap);
   1525                 setThreadId(ele, tmpCursor, fi, ap);
   1526             }
   1527         }
   1528         if(emailCursor != null)emailCursor.close();
   1529         if(smsCursor != null)smsCursor.close();
   1530         if(mmsCursor != null)mmsCursor.close();
   1531         if(D)Log.d(TAG, "messagelisting end");
   1532         return bmList;
   1533     }
   1534 
   1535     /**
   1536      * Get the size of the message listing
   1537      * @param folder Must contain a valid folder string != null
   1538      * @param ap Parameters specifying message content and filters
   1539      * @return Integer equal to message listing size
   1540      */
   1541     public int msgListingSize(BluetoothMapFolderElement folderElement,
   1542             BluetoothMapAppParams ap) {
   1543         if (D) Log.d(TAG, "msgListingSize: folder = " + folderElement.getName());
   1544         int cnt = 0;
   1545 
   1546         /* Cache some info used throughout filtering */
   1547         FilterInfo fi = new FilterInfo();
   1548         setFilterInfo(fi);
   1549 
   1550         if (smsSelected(fi, ap)  && folderElement.hasSmsMmsContent()) {
   1551             fi.mMsgType = FilterInfo.TYPE_SMS;
   1552             String where = setWhereFilter(folderElement, fi, ap);
   1553             Cursor c = mResolver.query(Sms.CONTENT_URI,
   1554                     SMS_PROJECTION, where, null, Sms.DATE + " DESC");
   1555 
   1556             if (c != null) {
   1557                 cnt = c.getCount();
   1558                 c.close();
   1559             }
   1560         }
   1561 
   1562         if (mmsSelected(fi, ap)  && folderElement.hasSmsMmsContent()) {
   1563             fi.mMsgType = FilterInfo.TYPE_MMS;
   1564             String where = setWhereFilter(folderElement, fi, ap);
   1565             Cursor c = mResolver.query(Mms.CONTENT_URI,
   1566                     MMS_PROJECTION, where, null, Mms.DATE + " DESC");
   1567             if (c != null) {
   1568                 cnt += c.getCount();
   1569                 c.close();
   1570             }
   1571         }
   1572 
   1573         if (emailSelected(fi, ap) && folderElement.getEmailFolderId() != -1) {
   1574             fi.mMsgType = FilterInfo.TYPE_EMAIL;
   1575             String where = setWhereFilter(folderElement, fi, ap);
   1576             if(!where.isEmpty()) {
   1577                 Uri contentUri = Uri.parse(mBaseEmailUri + BluetoothMapContract.TABLE_MESSAGE);
   1578                 Cursor c = mResolver.query(contentUri, BluetoothMapContract.BT_MESSAGE_PROJECTION,
   1579                         where, null, BluetoothMapContract.MessageColumns.DATE + " DESC");
   1580                 if (c != null) {
   1581                     cnt += c.getCount();
   1582                     c.close();
   1583                 }
   1584             }
   1585         }
   1586 
   1587         if (D) Log.d(TAG, "msgListingSize: size = " + cnt);
   1588         return cnt;
   1589     }
   1590 
   1591     /**
   1592      * Return true if there are unread messages in the requested list of messages
   1593      * @param folder folder where the message listing should come from
   1594      * @param ap application parameter object
   1595      * @return true if unread messages are in the list, else false
   1596      */
   1597     public boolean msgListingHasUnread(BluetoothMapFolderElement folderElement,
   1598             BluetoothMapAppParams ap) {
   1599         if (D) Log.d(TAG, "msgListingHasUnread: folder = " + folderElement.getName());
   1600         int cnt = 0;
   1601 
   1602         /* Cache some info used throughout filtering */
   1603         FilterInfo fi = new FilterInfo();
   1604         setFilterInfo(fi);
   1605 
   1606        if (smsSelected(fi, ap)  && folderElement.hasSmsMmsContent()) {
   1607             fi.mMsgType = FilterInfo.TYPE_SMS;
   1608             String where = setWhereFilterFolderType(folderElement, fi);
   1609             where += " AND " + Sms.READ + "=0 ";
   1610             where += setWhereFilterPeriod(ap, fi);
   1611             Cursor c = mResolver.query(Sms.CONTENT_URI,
   1612                 SMS_PROJECTION, where, null, Sms.DATE + " DESC");
   1613 
   1614             if (c != null) {
   1615                 cnt = c.getCount();
   1616                 c.close();
   1617             }
   1618         }
   1619 
   1620         if (mmsSelected(fi, ap)  && folderElement.hasSmsMmsContent()) {
   1621             fi.mMsgType = FilterInfo.TYPE_MMS;
   1622             String where = setWhereFilterFolderType(folderElement, fi);
   1623             where += " AND " + Mms.READ + "=0 ";
   1624             where += setWhereFilterPeriod(ap, fi);
   1625             Cursor c = mResolver.query(Mms.CONTENT_URI,
   1626                 MMS_PROJECTION, where, null, Sms.DATE + " DESC");
   1627 
   1628             if (c != null) {
   1629                 cnt += c.getCount();
   1630                 c.close();
   1631             }
   1632         }
   1633 
   1634 
   1635         if (emailSelected(fi, ap) && folderElement.getEmailFolderId() != -1) {
   1636             fi.mMsgType = FilterInfo.TYPE_EMAIL;
   1637             String where = setWhereFilterFolderType(folderElement, fi);
   1638             if(!where.isEmpty()) {
   1639                 where += " AND " + BluetoothMapContract.MessageColumns.FLAG_READ + "=0 ";
   1640                 where += setWhereFilterPeriod(ap, fi);
   1641                 Uri contentUri = Uri.parse(mBaseEmailUri + BluetoothMapContract.TABLE_MESSAGE);
   1642                 Cursor c = mResolver.query(contentUri, BluetoothMapContract.BT_MESSAGE_PROJECTION,
   1643                         where, null, BluetoothMapContract.MessageColumns.DATE + " DESC");
   1644                 if (c != null) {
   1645                     cnt += c.getCount();
   1646                     c.close();
   1647                 }
   1648             }
   1649         }
   1650 
   1651         if (D) Log.d(TAG, "msgListingHasUnread: numUnread = " + cnt);
   1652         return (cnt>0)?true:false;
   1653     }
   1654 
   1655     /**
   1656      * Get the folder name of an SMS message or MMS message.
   1657      * @param c the cursor pointing at the message
   1658      * @return the folder name.
   1659      */
   1660     private String getFolderName(int type, int threadId) {
   1661 
   1662         if(threadId == -1)
   1663             return BluetoothMapContract.FOLDER_NAME_DELETED;
   1664 
   1665         switch(type) {
   1666         case 1:
   1667             return BluetoothMapContract.FOLDER_NAME_INBOX;
   1668         case 2:
   1669             return BluetoothMapContract.FOLDER_NAME_SENT;
   1670         case 3:
   1671             return BluetoothMapContract.FOLDER_NAME_DRAFT;
   1672         case 4: // Just name outbox, failed and queued "outbox"
   1673         case 5:
   1674         case 6:
   1675             return BluetoothMapContract.FOLDER_NAME_OUTBOX;
   1676         }
   1677         return "";
   1678     }
   1679 
   1680     public byte[] getMessage(String handle, BluetoothMapAppParams appParams,
   1681             BluetoothMapFolderElement folderElement) throws UnsupportedEncodingException{
   1682         TYPE type = BluetoothMapUtils.getMsgTypeFromHandle(handle);
   1683         long id = BluetoothMapUtils.getCpHandle(handle);
   1684         if(appParams.getFractionRequest() == BluetoothMapAppParams.FRACTION_REQUEST_NEXT) {
   1685             throw new IllegalArgumentException("FRACTION_REQUEST_NEXT does not make sence as" +
   1686                                                " we always return the full message.");
   1687         }
   1688         switch(type) {
   1689         case SMS_GSM:
   1690         case SMS_CDMA:
   1691             return getSmsMessage(id, appParams.getCharset());
   1692         case MMS:
   1693             return getMmsMessage(id, appParams);
   1694         case EMAIL:
   1695             return getEmailMessage(id, appParams, folderElement);
   1696         }
   1697         throw new IllegalArgumentException("Invalid message handle.");
   1698     }
   1699 
   1700     private String setVCardFromPhoneNumber(BluetoothMapbMessage message, String phone, boolean incoming) {
   1701         String contactId = null, contactName = null;
   1702         String[] phoneNumbers = null;
   1703         String[] emailAddresses = null;
   1704         Cursor p;
   1705 
   1706         Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI,
   1707                 Uri.encode(phone));
   1708 
   1709         String[] projection = {Contacts._ID, Contacts.DISPLAY_NAME};
   1710         String selection = Contacts.IN_VISIBLE_GROUP + "=1";
   1711         String orderBy = Contacts._ID + " ASC";
   1712 
   1713         // Get the contact _ID and name
   1714         p = mResolver.query(uri, projection, selection, null, orderBy);
   1715         if (p != null && p.getCount() >= 1) {
   1716             p.moveToFirst();
   1717             contactId = p.getString(p.getColumnIndex(Contacts._ID));
   1718             contactName = p.getString(p.getColumnIndex(Contacts.DISPLAY_NAME));
   1719         }
   1720         p.close();
   1721 
   1722         // Bail out if we are unable to find a contact, based on the phone number
   1723         if(contactId == null) {
   1724             phoneNumbers = new String[1];
   1725             phoneNumbers[0] = phone;
   1726         } else {
   1727             // use only actual phone number
   1728             phoneNumbers = new String[1];
   1729             phoneNumbers[0] = phone;
   1730 
   1731             // Fetch contact e-mail addresses
   1732             p = mResolver.query(ContactsContract.CommonDataKinds.Email.CONTENT_URI, null,
   1733                     ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?",
   1734                     new String[]{contactId},
   1735                     null);
   1736             if(p != null) {
   1737                 int i = 0;
   1738                 emailAddresses = new String[p.getCount()];
   1739                 while (p != null && p.moveToNext()) {
   1740                     String emailAddress = p.getString(
   1741                         p.getColumnIndex(ContactsContract.CommonDataKinds.Email.ADDRESS));
   1742                     emailAddresses[i++] = emailAddress;
   1743                 }
   1744                 p.close();
   1745             }
   1746         }
   1747         if(incoming == true) {
   1748             if(V) Log.d(TAG, "Adding originator for phone:" + phone);
   1749             message.addOriginator(contactName, contactName, phoneNumbers, emailAddresses); // Use version 3.0 as we only have a formatted name
   1750         } else {
   1751             if(V) Log.d(TAG, "Adding recipient for phone:" + phone);
   1752             message.addRecipient(contactName, contactName, phoneNumbers, emailAddresses); // Use version 3.0 as we only have a formatted name
   1753         }
   1754         return contactName;
   1755     }
   1756 
   1757     public static final int MAP_MESSAGE_CHARSET_NATIVE = 0;
   1758     public static final int MAP_MESSAGE_CHARSET_UTF8 = 1;
   1759 
   1760     public byte[] getSmsMessage(long id, int charset) throws UnsupportedEncodingException{
   1761         int type, threadId;
   1762         long time = -1;
   1763         String msgBody;
   1764         BluetoothMapbMessageSms message = new BluetoothMapbMessageSms();
   1765         TelephonyManager tm = (TelephonyManager)mContext.getSystemService(Context.TELEPHONY_SERVICE);
   1766         Cursor c = mResolver.query(Sms.CONTENT_URI, SMS_PROJECTION, "_ID = " + id, null, null);
   1767 
   1768         if(c != null && c.moveToFirst())
   1769         {
   1770             if(V) Log.v(TAG,"c.count: " + c.getCount());
   1771 
   1772             if (tm.getPhoneType() == TelephonyManager.PHONE_TYPE_GSM) {
   1773                 message.setType(TYPE.SMS_GSM);
   1774             } else if (tm.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA) {
   1775                 message.setType(TYPE.SMS_CDMA);
   1776             }
   1777 
   1778             String read = c.getString(c.getColumnIndex(Sms.READ));
   1779             if (read.equalsIgnoreCase("1"))
   1780                 message.setStatus(true);
   1781             else
   1782                 message.setStatus(false);
   1783 
   1784             type = c.getInt(c.getColumnIndex(Sms.TYPE));
   1785             threadId = c.getInt(c.getColumnIndex(Sms.THREAD_ID));
   1786             message.setFolder(getFolderName(type, threadId));
   1787 
   1788             msgBody = c.getString(c.getColumnIndex(Sms.BODY));
   1789 
   1790             String phone = c.getString(c.getColumnIndex(Sms.ADDRESS));
   1791 
   1792             time = c.getLong(c.getColumnIndex(Sms.DATE));
   1793             if(type == 1) // Inbox message needs to set the vCard as originator
   1794                 setVCardFromPhoneNumber(message, phone, true);
   1795             else          // Other messages sets the vCard as the recipient
   1796                 setVCardFromPhoneNumber(message, phone, false);
   1797 
   1798             if(charset == MAP_MESSAGE_CHARSET_NATIVE) {
   1799                 if(type == 1) //Inbox
   1800                     message.setSmsBodyPdus(BluetoothMapSmsPdu.getDeliverPdus(msgBody, phone, time));
   1801                 else
   1802                     message.setSmsBodyPdus(BluetoothMapSmsPdu.getSubmitPdus(msgBody, phone));
   1803             } else /*if (charset == MAP_MESSAGE_CHARSET_UTF8)*/ {
   1804                 message.setSmsBody(msgBody);
   1805             }
   1806 
   1807             c.close();
   1808 
   1809             return message.encode();
   1810         }
   1811         throw new IllegalArgumentException("SMS handle not found");
   1812     }
   1813 
   1814     private void extractMmsAddresses(long id, BluetoothMapbMessageMms message) {
   1815         final String[] projection = null;
   1816         String selection = new String(Mms.Addr.MSG_ID + "=" + id);
   1817         String uriStr = new String(Mms.CONTENT_URI + "/" + id + "/addr");
   1818         Uri uriAddress = Uri.parse(uriStr);
   1819         String contactName = null;
   1820         Cursor c = mResolver.query( uriAddress, projection, selection, null, null);
   1821         if (c.moveToFirst()) {
   1822             do {
   1823                 String address = c.getString(c.getColumnIndex(Mms.Addr.ADDRESS));
   1824                 if(address.equals(INSERT_ADDRES_TOKEN))
   1825                     continue;
   1826                 Integer type = c.getInt(c.getColumnIndex(Mms.Addr.TYPE));
   1827                 switch(type) {
   1828                 case MMS_FROM:
   1829                     contactName = setVCardFromPhoneNumber(message, address, true);
   1830                     message.addFrom(contactName, address);
   1831                     break;
   1832                 case MMS_TO:
   1833                     contactName = setVCardFromPhoneNumber(message, address, false);
   1834                     message.addTo(contactName, address);
   1835                     break;
   1836                 case MMS_CC:
   1837                     contactName = setVCardFromPhoneNumber(message, address, false);
   1838                     message.addCc(contactName, address);
   1839                     break;
   1840                 case MMS_BCC:
   1841                     contactName = setVCardFromPhoneNumber(message, address, false);
   1842                     message.addBcc(contactName, address);
   1843                     break;
   1844                 default:
   1845                     break;
   1846                 }
   1847             } while(c.moveToNext());
   1848         }
   1849     }
   1850 
   1851     /**
   1852      * Read out a mms data part and return the data in a byte array.
   1853      * @param partid the content provider id of the mms.
   1854      * @return
   1855      */
   1856     private byte[] readMmsDataPart(long partid) {
   1857         String uriStr = new String(Mms.CONTENT_URI + "/part/" + partid);
   1858         Uri uriAddress = Uri.parse(uriStr);
   1859         InputStream is = null;
   1860         ByteArrayOutputStream os = new ByteArrayOutputStream();
   1861         int bufferSize = 8192;
   1862         byte[] buffer = new byte[bufferSize];
   1863         byte[] retVal = null;
   1864 
   1865         try {
   1866             is = mResolver.openInputStream(uriAddress);
   1867             int len = 0;
   1868             while ((len = is.read(buffer)) != -1) {
   1869               os.write(buffer, 0, len); // We need to specify the len, as it can be != bufferSize
   1870             }
   1871             retVal = os.toByteArray();
   1872         } catch (IOException e) {
   1873             // do nothing for now
   1874             Log.w(TAG,"Error reading part data",e);
   1875         } finally {
   1876             try {
   1877                 os.close();
   1878                 is.close();
   1879             } catch (IOException e) {
   1880             }
   1881         }
   1882         return retVal;
   1883     }
   1884 
   1885     /**
   1886      * Read out the mms parts and update the bMessage object provided i {@linkplain message}
   1887      * @param id the content provider ID of the message
   1888      * @param message the bMessage object to add the information to
   1889      */
   1890     private void extractMmsParts(long id, BluetoothMapbMessageMms message)
   1891     {
   1892         /* Handling of filtering out non-text parts for exclude
   1893          * attachments is handled within the bMessage object. */
   1894         final String[] projection = null;
   1895         String selection = new String(Mms.Part.MSG_ID + "=" + id);
   1896         String uriStr = new String(Mms.CONTENT_URI + "/"+ id + "/part");
   1897         Uri uriAddress = Uri.parse(uriStr);
   1898         BluetoothMapbMessageMms.MimePart part;
   1899         Cursor c = mResolver.query(
   1900             uriAddress,
   1901             projection,
   1902             selection,
   1903             null, null);
   1904 
   1905         if (c.moveToFirst()) {
   1906             do {
   1907                 Long partId = c.getLong(c.getColumnIndex(BaseColumns._ID));
   1908                 String contentType = c.getString(c.getColumnIndex(Mms.Part.CONTENT_TYPE));
   1909                 String name = c.getString(c.getColumnIndex(Mms.Part.NAME));
   1910                 String charset = c.getString(c.getColumnIndex(Mms.Part.CHARSET));
   1911                 String filename = c.getString(c.getColumnIndex(Mms.Part.FILENAME));
   1912                 String text = c.getString(c.getColumnIndex(Mms.Part.TEXT));
   1913                 Integer fd = c.getInt(c.getColumnIndex(Mms.Part._DATA));
   1914                 String cid = c.getString(c.getColumnIndex(Mms.Part.CONTENT_ID));
   1915                 String cl = c.getString(c.getColumnIndex(Mms.Part.CONTENT_LOCATION));
   1916                 String cdisp = c.getString(c.getColumnIndex(Mms.Part.CONTENT_DISPOSITION));
   1917 
   1918                 if(V) Log.d(TAG, "     _id : " + partId +
   1919                         "\n     ct : " + contentType +
   1920                         "\n     partname : " + name +
   1921                         "\n     charset : " + charset +
   1922                         "\n     filename : " + filename +
   1923                         "\n     text : " + text +
   1924                         "\n     fd : " + fd +
   1925                         "\n     cid : " + cid +
   1926                         "\n     cl : " + cl +
   1927                         "\n     cdisp : " + cdisp);
   1928 
   1929                 part = message.addMimePart();
   1930                 part.mContentType = contentType;
   1931                 part.mPartName = name;
   1932                 part.mContentId = cid;
   1933                 part.mContentLocation = cl;
   1934                 part.mContentDisposition = cdisp;
   1935 
   1936                 try {
   1937                     if(text != null) {
   1938                         part.mData = text.getBytes("UTF-8");
   1939                         part.mCharsetName = "utf-8";
   1940                     } else {
   1941                         part.mData = readMmsDataPart(partId);
   1942                         if(charset != null)
   1943                             part.mCharsetName = CharacterSets.getMimeName(Integer.parseInt(charset));
   1944                     }
   1945                 } catch (NumberFormatException e) {
   1946                     Log.d(TAG,"extractMmsParts",e);
   1947                     part.mData = null;
   1948                     part.mCharsetName = null;
   1949                 } catch (UnsupportedEncodingException e) {
   1950                     Log.d(TAG,"extractMmsParts",e);
   1951                     part.mData = null;
   1952                     part.mCharsetName = null;
   1953                 } finally {
   1954                 }
   1955                 part.mFileName = filename;
   1956             } while(c.moveToNext());
   1957         }
   1958         message.updateCharset();
   1959     }
   1960 
   1961     /**
   1962      *
   1963      * @param id the content provider id for the message to fetch.
   1964      * @param appParams The application parameter object received from the client.
   1965      * @return a byte[] containing the utf-8 encoded bMessage to send to the client.
   1966      * @throws UnsupportedEncodingException if UTF-8 is not supported,
   1967      * which is guaranteed to be supported on an android device
   1968      */
   1969     public byte[] getMmsMessage(long id, BluetoothMapAppParams appParams) throws UnsupportedEncodingException {
   1970         int msgBox, threadId;
   1971         if (appParams.getCharset() == MAP_MESSAGE_CHARSET_NATIVE)
   1972             throw new IllegalArgumentException("MMS charset native not allowed for MMS - must be utf-8");
   1973 
   1974         BluetoothMapbMessageMms message = new BluetoothMapbMessageMms();
   1975         Cursor c = mResolver.query(Mms.CONTENT_URI, MMS_PROJECTION, "_ID = " + id, null, null);
   1976         if(c != null && c.moveToFirst())
   1977         {
   1978             message.setType(TYPE.MMS);
   1979 
   1980             // The MMS info:
   1981             String read = c.getString(c.getColumnIndex(Mms.READ));
   1982             if (read.equalsIgnoreCase("1"))
   1983                 message.setStatus(true);
   1984             else
   1985                 message.setStatus(false);
   1986 
   1987             msgBox = c.getInt(c.getColumnIndex(Mms.MESSAGE_BOX));
   1988             threadId = c.getInt(c.getColumnIndex(Mms.THREAD_ID));
   1989             message.setFolder(getFolderName(msgBox, threadId));
   1990             message.setSubject(c.getString(c.getColumnIndex(Mms.SUBJECT)));
   1991             message.setMessageId(c.getString(c.getColumnIndex(Mms.MESSAGE_ID)));
   1992             message.setContentType(c.getString(c.getColumnIndex(Mms.CONTENT_TYPE)));
   1993             message.setDate(c.getLong(c.getColumnIndex(Mms.DATE)) * 1000L);
   1994             message.setTextOnly(c.getInt(c.getColumnIndex(Mms.TEXT_ONLY)) == 0 ? false : true);
   1995             message.setIncludeAttachments(appParams.getAttachment() == 0 ? false : true);
   1996             // c.getLong(c.getColumnIndex(Mms.DATE_SENT)); - this is never used
   1997             // c.getInt(c.getColumnIndex(Mms.STATUS)); - don't know what this is
   1998 
   1999             // The parts
   2000             extractMmsParts(id, message);
   2001 
   2002             // The addresses
   2003             extractMmsAddresses(id, message);
   2004 
   2005             c.close();
   2006 
   2007             return message.encode();
   2008         } else if(c != null) {
   2009             c.close();
   2010         }
   2011 
   2012         throw new IllegalArgumentException("MMS handle not found");
   2013     }
   2014 
   2015     /**
   2016     *
   2017     * @param id the content provider id for the message to fetch.
   2018     * @param appParams The application parameter object received from the client.
   2019     * @return a byte[] containing the utf-8 encoded bMessage to send to the client.
   2020     * @throws UnsupportedEncodingException if UTF-8 is not supported,
   2021     * which is guaranteed to be supported on an android device
   2022     */
   2023    public byte[] getEmailMessage(long id, BluetoothMapAppParams appParams,
   2024            BluetoothMapFolderElement currentFolder) throws UnsupportedEncodingException {
   2025        // Log print out of application parameters set
   2026        if(D && appParams != null) {
   2027            Log.d(TAG,"TYPE_MESSAGE (GET): Attachment = " + appParams.getAttachment() +
   2028                    ", Charset = " + appParams.getCharset() +
   2029                    ", FractionRequest = " + appParams.getFractionRequest());
   2030        }
   2031 
   2032        // Throw exception if requester NATIVE charset for Email
   2033        // Exception is caught by MapObexServer sendGetMessageResp
   2034        if (appParams.getCharset() == MAP_MESSAGE_CHARSET_NATIVE)
   2035            throw new IllegalArgumentException("EMAIL charset not UTF-8");
   2036 
   2037        BluetoothMapbMessageEmail message = new BluetoothMapbMessageEmail();
   2038        Uri contentUri = Uri.parse(mBaseEmailUri + BluetoothMapContract.TABLE_MESSAGE);
   2039        Cursor c = mResolver.query(contentUri, BluetoothMapContract.BT_MESSAGE_PROJECTION, "_ID = " + id, null, null);
   2040        if(c != null && c.moveToFirst())
   2041        {
   2042            BluetoothMapFolderElement folderElement;
   2043            FileInputStream is = null;
   2044            ParcelFileDescriptor fd = null;
   2045 
   2046            // Handle fraction requests
   2047            int fractionRequest = appParams.getFractionRequest();
   2048            if (fractionRequest != BluetoothMapAppParams.INVALID_VALUE_PARAMETER) {
   2049                // Fraction requested
   2050                if(V) {
   2051                    String fractionStr = (fractionRequest == 0) ? "FIRST" : "NEXT";
   2052                    Log.v(TAG, "getEmailMessage - FractionRequest " + fractionStr
   2053                            +  " - send compete message" );
   2054                }
   2055                // Check if message is complete and if not - request message from server
   2056                if (c.getString(c.getColumnIndex(
   2057                        BluetoothMapContract.MessageColumns.RECEPTION_STATE)).equalsIgnoreCase(
   2058                                BluetoothMapContract.RECEPTION_STATE_COMPLETE) == false)  {
   2059                    // TODO: request message from server
   2060                    Log.w(TAG, "getEmailMessage - receptionState not COMPLETE -  Not Implemented!" );
   2061                }
   2062            }
   2063            // Set read status:
   2064            String read = c.getString(c.getColumnIndex(BluetoothMapContract.MessageColumns.FLAG_READ));
   2065            if (read != null && read.equalsIgnoreCase("1"))
   2066                message.setStatus(true);
   2067            else
   2068                message.setStatus(false);
   2069 
   2070            // Set message type:
   2071            message.setType(TYPE.EMAIL);
   2072 
   2073            // Set folder:
   2074            long folderId = c.getLong(c.getColumnIndex(BluetoothMapContract.MessageColumns.FOLDER_ID));
   2075            folderElement = currentFolder.getEmailFolderById(folderId);
   2076            message.setCompleteFolder(folderElement.getFullPath());
   2077 
   2078            // Set recipient:
   2079            String nameEmail = c.getString(c.getColumnIndex(BluetoothMapContract.MessageColumns.TO_LIST));
   2080            Rfc822Token tokens[] = Rfc822Tokenizer.tokenize(nameEmail);
   2081            if (tokens.length != 0) {
   2082                if(D) Log.d(TAG, "Recipient count= " + tokens.length);
   2083                int i = 0;
   2084                while (i < tokens.length) {
   2085                    if(V) Log.d(TAG, "Recipient = " + tokens[i].toString());
   2086                    String[] emails = new String[1];
   2087                    emails[0] = tokens[i].getAddress();
   2088                    String name = tokens[i].getName();
   2089                    message.addRecipient(name, name, null, emails);
   2090                    i++;
   2091                }
   2092            }
   2093 
   2094            // Set originator:
   2095            nameEmail = c.getString(c.getColumnIndex(BluetoothMapContract.MessageColumns.FROM_LIST));
   2096            tokens = Rfc822Tokenizer.tokenize(nameEmail);
   2097            if (tokens.length != 0) {
   2098                if(D) Log.d(TAG, "Originator count= " + tokens.length);
   2099                int i = 0;
   2100                while (i < tokens.length) {
   2101                    if(V) Log.d(TAG, "Originator = " + tokens[i].toString());
   2102                    String[] emails = new String[1];
   2103                    emails[0] = tokens[i].getAddress();
   2104                    String name = tokens[i].getName();
   2105                    message.addOriginator(name, name, null, emails);
   2106                    i++;
   2107                }
   2108            }
   2109            c.close();
   2110 
   2111            // Find out if we get attachments
   2112            String attStr = (appParams.getAttachment() == 0) ?  "/" +  BluetoothMapContract.FILE_MSG_NO_ATTACHMENTS : "";
   2113            Uri uri = Uri.parse(contentUri + "/" + id + attStr);
   2114 
   2115            // Get email message body content
   2116            int count = 0;
   2117            try {
   2118                fd = mResolver.openFileDescriptor(uri, "r");
   2119                is = new FileInputStream(fd.getFileDescriptor());
   2120                StringBuilder email = new StringBuilder("");
   2121                byte[] buffer = new byte[1024];
   2122                while((count = is.read(buffer)) != -1) {
   2123                    // TODO: Handle breaks within a UTF8 character
   2124                    email.append(new String(buffer,0,count));
   2125                    if(V) Log.d(TAG, "Email part = " + new String(buffer,0,count) + " count=" + count);
   2126                }
   2127                // Set email message body:
   2128                message.setEmailBody(email.toString());
   2129            } catch (FileNotFoundException e) {
   2130                Log.w(TAG, e);
   2131            } catch (NullPointerException e) {
   2132                Log.w(TAG, e);
   2133            } catch (IOException e) {
   2134                Log.w(TAG, e);
   2135            }
   2136            finally {
   2137                try {
   2138                    if(is != null)
   2139                        is.close();
   2140                } catch (IOException e) {}
   2141                try {
   2142                    if(fd != null)
   2143                        fd.close();
   2144                } catch (IOException e) {}
   2145            }
   2146            return message.encode();
   2147        } else if(c != null) {
   2148            c.close();
   2149        }
   2150        throw new IllegalArgumentException("EMAIL handle not found");
   2151    }
   2152 }
   2153