BluetoothPbapObexServer.java revision 2c282d5898ac0916470ebfa9ff26ba784cf4bb24
1
2package com.android.bluetooth.pbap;
3
4import android.os.Message;
5import android.os.Handler;
6import android.util.Log;
7
8import java.io.IOException;
9import java.io.OutputStream;
10import java.io.PrintWriter;
11import java.util.ArrayList;
12
13import javax.obex.ServerRequestHandler;
14import javax.obex.ResponseCodes;
15import javax.obex.ApplicationParameter;
16import javax.obex.Operation;
17import javax.obex.HeaderSet;
18
19public class BluetoothPbapObexServer extends ServerRequestHandler {
20
21    private static final String TAG = "BluetoothPbapObexServer";
22
23    private static final int UUID_LENGTH = 16;
24
25    private static final int LEAGAL_PATH_NUM = 10;
26
27    private static final int VCARD_NAME_MIN_LEN = 5;
28
29    private long mConnectionId;
30
31    private Handler mCallback = null;
32
33    // 128 bit UUID for PBAP
34    private static final byte[] PBAP_TARGET = new byte[] {
35            0x79, 0x61, 0x35, (byte)0xf0, (byte)0xf0, (byte)0xc5, 0x11, (byte)0xd8, 0x09, 0x66,
36            0x08, 0x00, 0x20, 0x0c, (byte)0x9a, 0x66
37    };
38
39    private static final String[] LEAGEL_PATH = {
40            "/telecom", "/telecom/pb", "/telecom/ich", "/telecom/och", "/telecom/mch",
41            "/telecom/cch", "/SIM1", "/SIM1/telecom", "/SIM1/telecom/ich", "/SIM1/telecom/och",
42            "/SIM1/telecom/mch", "/SIM1/telecom/cch", "/SIM1/telecom/pb"
43    };
44
45    // missed call history
46    private static final String MCH = "mch";
47
48    // incoming call history
49    private static final String ICH = "ich";
50
51    // outgoing call history
52    private static final String OCH = "och";
53
54    // combined call history
55    private static final String CCH = "cch";
56
57    // phone book
58    private static final String PB = "pb";
59
60    private static final String ICH_PATH = "/telecom/ich";
61
62    private static final String OCH_PATH = "/telecom/och";
63
64    private static final String MCH_PATH = "/telecom/mch";
65
66    private static final String CCH_PATH = "/telecom/cch";
67
68    private static final String PB_PATH = "/telecom/pb";
69
70    // type for list vcard objects
71    private static final String TYPE_LISTING = "x-bt/vcard-listing";
72
73    // type for get single vcard object
74    private static final String TYPE_VCARD = "x-bt/vcard";
75
76    // type for download all vcard objects
77    private static final String TYPE_PB = "x-bt/phonebook";
78
79    // record current path the client are browsing
80    private String mCurrentPath = "";
81
82    // record how many missed call happens since last client operation
83    private int mMissedCallSize = 0;
84
85    private static final int NEED_PB_SIZE = 0x01;
86
87    private static final int NEED_NEW_MISSED_CALL_NUMBER = 0x02;
88
89    public static final int NEED_PHONEBOOK = 0x04;
90
91    public static final int NEED_INCOMING_CALL_NUMBER = 0x08;
92
93    public static final int NEED_OUTGOING_CALL_NUMBER = 0x10;
94
95    public static final int NEED_MISSED_CALL_NUMBER = 0x20;
96
97    public static final int NEED_COMBINED_CALL_NUMBER = 0x40;
98
99    private static final int PRECONDITION_FAILED = -1;
100
101    private static final int INTERNAL_ERROR = -2;
102
103    public BluetoothPbapObexServer(Handler callback) {
104        super();
105        mConnectionId = -1;
106        mCallback = callback;
107    }
108
109    @Override
110    public int onConnect(final HeaderSet request, final HeaderSet reply) {
111        try {
112            byte[] uuid_tmp = (byte[])request.getHeader(HeaderSet.TARGET);
113            if (uuid_tmp.length != UUID_LENGTH) {
114                Log.w(TAG, "Wrong UUID length");
115                return ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE;
116            }
117            for (int i = 0; i < UUID_LENGTH; i++) {
118                if (uuid_tmp[i] != PBAP_TARGET[i]) {
119                    Log.w(TAG, "Wrong UUID");
120                    return ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE;
121                }
122            }
123        } catch (IOException e) {
124            Log.e(TAG, e.toString());
125            return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
126        }
127
128        Message msg = Message.obtain(mCallback);
129        msg.what = BluetoothPbapService.MSG_SESSION_ESTABLISHED;
130        msg.sendToTarget();
131
132        return ResponseCodes.OBEX_HTTP_OK;
133    }
134
135    @Override
136    public void onDisconnect(final HeaderSet req, final HeaderSet resp) {
137        resp.responseCode = ResponseCodes.OBEX_HTTP_OK;
138        if (mCallback != null) {
139            Message msg = Message.obtain(mCallback);
140            msg.what = BluetoothPbapService.MSG_SESSION_DISCONNECTED;
141            msg.sendToTarget();
142        }
143    }
144
145    @Override
146    public int onPut(final Operation op) {
147        return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
148    }
149
150    @Override
151    public int onSetPath(final HeaderSet request, final HeaderSet reply, final boolean backup,
152            final boolean create) {
153        String current_path_tmp = mCurrentPath;
154        String tmp_path = null;
155        try {
156            tmp_path = (String)request.getHeader(HeaderSet.NAME);
157        } catch (IOException e) {
158            Log.e(TAG, "Get name header fail");
159            return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
160        }
161        if (BluetoothPbapService.DBG) {
162            Log.d(TAG, "backup=" + backup + " create=" + create);
163        }
164        if (!backup && create) {
165            if (tmp_path == null) {
166                current_path_tmp = "";
167            } else {
168                current_path_tmp = current_path_tmp + "/" + tmp_path;
169            }
170        }
171
172        if (backup && create) {
173            if (current_path_tmp.length() != 0) {
174                current_path_tmp = current_path_tmp.substring(0, current_path_tmp.lastIndexOf("/"));
175            }
176        }
177
178        if ((current_path_tmp.length() != 0) && (!isLegalPath(current_path_tmp))) {
179            Log.w(TAG, "path is not legal");
180            return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
181        }
182        mCurrentPath = current_path_tmp;
183        return ResponseCodes.OBEX_HTTP_OK;
184    }
185
186    @Override
187    public void onClose() {
188        if (mCallback != null) {
189            Message msg = Message.obtain(mCallback);
190            msg.what = BluetoothPbapService.MSG_SERVERSESSION_CLOSE;
191            msg.sendToTarget();
192        }
193    }
194
195    @Override
196    public int onGet(final Operation op) {
197        HeaderSet request = null;
198        HeaderSet reply = new HeaderSet();
199        String type = "";
200        String name = "";
201        byte[] appParam = null;
202        AppParamValue appParamValue = new AppParamValue();
203        try {
204            request = op.getReceivedHeader();
205            type = (String)request.getHeader(HeaderSet.TYPE);
206            name = (String)request.getHeader(HeaderSet.NAME);
207            appParam = (byte[])request.getHeader(HeaderSet.APPLICATION_PARAMETER);
208        } catch (IOException e) {
209            Log.e(TAG, "request headers error");
210            return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
211        }
212        // Accroding to specification,the name header could be omitted such as
213        // sony erriccsonHBH-DS980
214        if (BluetoothPbapService.DBG) {
215            Log.d(TAG, "OnGet type is " + type + " name is " + name);
216        }
217        if (name == null) {
218            if (BluetoothPbapService.DBG) {
219                Log.i(TAG, "name =null,guess what carkit actually want from current path");
220            }
221            if (mCurrentPath.compareTo(PB_PATH) == 0) {
222                appParamValue.needTag |= NEED_PHONEBOOK;
223            }
224            if (mCurrentPath.compareTo(ICH_PATH) == 0) {
225                appParamValue.needTag |= NEED_INCOMING_CALL_NUMBER;
226            }
227            if (mCurrentPath.compareTo(OCH_PATH) == 0) {
228                appParamValue.needTag |= NEED_OUTGOING_CALL_NUMBER;
229            }
230            if (mCurrentPath.compareTo(CCH_PATH) == 0) {
231                appParamValue.needTag |= NEED_COMBINED_CALL_NUMBER;
232            }
233            if (mCurrentPath.compareTo(MCH_PATH) == 0) {
234                appParamValue.needTag |= NEED_MISSED_CALL_NUMBER;
235            }
236        } else {// we have weak name checking here to provide better
237            // compatibility with other devices,although unique name such as
238            // "pb.vcf" is required by SIG spec.
239            if (name.contains(PB.subSequence(0, PB.length()))) {
240                appParamValue.needTag |= NEED_PHONEBOOK;
241                if (BluetoothPbapService.DBG) {
242                    Log.v(TAG, "download phonebook request");
243                }
244            }
245            if (name.contains(ICH.subSequence(0, ICH.length()))) {
246                appParamValue.needTag |= NEED_INCOMING_CALL_NUMBER;
247                if (BluetoothPbapService.DBG) {
248                    Log.v(TAG, "download incoming calls request");
249                }
250            }
251            if (name.contains(OCH.subSequence(0, OCH.length()))) {
252                appParamValue.needTag |= NEED_OUTGOING_CALL_NUMBER;
253                if (BluetoothPbapService.DBG) {
254                    Log.v(TAG, "download outgoing calls request");
255                }
256            }
257            if (name.contains(CCH.subSequence(0, CCH.length()))) {
258                appParamValue.needTag |= NEED_COMBINED_CALL_NUMBER;
259                if (BluetoothPbapService.DBG) {
260                    Log.v(TAG, "download combined calls request");
261                }
262            }
263            if (name.contains(MCH.subSequence(0, MCH.length()))) {
264                appParamValue.needTag |= NEED_MISSED_CALL_NUMBER;
265                if (BluetoothPbapService.DBG) {
266                    Log.v(TAG, "download missed calls request");
267                }
268            }
269        }
270        // listing request
271        if (type.equals(TYPE_LISTING)) {
272            return pullVcardListing(appParam, appParamValue, reply, op);
273        }
274        // pull vcard entry request
275        else if (type.equals(TYPE_VCARD)) {
276            return pullVcardEntry(appParam, appParamValue, op, name, mCurrentPath);
277        }
278        // down load phone book request
279        else if (type.equals(TYPE_PB)) {
280            return pullPhonebook(appParam, appParamValue, reply, op, name);
281        } else {
282            Log.w(TAG, "unknown type request!!!");
283            return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
284        }
285    } // end of onGet()
286
287    /** check whether path is legal */
288    private final boolean isLegalPath(final String str) {
289        if (str.length() == 0) {
290            return true;
291        }
292        for (int i = 0; i < LEAGAL_PATH_NUM; i++) {
293            if (str.equals(LEAGEL_PATH[i])) {
294                return true;
295            }
296        }
297        return false;
298    }
299
300    private class AppParamValue {
301        public int maxListCount;
302
303        public int listStartOffset;
304
305        public String searchValue;
306
307        public String searchAttr;
308
309        public int needTag;
310
311        public boolean vcard21;
312
313        public AppParamValue() {
314            maxListCount = 0;
315            listStartOffset = 0;
316            searchValue = "";
317            searchAttr = "";
318            needTag = 0x00;
319            vcard21 = true;
320        }
321
322        public void dump() {
323            Log.i(TAG, "maxListCount=" + maxListCount + " listStartOffset=" + listStartOffset
324                    + " searchValue=" + searchValue + " searchAttr=" + searchAttr + " needTag="
325                    + needTag + " vcard21=" + vcard21);
326        }
327    }
328
329    /** To parse obex application parameter */
330    private final boolean parseApplicationParameter(final byte[] appParam,
331            AppParamValue appParamValue) {
332        int i = 0;
333        boolean badRequest = true;
334        while (i < appParam.length) {
335            switch (appParam[i]) {
336                case ApplicationParameter.TRIPLET_TAGID.FILTER_TAGID:
337                    i += 2; // length and tag field in triplet
338                    i += ApplicationParameter.TRIPLET_LENGTH.FILTER_LENGTH;
339                    break;
340                case ApplicationParameter.TRIPLET_TAGID.ORDER_TAGID:
341                    i += 2; // length and tag field in triplet
342                    i += ApplicationParameter.TRIPLET_LENGTH.ORDER_LENGTH;
343                    break;
344                case ApplicationParameter.TRIPLET_TAGID.SEARCH_VALUE_TAGID:
345                    i += 1; // length field in triplet
346                    for (int k = 1; k <= appParam[i]; k++) {
347                        appParamValue.searchValue += Byte.toString(appParam[i + k]);
348                    }
349                    // length of search value is variable
350                    i += appParam[i];
351                    i += 1;
352                    break;
353                case ApplicationParameter.TRIPLET_TAGID.SEARCH_ATTRIBUTE_TAGID:
354                    i += 2;
355                    appParamValue.searchAttr = Byte.toString(appParam[i]);
356                    i += ApplicationParameter.TRIPLET_LENGTH.SEARCH_ATTRIBUTE_LENGTH;
357                    break;
358                case ApplicationParameter.TRIPLET_TAGID.MAXLISTCOUNT_TAGID:
359                    i += 2;
360                    if (appParam[i] == 0 && appParam[i + 1] == 0) {
361                        appParamValue.needTag |= NEED_PB_SIZE;
362                    } else {
363                        int highValue = appParam[i] & 0xff;
364                        int lowValue = appParam[i + 1] & 0xff;
365                        appParamValue.maxListCount = highValue * 256 + lowValue;
366                    }
367                    i += ApplicationParameter.TRIPLET_LENGTH.MAXLISTCOUNT_LENGTH;
368                    break;
369                case ApplicationParameter.TRIPLET_TAGID.LISTSTARTOFFSET_TAGID:
370                    i += 2;
371                    if (appParam[i] == 0 && appParam[i + 1] == 0) {
372                        appParamValue.listStartOffset = 0;
373                    } else {
374                        int highValue = appParam[i] & 0xff;
375                        int lowValue = appParam[i + 1] & 0xff;
376                        appParamValue.listStartOffset = highValue * 256 + lowValue;
377                    }
378                    i += ApplicationParameter.TRIPLET_LENGTH.LISTSTARTOFFSET_LENGTH;
379                    break;
380                case ApplicationParameter.TRIPLET_TAGID.PHONEBOOKSIZE_TAGID:
381                    i += 2;// length field in triplet
382                    i += ApplicationParameter.TRIPLET_LENGTH.PHONEBOOKSIZE_LENGTH;
383                    appParamValue.needTag |= NEED_PB_SIZE;
384                    break;
385                case ApplicationParameter.TRIPLET_TAGID.NEWMISSEDCALLS_TAGID:
386                    i += 1;
387                    appParamValue.needTag |= NEED_NEW_MISSED_CALL_NUMBER;
388                    i += ApplicationParameter.TRIPLET_LENGTH.NEWMISSEDCALLS_LENGTH;
389                    break;
390                case ApplicationParameter.TRIPLET_TAGID.FORMAT_TAGID:
391                    i += 2;// length field in triplet
392                    if (Byte.toString(appParam[i]).compareTo("0") != 0) {
393                        appParamValue.vcard21 = false;
394                    }
395                    i += ApplicationParameter.TRIPLET_LENGTH.FORMAT_LENGTH;
396                    break;
397                default:
398                    badRequest = false;
399                    break;
400            }
401        }
402        return badRequest;
403    }
404
405    /** Form and Send an XML format String to client for Phone book listing */
406    private final int createVcardListingXml(final int type, final Operation op,
407            final int maxListCount, final int listStartOffset, final String searchValue,
408            String searchAttr) {
409        OutputStream out = null;
410        StringBuilder result = new StringBuilder();
411        int itemsFound = 0;
412        result.append("<?xml version=\"1.0\"?>");
413        result.append("<!DOCTYPE vcard-listing SYSTEM \"vcard-listing.dtd\">");
414        result.append("<vCard-listing version=\"1.0\">");
415
416        // Phonebook listing request
417        if (type == NEED_PHONEBOOK) {
418            // if searchAttr is not set by client,make searchAttr by name
419            // as default;
420            if (searchAttr == null || searchAttr.trim().length() == 0) {
421                if (BluetoothPbapService.DBG) {
422                    Log.i(TAG, "searchAttr is not set, assume search by name");
423                }
424                searchAttr = "0";
425            }
426            // begin of search by name
427            if (searchAttr.compareTo("0") == 0) {
428                ArrayList<String> nameList = BluetoothPbapService.getPhonebookNameList();
429                int size = nameList.size() >= maxListCount ? maxListCount : nameList.size();
430                if (BluetoothPbapService.DBG) {
431                    Log.d(TAG, "search by name, size=" + size + " offset=" + listStartOffset
432                            + " searchValue=" + searchValue);
433                }
434                // if searchValue if not set by client,provide the entire
435                // list by name
436                if (searchValue == null || searchValue.trim().length() == 0) {
437                    for (int j = listStartOffset, i = 0; i < size; i++, j++) {
438                        result.append("<card handle=\"" + j + ".vcf\" name=\"" + nameList.get(j)
439                                + "\"" + "/>");
440                        itemsFound++;
441                    }
442                } else {
443                    for (int j = listStartOffset, i = 0; i < size; i++, j++) {
444                        // only find the name which begins with the searchValue
445                        if (nameList.get(j).indexOf(searchValue) == 0) {
446                            itemsFound++;
447                            result.append("<card handle=\"" + j + ".vcf\" name=\""
448                                    + nameList.get(j) + "\"" + "/>");
449                        }
450                    }
451                }
452            }// end of search by name
453            // begin of search by number
454            else if (searchAttr.compareTo("1") == 0) {
455                ArrayList<String> numberList = BluetoothPbapService.getPhonebookNumberList();
456                int size = numberList.size() >= maxListCount ? maxListCount : numberList.size();
457                if (BluetoothPbapService.DBG) {
458                    Log.d(TAG, "search by number, size=" + size + " offset=" + listStartOffset
459                            + " searchValue=" + searchValue);
460                }
461                // if searchValue if not set by client,provide the entire
462                // list by number
463                if (searchValue == null || searchValue.trim().length() == 0) {
464                    for (int j = listStartOffset, i = 0; i < size; i++, j++) {
465                        result.append("<card handle=\"" + j + ".vcf\" number=\""
466                                + numberList.get(j) + "\"" + "/>");
467                        itemsFound++;
468                    }
469                } else {
470                    for (int j = listStartOffset, i = 0; i < size; i++, j++) {
471                        // only find the name which begins with the searchValue
472                        if (numberList.get(j).indexOf(searchValue.trim()) == 0) {
473                            itemsFound++;
474                            result.append("<card handle=\"" + j + ".vcf\" number=\""
475                                    + numberList.get(j) + "\"" + "/>");
476                        }
477                    }
478                }
479            }// end of search by number
480            else {
481                return PRECONDITION_FAILED;
482            }
483        }
484        // Call history listing request
485        else {
486            ArrayList<String> nameList = BluetoothPbapService.getCallLogList(type);
487            int size = nameList.size() >= maxListCount ? maxListCount : nameList.size();
488            if (BluetoothPbapService.DBG) {
489                Log.d(TAG, "call log list, size=" + size + " offset=" + listStartOffset);
490            }
491            // listing object begin with 1.vcf
492            for (int j = listStartOffset + 1, i = 0; i < size; i++, j++) {
493                result.append("<card handle=\"" + j + ".vcf\" name=\"" + nameList.get(j - 1) + "\""
494                        + "/>");
495                itemsFound++;
496            }
497        }
498        result.append("</vCard-listing>");
499        try {
500            out = op.openOutputStream();
501            out.write(result.toString().getBytes());
502            out.flush();
503        } catch (IOException e) {
504            Log.e(TAG, "output stream fail " + e.toString());
505            itemsFound = INTERNAL_ERROR;
506        } finally {
507            if (!closeStream(out, op)) {
508                itemsFound = INTERNAL_ERROR;
509            }
510        }
511
512        if (BluetoothPbapService.DBG) {
513            Log.d(TAG, "itemsFound =" + itemsFound);
514        }
515        return itemsFound;
516    }
517
518    /**
519     * Function to send obex header back to client such as get phonebook size
520     * request
521     */
522    private final int pushHeader(final Operation op, final HeaderSet reply) {
523        OutputStream outputStream = null;
524        int ret = ResponseCodes.OBEX_HTTP_OK;
525        try {
526            op.sendHeaders(reply);
527            outputStream = op.openOutputStream();
528            outputStream.flush();
529        } catch (IOException e) {
530            Log.e(TAG, e.toString());
531            ret = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
532        } finally {
533            if (!closeStream(outputStream, op)) {
534                ret = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
535            }
536        }
537        return ret;
538    }
539
540    /** Function to send vcard data to client */
541    private final int pushBytes(final Operation op, final String string) {
542        if (string == null) {
543            return ResponseCodes.OBEX_HTTP_OK;
544        }
545        byte[] filebytes;
546        int ret = ResponseCodes.OBEX_HTTP_OK;
547        OutputStream outputStream = null;
548        if (BluetoothPbapService.DBG) {
549            Log.d(TAG, "Send Data");
550            Log.d(TAG, string);
551        }
552        try {
553            outputStream = op.openOutputStream();
554            filebytes = string.getBytes();
555            outputStream.write(filebytes);
556        } catch (IOException e) {
557            Log.e(TAG, "open outputstrem failed" + e.toString());
558            ret = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
559        } finally {
560            if (!closeStream(outputStream, op)) {
561                ret = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
562            }
563        }
564        return ret;
565    }
566
567    private final int pullVcardListing(byte[] appParam, AppParamValue appParamValue,
568            HeaderSet reply, final Operation op) {
569        String searchAttr = appParamValue.searchAttr.trim();
570        if (!parseApplicationParameter(appParam, appParamValue)) {
571            return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
572        }
573        if (BluetoothPbapService.DBG) {
574            appParamValue.dump();
575        }
576        if (searchAttr == null || searchAttr.length() == 0) {
577            return ResponseCodes.OBEX_HTTP_PRECON_FAILED;
578        }
579        if (searchAttr.compareTo("0") != 0 && searchAttr.compareTo("1") != 0) {
580            Log.w(TAG, "search attr not supported");
581            if (searchAttr.compareTo("2") == 0) {
582                // search by sound is not supported currently
583                Log.w(TAG, "do not support search by sound");
584                return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED;
585            }
586            return ResponseCodes.OBEX_HTTP_PRECON_FAILED;
587        }
588
589        ApplicationParameter ap = new ApplicationParameter();
590        if ((appParamValue.needTag & NEED_PB_SIZE) == NEED_PB_SIZE) {
591            int size = 0;
592            byte[] pbsize = new byte[2];
593            if ((appParamValue.needTag & NEED_PHONEBOOK) == NEED_PHONEBOOK) {
594                size = BluetoothPbapService.getPhonebookSize(NEED_PHONEBOOK);
595            } else if ((appParamValue.needTag & NEED_INCOMING_CALL_NUMBER) == NEED_INCOMING_CALL_NUMBER) {
596                size = BluetoothPbapService.getPhonebookSize(NEED_INCOMING_CALL_NUMBER);
597            } else if ((appParamValue.needTag & NEED_OUTGOING_CALL_NUMBER) == NEED_OUTGOING_CALL_NUMBER) {
598                size = BluetoothPbapService.getPhonebookSize(NEED_OUTGOING_CALL_NUMBER);
599            } else if ((appParamValue.needTag & NEED_COMBINED_CALL_NUMBER) == NEED_COMBINED_CALL_NUMBER) {
600                size = BluetoothPbapService.getPhonebookSize(NEED_COMBINED_CALL_NUMBER);
601            } else if ((appParamValue.needTag & NEED_MISSED_CALL_NUMBER) == NEED_MISSED_CALL_NUMBER) {
602                size = BluetoothPbapService.getPhonebookSize(NEED_MISSED_CALL_NUMBER);
603                mMissedCallSize = size;
604            }
605            pbsize[0] = (byte)((size / 256) & 0xff);// HIGH VALUE
606            pbsize[1] = (byte)((size % 256) & 0xff);// LOW VALUE
607            ap.addAPPHeader(ApplicationParameter.TRIPLET_TAGID.PHONEBOOKSIZE_TAGID,
608                    ApplicationParameter.TRIPLET_LENGTH.PHONEBOOKSIZE_LENGTH, pbsize);
609            reply.setHeader(HeaderSet.APPLICATION_PARAMETER, ap.getAPPparam());
610            return pushHeader(op, reply);
611        }
612
613        if ((appParamValue.needTag & NEED_NEW_MISSED_CALL_NUMBER) == NEED_NEW_MISSED_CALL_NUMBER) {
614            byte[] misnum = new byte[1];
615            int nmnum = BluetoothPbapService.getPhonebookSize(NEED_MISSED_CALL_NUMBER)
616                    - mMissedCallSize;
617            nmnum = nmnum > 0 ? nmnum : 0;
618            misnum[0] = (byte)nmnum;
619            ap.addAPPHeader(ApplicationParameter.TRIPLET_TAGID.NEWMISSEDCALLS_TAGID,
620                    ApplicationParameter.TRIPLET_LENGTH.NEWMISSEDCALLS_LENGTH, misnum);
621            reply.setHeader(HeaderSet.APPLICATION_PARAMETER, ap.getAPPparam());
622            return pushHeader(op, reply);
623        }
624
625        else if ((appParamValue.needTag & NEED_PB_SIZE) == 0
626                && ((appParamValue.needTag & NEED_PHONEBOOK) == NEED_PHONEBOOK)) {
627            if (createVcardListingXml(NEED_PHONEBOOK, op, appParamValue.maxListCount,
628                    appParamValue.listStartOffset, appParamValue.searchValue,
629                    appParamValue.searchAttr) <= 0) {
630                return ResponseCodes.OBEX_HTTP_NOT_FOUND;
631            }
632            return ResponseCodes.OBEX_HTTP_OK;
633        }
634
635        else if ((appParamValue.needTag & NEED_PB_SIZE) == 0
636                && ((appParamValue.needTag & NEED_INCOMING_CALL_NUMBER) == NEED_INCOMING_CALL_NUMBER)) {
637            if (createVcardListingXml(NEED_INCOMING_CALL_NUMBER, op, appParamValue.maxListCount,
638                    appParamValue.listStartOffset, null, null) <= 0) {
639                return ResponseCodes.OBEX_HTTP_NOT_FOUND;
640            }
641            return ResponseCodes.OBEX_HTTP_OK;
642        }
643
644        else if ((appParamValue.needTag & NEED_PB_SIZE) == 0
645                && ((appParamValue.needTag & NEED_OUTGOING_CALL_NUMBER) == NEED_OUTGOING_CALL_NUMBER)) {
646            if (createVcardListingXml(NEED_OUTGOING_CALL_NUMBER, op, appParamValue.maxListCount,
647                    appParamValue.listStartOffset, null, null) <= 0) {
648                return ResponseCodes.OBEX_HTTP_NOT_FOUND;
649            }
650            return ResponseCodes.OBEX_HTTP_OK;
651        }
652
653        else if ((appParamValue.needTag & NEED_PB_SIZE) == 0
654                && ((appParamValue.needTag & NEED_COMBINED_CALL_NUMBER) == NEED_COMBINED_CALL_NUMBER)) {
655            if (createVcardListingXml(NEED_COMBINED_CALL_NUMBER, op, appParamValue.maxListCount,
656                    appParamValue.listStartOffset, null, null) <= 0) {
657                return ResponseCodes.OBEX_HTTP_NOT_FOUND;
658            }
659            return ResponseCodes.OBEX_HTTP_OK;
660        }
661
662        else if ((appParamValue.needTag & NEED_PB_SIZE) == 0
663                && ((appParamValue.needTag & NEED_MISSED_CALL_NUMBER) == NEED_MISSED_CALL_NUMBER)) {
664            if (createVcardListingXml(NEED_MISSED_CALL_NUMBER, op, appParamValue.maxListCount,
665                    appParamValue.listStartOffset, null, null) <= 0) {
666                return ResponseCodes.OBEX_HTTP_NOT_FOUND;
667            }
668            return ResponseCodes.OBEX_HTTP_OK;
669        }
670        return ResponseCodes.OBEX_HTTP_OK;
671    }
672
673    private final int pullVcardEntry(byte[] appParam, AppParamValue appParamValue,
674            final Operation op, final String name, final String current_path) {
675        boolean vcard21 = true;
676        if (!parseApplicationParameter(appParam, appParamValue))
677            return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
678        String str = "";
679        String strIndex = "";
680        int intIndex = 0;
681        if (name == null || name.length() < VCARD_NAME_MIN_LEN) {
682            return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
683        }
684        strIndex = name.substring(0, name.length() - VCARD_NAME_MIN_LEN + 1);
685        if (strIndex.trim().length() != 0) {
686            try {
687                intIndex = Integer.parseInt(strIndex);
688            } catch (NumberFormatException e) {
689                Log.e(TAG, "catch number format exception " + e.toString());
690                return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
691            }
692        }
693        if (current_path.compareTo(PB_PATH) == 0) {
694            if (intIndex < 0 || intIndex >= BluetoothPbapService.getPhonebookSize(NEED_PHONEBOOK))
695                return ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE;
696            if (intIndex >= 0)
697                str = BluetoothPbapService.getPhonebook(NEED_PHONEBOOK, intIndex, vcard21);
698        } else if (current_path.compareTo(ICH_PATH) == 0) {
699            if (intIndex <= 0
700                    || intIndex > BluetoothPbapService.getPhonebookSize(NEED_INCOMING_CALL_NUMBER))
701                return ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE;
702            if (intIndex >= 1)
703                str = BluetoothPbapService.getPhonebook(NEED_INCOMING_CALL_NUMBER, intIndex - 1,
704                        vcard21);
705        } else if (current_path.compareTo(OCH_PATH) == 0) {
706            if (intIndex <= 0
707                    || intIndex > BluetoothPbapService.getPhonebookSize(NEED_OUTGOING_CALL_NUMBER))
708                return ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE;
709            if (intIndex >= 1)
710                str = BluetoothPbapService.getPhonebook(NEED_OUTGOING_CALL_NUMBER, intIndex - 1,
711                        vcard21);
712        } else if (current_path.compareTo(CCH_PATH) == 0) {
713            if (intIndex == 0)
714                return ResponseCodes.OBEX_HTTP_OK;
715            if (intIndex < 0
716                    || intIndex > BluetoothPbapService.getPhonebookSize(NEED_COMBINED_CALL_NUMBER))
717                return ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE;
718            if (intIndex >= 1)
719                str = BluetoothPbapService.getPhonebook(NEED_COMBINED_CALL_NUMBER, intIndex - 1,
720                        vcard21);
721        } else if (current_path.compareTo(MCH_PATH) == 0) {
722            if (intIndex <= 0
723                    || intIndex > BluetoothPbapService.getPhonebookSize(NEED_MISSED_CALL_NUMBER))
724                return ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE;
725            if (intIndex >= 1)
726                str = BluetoothPbapService.getPhonebook(NEED_MISSED_CALL_NUMBER, intIndex - 1,
727                        vcard21);
728        } else {
729            Log.w(TAG, "wrong path!");
730            return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
731        }
732        return pushBytes(op, str);
733    }
734
735    private final int pullPhonebook(byte[] appParam, AppParamValue appParamValue, HeaderSet reply,
736            final Operation op, final String name) {
737        boolean vcard21 = true;
738        String str;
739        String strstr = "";
740        int pbSize = 0;
741        int size = 0;
742        int pos = 0;
743        // code start for passing PTS3.2 TC_PSE_PBD_BI_01_C
744        if (name != null) {
745            int dotIndex = name.indexOf(".");
746            String vcf = "vcf";
747            if (dotIndex >= 0 && dotIndex <= name.length()) {
748                if (name.regionMatches(dotIndex + 1, vcf, 0, vcf.length()) == false) {
749                    Log.w(TAG, "name is not .vcf");
750                    return ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE;
751                }
752            }
753        }
754        // code end for passing PTS3.2 TC_PSE_PBD_BI_01_C
755        if (!parseApplicationParameter(appParam, appParamValue)) {
756            return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
757        }
758        // check whether to send application response parameter to client
759        ApplicationParameter ap = new ApplicationParameter();
760        if ((appParamValue.needTag & NEED_PB_SIZE) == NEED_PB_SIZE) {
761            byte[] pbsize = new byte[2];
762            if ((appParamValue.needTag & NEED_PHONEBOOK) == NEED_PHONEBOOK) {
763                size = BluetoothPbapService.getPhonebookSize(NEED_PHONEBOOK);
764            } else if ((appParamValue.needTag & NEED_INCOMING_CALL_NUMBER) == NEED_INCOMING_CALL_NUMBER) {
765                size = BluetoothPbapService.getPhonebookSize(NEED_INCOMING_CALL_NUMBER);
766            } else if ((appParamValue.needTag & NEED_OUTGOING_CALL_NUMBER) == NEED_OUTGOING_CALL_NUMBER) {
767                size = BluetoothPbapService.getPhonebookSize(NEED_OUTGOING_CALL_NUMBER);
768            } else if ((appParamValue.needTag & NEED_COMBINED_CALL_NUMBER) == NEED_COMBINED_CALL_NUMBER) {
769                size = BluetoothPbapService.getPhonebookSize(NEED_COMBINED_CALL_NUMBER);
770            } else if ((appParamValue.needTag & NEED_MISSED_CALL_NUMBER) == NEED_MISSED_CALL_NUMBER) {
771                size = BluetoothPbapService.getPhonebookSize(NEED_MISSED_CALL_NUMBER);
772                mMissedCallSize = size;
773            }
774            pbsize[0] = (byte)((size / 256) & 0xff);// HIGH VALUE
775            pbsize[1] = (byte)((size % 256) & 0xff);// LOW VALUE
776            ap.addAPPHeader(ApplicationParameter.TRIPLET_TAGID.PHONEBOOKSIZE_TAGID,
777                    ApplicationParameter.TRIPLET_LENGTH.PHONEBOOKSIZE_LENGTH, pbsize);
778            reply.setHeader(HeaderSet.APPLICATION_PARAMETER, ap.getAPPparam());
779            return pushHeader(op, reply);
780        }
781
782        if ((appParamValue.needTag & NEED_NEW_MISSED_CALL_NUMBER) == NEED_NEW_MISSED_CALL_NUMBER) {
783            byte[] misnum = new byte[1];
784            int nmnum = BluetoothPbapService.getPhonebookSize(NEED_MISSED_CALL_NUMBER)
785                    - mMissedCallSize;
786            nmnum = nmnum > 0 ? nmnum : 0;
787            misnum[0] = (byte)nmnum;
788            ap.addAPPHeader(ApplicationParameter.TRIPLET_TAGID.NEWMISSEDCALLS_TAGID,
789                    ApplicationParameter.TRIPLET_LENGTH.NEWMISSEDCALLS_LENGTH, misnum);
790            reply.setHeader(HeaderSet.APPLICATION_PARAMETER, ap.getAPPparam());
791            return pushHeader(op, reply);
792        }
793
794        if ((appParamValue.needTag & NEED_PHONEBOOK) == NEED_PHONEBOOK) {
795            pbSize = BluetoothPbapService.getPhonebookSize(NEED_PHONEBOOK);
796            size = pbSize >= appParamValue.maxListCount ? appParamValue.maxListCount : pbSize;
797            for (pos = appParamValue.listStartOffset; pos < size; pos++) {
798                str = BluetoothPbapService.getPhonebook(NEED_PHONEBOOK, pos, vcard21);
799                strstr += str;
800            }
801        }
802
803        if ((appParamValue.needTag & NEED_INCOMING_CALL_NUMBER) == NEED_INCOMING_CALL_NUMBER) {
804            pbSize = BluetoothPbapService.getPhonebookSize(NEED_INCOMING_CALL_NUMBER);
805            size = pbSize >= appParamValue.maxListCount ? appParamValue.maxListCount : pbSize;
806            for (pos = appParamValue.listStartOffset; pos < size; pos++) {
807                str = BluetoothPbapService.getPhonebook(NEED_INCOMING_CALL_NUMBER, pos, vcard21);
808                strstr += str;
809            }
810        }
811
812        if ((appParamValue.needTag & NEED_OUTGOING_CALL_NUMBER) == NEED_OUTGOING_CALL_NUMBER) {
813            pbSize = BluetoothPbapService.getPhonebookSize(NEED_OUTGOING_CALL_NUMBER);
814            size = pbSize >= appParamValue.maxListCount ? appParamValue.maxListCount : pbSize;
815            for (pos = appParamValue.listStartOffset; pos < size; pos++) {
816                str = BluetoothPbapService.getPhonebook(NEED_OUTGOING_CALL_NUMBER, pos, vcard21);
817                strstr += str;
818            }
819        }
820
821        if ((appParamValue.needTag & NEED_COMBINED_CALL_NUMBER) == NEED_COMBINED_CALL_NUMBER) {
822            pbSize = BluetoothPbapService.getPhonebookSize(NEED_COMBINED_CALL_NUMBER);
823            size = pbSize >= appParamValue.maxListCount ? appParamValue.maxListCount : pbSize;
824            for (pos = appParamValue.listStartOffset; pos < size; pos++) {
825                str = BluetoothPbapService.getPhonebook(NEED_COMBINED_CALL_NUMBER, pos, vcard21);
826                strstr += str;
827            }
828        }
829
830        if ((appParamValue.needTag & NEED_MISSED_CALL_NUMBER) == NEED_MISSED_CALL_NUMBER) {
831            pbSize = BluetoothPbapService.getPhonebookSize(NEED_MISSED_CALL_NUMBER);
832            size = pbSize >= appParamValue.maxListCount ? appParamValue.maxListCount : pbSize;
833            for (pos = appParamValue.listStartOffset; pos < size; pos++) {
834                str = BluetoothPbapService.getPhonebook(NEED_MISSED_CALL_NUMBER, pos, vcard21);
835                strstr += str;
836            }
837        }
838        return pushBytes(op, strstr);
839    }
840
841    private boolean closeStream(final OutputStream out, final Operation op) {
842        boolean returnvalue = true;
843        try {
844            if (out != null) {
845                out.close();
846            }
847        } catch (IOException e) {
848            Log.e(TAG, "outputStream close failed" + e.toString());
849            returnvalue = false;
850        }
851        try {
852            if (op != null) {
853                op.close();
854            }
855        } catch (IOException e) {
856            Log.e(TAG, "oeration close failed" + e.toString());
857            returnvalue = false;
858        }
859        return returnvalue;
860    }
861
862    public final void setConnectionID(final long id) {
863        if ((id < -1) || (id > 0xFFFFFFFFL)) {
864            throw new IllegalArgumentException("Illegal Connection ID");
865        }
866        mConnectionId = id;
867    }
868
869    /**
870     * Retrieves the connection ID that is being used in the present connection.
871     * This method will return -1 if no connection ID is being used.
872     *
873     * @return the connection id being used or -1 if no connection ID is being
874     *         used
875     */
876    public final long getConnectionID() {
877        return mConnectionId;
878    }
879
880    // Reserved for future use.In case PSE challenge PCE and PCE input wrong
881    // session key.
882    public final void onAuthenticationFailure(final byte[] userName) {
883    }
884}
885