BluetoothPbapVcardManager.java 22.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
/*
 * Copyright (c) 2008-2009, Motorola, Inc.
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * - Neither the name of the Motorola, Inc. nor the names of its contributors
 * may be used to endorse or promote products derived from this software
 * without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
Jackson Fan's avatar
Jackson Fan committed
32 33 34

package com.android.bluetooth.pbap;

35 36
import android.content.ContentResolver;
import android.content.Context;
37 38 39 40
import android.database.Cursor;
import android.net.Uri;
import android.provider.CallLog;
import android.provider.CallLog.Calls;
41 42 43 44
import android.provider.ContactsContract.CommonDataKinds;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.CommonDataKinds.Phone;
45 46 47 48
import android.text.TextUtils;
import android.util.Log;

import com.android.bluetooth.R;
49 50 51
import com.android.vcard.VCardComposer;
import com.android.vcard.VCardConfig;
import com.android.vcard.VCardComposer.OneEntryHandler;
52 53 54

import java.io.IOException;
import java.io.OutputStream;
Jackson Fan's avatar
Jackson Fan committed
55 56
import java.util.ArrayList;

Lixin Yue's avatar
Lixin Yue committed
57
import javax.obex.ServerOperation;
58 59 60
import javax.obex.Operation;
import javax.obex.ResponseCodes;

Jackson Fan's avatar
Jackson Fan committed
61
public class BluetoothPbapVcardManager {
62
    private static final String TAG = "BluetoothPbapVcardManager";
Jackson Fan's avatar
Jackson Fan committed
63

Lixin Yue's avatar
Lixin Yue committed
64 65
    private static final boolean V = BluetoothPbapService.VERBOSE;

Jackson Fan's avatar
Jackson Fan committed
66 67 68 69
    private ContentResolver mResolver;

    private Context mContext;

70
    private StringBuilder mVcardResults = null;
Jackson Fan's avatar
Jackson Fan committed
71

72 73 74 75 76 77
    static final String[] PHONES_PROJECTION = new String[] {
            Data._ID, // 0
            CommonDataKinds.Phone.TYPE, // 1
            CommonDataKinds.Phone.LABEL, // 2
            CommonDataKinds.Phone.NUMBER, // 3
            Contacts.DISPLAY_NAME, // 4
Jackson Fan's avatar
Jackson Fan committed
78 79
    };

80
    private static final int ID_COLUMN_INDEX = 0;
Jackson Fan's avatar
Jackson Fan committed
81

82
    private static final int PHONE_TYPE_COLUMN_INDEX = 1;
Jackson Fan's avatar
Jackson Fan committed
83

84
    private static final int PHONE_LABEL_COLUMN_INDEX = 2;
Jackson Fan's avatar
Jackson Fan committed
85

Daisuke Miyakawa's avatar
Daisuke Miyakawa committed
86
    private static final int PHONE_NUMBER_COLUMN_INDEX = 3;
Jackson Fan's avatar
Jackson Fan committed
87

88
    private static final int CONTACTS_DISPLAY_NAME_COLUMN_INDEX = 4;
Lixin Yue's avatar
Lixin Yue committed
89

90
    static final String SORT_ORDER_PHONE_NUMBER = CommonDataKinds.Phone.NUMBER + " ASC";
Lixin Yue's avatar
Lixin Yue committed
91

92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
    static final String[] CONTACTS_PROJECTION = new String[] {
            Contacts._ID, // 0
            Contacts.DISPLAY_NAME, // 1
    };

    static final int CONTACTS_ID_COLUMN_INDEX = 0;

    static final int CONTACTS_NAME_COLUMN_INDEX = 1;

    // call histories use dynamic handles, and handles should order by date; the
    // most recently one should be the first handle. In table "calls", _id and
    // date are consistent in ordering, to implement simply, we sort by _id
    // here.
    static final String CALLLOG_SORT_ORDER = Calls._ID + " DESC";

107 108
    private static final String CLAUSE_ONLY_VISIBLE = Contacts.IN_VISIBLE_GROUP + "=1";

Jackson Fan's avatar
Jackson Fan committed
109 110 111 112 113
    public BluetoothPbapVcardManager(final Context context) {
        mContext = context;
        mResolver = mContext.getContentResolver();
    }

114
    public final String getOwnerPhoneNumberVcard(final boolean vcardType21) {
115
        BluetoothPbapCallLogComposer composer = new BluetoothPbapCallLogComposer(mContext, false);
116 117 118 119 120
        String name = BluetoothPbapService.getLocalPhoneName();
        String number = BluetoothPbapService.getLocalPhoneNum();
        String vcard = composer.composeVCardForPhoneOwnNumber(Phone.TYPE_MOBILE, name, number,
                vcardType21);
        return vcard;
Jackson Fan's avatar
Jackson Fan committed
121 122
    }

123 124 125 126 127 128 129 130 131
    public final int getPhonebookSize(final int type) {
        int size;
        switch (type) {
            case BluetoothPbapObexServer.ContentType.PHONEBOOK:
                size = getContactsSize();
                break;
            default:
                size = getCallHistorySize(type);
                break;
Jackson Fan's avatar
Jackson Fan committed
132
        }
133 134
        if (V) Log.v(TAG, "getPhonebookSzie size = " + size + " type = " + type);
        return size;
Jackson Fan's avatar
Jackson Fan committed
135 136
    }

137
    public final int getContactsSize() {
138
        final Uri myUri = Contacts.CONTENT_URI;
139 140 141
        int size = 0;
        Cursor contactCursor = null;
        try {
142
            contactCursor = mResolver.query(myUri, null, CLAUSE_ONLY_VISIBLE, null, null);
143 144 145 146 147 148 149
            if (contactCursor != null) {
                size = contactCursor.getCount() + 1; // always has the 0.vcf
            }
        } finally {
            if (contactCursor != null) {
                contactCursor.close();
            }
Jackson Fan's avatar
Jackson Fan committed
150
        }
151
        return size;
Jackson Fan's avatar
Jackson Fan committed
152 153
    }

154
    public final int getCallHistorySize(final int type) {
155
        final Uri myUri = CallLog.Calls.CONTENT_URI;
156 157 158 159 160 161 162 163
        String selection = BluetoothPbapObexServer.createSelectionPara(type);
        int size = 0;
        Cursor callCursor = null;
        try {
            callCursor = mResolver.query(myUri, null, selection, null,
                    CallLog.Calls.DEFAULT_SORT_ORDER);
            if (callCursor != null) {
                size = callCursor.getCount();
Lixin Yue's avatar
Lixin Yue committed
164
            }
165 166 167
        } finally {
            if (callCursor != null) {
                callCursor.close();
Lixin Yue's avatar
Lixin Yue committed
168
            }
169 170 171
        }
        return size;
    }
Lixin Yue's avatar
Lixin Yue committed
172

173
    public final ArrayList<String> loadCallHistoryList(final int type) {
174
        final Uri myUri = CallLog.Calls.CONTENT_URI;
175 176 177 178 179 180 181 182 183 184 185
        String selection = BluetoothPbapObexServer.createSelectionPara(type);
        String[] projection = new String[] {
                Calls.NUMBER, Calls.CACHED_NAME
        };
        final int CALLS_NUMBER_COLUMN_INDEX = 0;
        final int CALLS_NAME_COLUMN_INDEX = 1;

        Cursor callCursor = null;
        ArrayList<String> list = new ArrayList<String>();
        try {
            callCursor = mResolver.query(myUri, projection, selection, null,
186
                    CALLLOG_SORT_ORDER);
187 188 189 190 191 192 193 194 195 196 197 198 199 200
            if (callCursor != null) {
                for (callCursor.moveToFirst(); !callCursor.isAfterLast();
                        callCursor.moveToNext()) {
                    String name = callCursor.getString(CALLS_NAME_COLUMN_INDEX);
                    if (TextUtils.isEmpty(name)) {
                        // name not found,use number instead
                        name = callCursor.getString(CALLS_NUMBER_COLUMN_INDEX);
                    }
                    list.add(name);
                }
            }
        } finally {
            if (callCursor != null) {
                callCursor.close();
Jackson Fan's avatar
Jackson Fan committed
201 202
            }
        }
203
        return list;
Jackson Fan's avatar
Jackson Fan committed
204 205
    }

206
    public final ArrayList<String> getPhonebookNameList(final int orderByWhat) {
207 208
        ArrayList<String> nameList = new ArrayList<String>();
        nameList.add(BluetoothPbapService.getLocalPhoneName());
Lixin Yue's avatar
Lixin Yue committed
209

210 211
        final Uri myUri = Contacts.CONTENT_URI;
        Cursor contactCursor = null;
212
        try {
213
            if (orderByWhat == BluetoothPbapObexServer.ORDER_BY_INDEXED) {
214 215
                contactCursor = mResolver.query(myUri, CONTACTS_PROJECTION, CLAUSE_ONLY_VISIBLE,
                        null, Contacts._ID);
216
            } else if (orderByWhat == BluetoothPbapObexServer.ORDER_BY_ALPHABETICAL) {
217 218
                contactCursor = mResolver.query(myUri, CONTACTS_PROJECTION, CLAUSE_ONLY_VISIBLE,
                        null, Contacts.DISPLAY_NAME);
219 220 221
            }
            if (contactCursor != null) {
                for (contactCursor.moveToFirst(); !contactCursor.isAfterLast(); contactCursor
222
                        .moveToNext()) {
223
                    String name = contactCursor.getString(CONTACTS_NAME_COLUMN_INDEX);
224 225 226 227 228 229 230
                    if (TextUtils.isEmpty(name)) {
                        name = mContext.getString(android.R.string.unknownName);
                    }
                    nameList.add(name);
                }
            }
        } finally {
231 232
            if (contactCursor != null) {
                contactCursor.close();
233 234 235
            }
        }
        return nameList;
Lixin Yue's avatar
Lixin Yue committed
236 237
    }

238 239 240
    public final ArrayList<String> getPhonebookNumberList() {
        ArrayList<String> numberList = new ArrayList<String>();
        numberList.add(BluetoothPbapService.getLocalPhoneNum());
Lixin Yue's avatar
Lixin Yue committed
241

242
        final Uri myUri = Phone.CONTENT_URI;
243 244
        Cursor phoneCursor = null;
        try {
245
            phoneCursor = mResolver.query(myUri, PHONES_PROJECTION, CLAUSE_ONLY_VISIBLE, null,
246 247 248 249
                    SORT_ORDER_PHONE_NUMBER);
            if (phoneCursor != null) {
                for (phoneCursor.moveToFirst(); !phoneCursor.isAfterLast(); phoneCursor
                        .moveToNext()) {
Daisuke Miyakawa's avatar
Daisuke Miyakawa committed
250
                    String number = phoneCursor.getString(PHONE_NUMBER_COLUMN_INDEX);
251 252 253 254 255 256 257 258 259 260
                    if (TextUtils.isEmpty(number)) {
                        number = mContext.getString(R.string.defaultnumber);
                    }
                    numberList.add(number);
                }
            }
        } finally {
            if (phoneCursor != null) {
                phoneCursor.close();
            }
Jackson Fan's avatar
Jackson Fan committed
261
        }
262
        return numberList;
Jackson Fan's avatar
Jackson Fan committed
263 264
    }

Lixin Yue's avatar
Lixin Yue committed
265
    public final int composeAndSendCallLogVcards(final int type, Operation op,
266
            final int startPoint, final int endPoint, final boolean vcardType21) {
267 268 269 270
        if (startPoint < 1 || startPoint > endPoint) {
            Log.e(TAG, "internal error: startPoint or endPoint is not correct.");
            return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
        }
271
        String typeSelection = BluetoothPbapObexServer.createSelectionPara(type);
272

273
        final Uri myUri = CallLog.Calls.CONTENT_URI;
274 275 276 277 278 279 280 281 282 283 284
        final String[] CALLLOG_PROJECTION = new String[] {
            CallLog.Calls._ID, // 0
        };
        final int ID_COLUMN_INDEX = 0;

        Cursor callsCursor = null;
        long startPointId = 0;
        long endPointId = 0;
        try {
            // Need test to see if order by _ID is ok here, or by date?
            callsCursor = mResolver.query(myUri, CALLLOG_PROJECTION, typeSelection, null,
285
                    CALLLOG_SORT_ORDER);
286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303
            if (callsCursor != null) {
                callsCursor.moveToPosition(startPoint - 1);
                startPointId = callsCursor.getLong(ID_COLUMN_INDEX);
                if (V) Log.v(TAG, "Call Log query startPointId = " + startPointId);
                if (startPoint == endPoint) {
                    endPointId = startPointId;
                } else {
                    callsCursor.moveToPosition(endPoint - 1);
                    endPointId = callsCursor.getLong(ID_COLUMN_INDEX);
                }
                if (V) Log.v(TAG, "Call log query endPointId = " + endPointId);
            }
        } finally {
            if (callsCursor != null) {
                callsCursor.close();
            }
        }

304 305
        String recordSelection;
        if (startPoint == endPoint) {
306
            recordSelection = Calls._ID + "=" + startPointId;
307
        } else {
308 309 310 311
            // The query to call table is by "_id DESC" order, so change
            // correspondingly.
            recordSelection = Calls._ID + ">=" + endPointId + " AND " + Calls._ID + "<="
                    + startPointId;
Lixin Yue's avatar
Lixin Yue committed
312 313
        }

314 315 316 317 318
        String selection;
        if (typeSelection == null) {
            selection = recordSelection;
        } else {
            selection = "(" + typeSelection + ") AND (" + recordSelection + ")";
Lixin Yue's avatar
Lixin Yue committed
319 320
        }

321
        if (V) Log.v(TAG, "Call log query selection is: " + selection);
Lixin Yue's avatar
Lixin Yue committed
322

323
        return composeAndSendVCards(op, selection, vcardType21, null, false);
Lixin Yue's avatar
Lixin Yue committed
324 325
    }

Lixin Yue's avatar
Lixin Yue committed
326
    public final int composeAndSendPhonebookVcards(Operation op, final int startPoint,
327
            final int endPoint, final boolean vcardType21, String ownerVCard) {
328 329 330 331
        if (startPoint < 1 || startPoint > endPoint) {
            Log.e(TAG, "internal error: startPoint or endPoint is not correct.");
            return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
        }
332
        final Uri myUri = Contacts.CONTENT_URI;
333 334 335 336 337

        Cursor contactCursor = null;
        long startPointId = 0;
        long endPointId = 0;
        try {
338
            contactCursor = mResolver.query(myUri, CONTACTS_PROJECTION, CLAUSE_ONLY_VISIBLE, null,
339
                    Contacts._ID);
340 341
            if (contactCursor != null) {
                contactCursor.moveToPosition(startPoint - 1);
342
                startPointId = contactCursor.getLong(CONTACTS_ID_COLUMN_INDEX);
343 344 345 346 347
                if (V) Log.v(TAG, "Query startPointId = " + startPointId);
                if (startPoint == endPoint) {
                    endPointId = startPointId;
                } else {
                    contactCursor.moveToPosition(endPoint - 1);
348
                    endPointId = contactCursor.getLong(CONTACTS_ID_COLUMN_INDEX);
349 350 351 352 353 354 355 356 357
                }
                if (V) Log.v(TAG, "Query endPointId = " + endPointId);
            }
        } finally {
            if (contactCursor != null) {
                contactCursor.close();
            }
        }

358
        final String selection;
359
        if (startPoint == endPoint) {
360
            selection = Contacts._ID + "=" + startPointId + " AND " + CLAUSE_ONLY_VISIBLE;
361
        } else {
362
            selection = Contacts._ID + ">=" + startPointId + " AND " + Contacts._ID + "<="
363
                    + endPointId + " AND " + CLAUSE_ONLY_VISIBLE;
Lixin Yue's avatar
Lixin Yue committed
364 365
        }

366
        if (V) Log.v(TAG, "Query selection is: " + selection);
Lixin Yue's avatar
Lixin Yue committed
367

368
        return composeAndSendVCards(op, selection, vcardType21, ownerVCard, true);
Lixin Yue's avatar
Lixin Yue committed
369 370
    }

Lixin Yue's avatar
Lixin Yue committed
371
    public final int composeAndSendPhonebookOneVcard(Operation op, final int offset,
372 373 374 375 376 377 378 379 380 381 382
            final boolean vcardType21, String ownerVCard, int orderByWhat) {
        if (offset < 1) {
            Log.e(TAG, "Internal error: offset is not correct.");
            return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
        }
        final Uri myUri = Contacts.CONTENT_URI;
        Cursor contactCursor = null;
        String selection = null;
        long contactId = 0;
        if (orderByWhat == BluetoothPbapObexServer.ORDER_BY_INDEXED) {
            try {
383 384
                contactCursor = mResolver.query(myUri, CONTACTS_PROJECTION, CLAUSE_ONLY_VISIBLE,
                        null, Contacts._ID);
385 386 387 388 389 390 391 392 393 394 395 396
                if (contactCursor != null) {
                    contactCursor.moveToPosition(offset - 1);
                    contactId = contactCursor.getLong(CONTACTS_ID_COLUMN_INDEX);
                    if (V) Log.v(TAG, "Query startPointId = " + contactId);
                }
            } finally {
                if (contactCursor != null) {
                    contactCursor.close();
                }
            }
        } else if (orderByWhat == BluetoothPbapObexServer.ORDER_BY_ALPHABETICAL) {
            try {
397 398
                contactCursor = mResolver.query(myUri, CONTACTS_PROJECTION, CLAUSE_ONLY_VISIBLE,
                        null, Contacts.DISPLAY_NAME);
399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419
                if (contactCursor != null) {
                    contactCursor.moveToPosition(offset - 1);
                    contactId = contactCursor.getLong(CONTACTS_ID_COLUMN_INDEX);
                    if (V) Log.v(TAG, "Query startPointId = " + contactId);
                }
            } finally {
                if (contactCursor != null) {
                    contactCursor.close();
                }
            }
        } else {
            Log.e(TAG, "Parameter orderByWhat is not supported!");
            return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
        }
        selection = Contacts._ID + "=" + contactId;

        if (V) Log.v(TAG, "Query selection is: " + selection);

        return composeAndSendVCards(op, selection, vcardType21, ownerVCard, true);
    }

Lixin Yue's avatar
Lixin Yue committed
420
    public final int composeAndSendVCards(Operation op, final String selection,
421 422 423
            final boolean vcardType21, String ownerVCard, boolean isContacts) {
        long timestamp = 0;
        if (V) timestamp = System.currentTimeMillis();
Lixin Yue's avatar
Lixin Yue committed
424

425 426 427 428 429 430
        if (isContacts) {
            VCardComposer composer = null;
            try {
                // Currently only support Generic Vcard 2.1 and 3.0
                int vcardType;
                if (vcardType21) {
Daisuke Miyakawa's avatar
Daisuke Miyakawa committed
431
                    vcardType = VCardConfig.VCARD_TYPE_V21_GENERIC;
432
                } else {
Daisuke Miyakawa's avatar
Daisuke Miyakawa committed
433
                    vcardType = VCardConfig.VCARD_TYPE_V30_GENERIC;
434
                }
435
                vcardType |= VCardConfig.FLAG_REFRAIN_PHONE_NUMBER_FORMATTING;
Lixin Yue's avatar
Lixin Yue committed
436

437
                composer = new VCardComposer(mContext, vcardType, true);
Daisuke Miyakawa's avatar
Daisuke Miyakawa committed
438
                composer.addHandler(new HandlerForStringBuffer(op, ownerVCard));
439
                if (!composer.init(Contacts.CONTENT_URI, selection, null, null)) {
440
                    return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
Lixin Yue's avatar
Lixin Yue committed
441
                }
442 443

                while (!composer.isAfterLast()) {
Lixin Yue's avatar
Lixin Yue committed
444 445 446 447 448
                    if (BluetoothPbapObexServer.sIsAborted) {
                        ((ServerOperation)op).isAborted = true;
                        BluetoothPbapObexServer.sIsAborted = false;
                        break;
                    }
449 450 451 452 453 454 455 456 457 458
                    if (!composer.createOneEntry()) {
                        Log.e(TAG, "Failed to read a contact. Error reason: "
                                + composer.getErrorReason());
                        return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
                    }
                }
            } finally {
                if (composer != null) {
                    composer.terminate();
                }
Lixin Yue's avatar
Lixin Yue committed
459
            }
460 461 462 463 464 465 466 467 468 469
        } else { // CallLog
            BluetoothPbapCallLogComposer composer = null;
            try {
                composer = new BluetoothPbapCallLogComposer(mContext, true);
                composer.addHandler(new HandlerForStringBuffer(op, ownerVCard));
                if (!composer.init(CallLog.Calls.CONTENT_URI, selection, null, null)) {
                    return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
                }

                while (!composer.isAfterLast()) {
Lixin Yue's avatar
Lixin Yue committed
470 471 472 473 474
                    if (BluetoothPbapObexServer.sIsAborted) {
                        ((ServerOperation)op).isAborted = true;
                        BluetoothPbapObexServer.sIsAborted = false;
                        break;
                    }
475 476 477 478 479 480 481 482 483 484
                    if (!composer.createOneEntry()) {
                        Log.e(TAG, "Failed to read a contact. Error reason: "
                                + composer.getErrorReason());
                        return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
                    }
                }
            } finally {
                if (composer != null) {
                    composer.terminate();
                }
Lixin Yue's avatar
Lixin Yue committed
485 486 487
            }
        }

488 489 490 491
        if (V) Log.v(TAG, "Total vcard composing and sending out takes "
                    + (System.currentTimeMillis() - timestamp) + " ms");

        return ResponseCodes.OBEX_HTTP_OK;
Lixin Yue's avatar
Lixin Yue committed
492 493
    }

494 495 496 497 498 499
    /**
     * Handler to emit VCard String to PCE once size grow to maxPacketSize.
     */
    public class HandlerForStringBuffer implements OneEntryHandler {
        @SuppressWarnings("hiding")
        private Operation operation;
Lixin Yue's avatar
Lixin Yue committed
500

501 502 503 504 505 506 507 508 509 510 511 512 513 514
        private OutputStream outputStream;

        private int maxPacketSize;

        private String phoneOwnVCard = null;

        public HandlerForStringBuffer(Operation op, String ownerVCard) {
            operation = op;
            maxPacketSize = operation.getMaxPacketSize();
            if (V) Log.v(TAG, "getMaxPacketSize() = " + maxPacketSize);
            if (ownerVCard != null) {
                phoneOwnVCard = ownerVCard;
                if (V) Log.v(TAG, "phone own number vcard:");
                if (V) Log.v(TAG, phoneOwnVCard);
Jackson Fan's avatar
Jackson Fan committed
515 516 517
            }
        }

518 519 520 521 522 523
        public boolean onInit(Context context) {
            try {
                outputStream = operation.openOutputStream();
                mVcardResults = new StringBuilder();
                if (phoneOwnVCard != null) {
                    mVcardResults.append(phoneOwnVCard);
Jackson Fan's avatar
Jackson Fan committed
524
                }
525 526 527
            } catch (IOException e) {
                Log.e(TAG, "open outputstrem failed" + e.toString());
                return false;
Jackson Fan's avatar
Jackson Fan committed
528
            }
529 530
            if (V) Log.v(TAG, "openOutputStream() ok.");
            return true;
Jackson Fan's avatar
Jackson Fan committed
531 532
        }

533 534 535 536 537 538 539 540 541 542 543 544 545
        public boolean onEntryCreated(String vcard) {
            int vcardLen = vcard.length();
            if (V) Log.v(TAG, "The length of this vcard is: " + vcardLen);

            mVcardResults.append(vcard);
            int vcardStringLen = mVcardResults.toString().length();
            if (V) Log.v(TAG, "The length of this vcardResults is: " + vcardStringLen);

            if (vcardStringLen >= maxPacketSize) {
                long timestamp = 0;
                int position = 0;

                // Need while loop to handle the big vcard case
Lixin Yue's avatar
Lixin Yue committed
546 547
                while (!BluetoothPbapObexServer.sIsAborted
                        && position < (vcardStringLen - maxPacketSize)) {
548 549 550 551 552 553 554 555 556 557 558 559 560 561
                    if (V) timestamp = System.currentTimeMillis();

                    String subStr = mVcardResults.toString().substring(position,
                            position + maxPacketSize);
                    try {
                        outputStream.write(subStr.getBytes(), 0, maxPacketSize);
                    } catch (IOException e) {
                        Log.e(TAG, "write outputstrem failed" + e.toString());
                        return false;
                    }
                    if (V) Log.v(TAG, "Sending vcard String " + maxPacketSize + " bytes took "
                            + (System.currentTimeMillis() - timestamp) + " ms");

                    position += maxPacketSize;
Jackson Fan's avatar
Jackson Fan committed
562
                }
563
                mVcardResults.delete(0, position);
Jackson Fan's avatar
Jackson Fan committed
564
            }
565
            return true;
Jackson Fan's avatar
Jackson Fan committed
566 567
        }

568 569 570 571 572 573 574
        public void onTerminate() {
            // Send out last packet
            String lastStr = mVcardResults.toString();
            try {
                outputStream.write(lastStr.getBytes(), 0, lastStr.length());
            } catch (IOException e) {
                Log.e(TAG, "write outputstrem failed" + e.toString());
Jackson Fan's avatar
Jackson Fan committed
575
            }
576
            if (V) Log.v(TAG, "Last packet sent out, sending process complete!");
Jackson Fan's avatar
Jackson Fan committed
577

578 579
            if (!BluetoothPbapObexServer.closeStream(outputStream, operation)) {
                if (V) Log.v(TAG, "CloseStream failed!");
Jackson Fan's avatar
Jackson Fan committed
580
            } else {
581
                if (V) Log.v(TAG, "CloseStream ok!");
Jackson Fan's avatar
Jackson Fan committed
582 583 584 585
            }
        }
    }
}