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