• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.bluetooth.pbapclient;
18 
19 import android.util.Log;
20 
21 import com.android.vcard.VCardConfig;
22 import com.android.vcard.VCardEntry;
23 import com.android.vcard.VCardEntryConstructor;
24 import com.android.vcard.VCardEntryHandler;
25 import com.android.vcard.VCardParser;
26 import com.android.vcard.VCardParser_V21;
27 import com.android.vcard.VCardParser_V30;
28 import com.android.vcard.exception.VCardException;
29 import com.android.vcard.exception.VCardVersionException;
30 
31 import java.io.BufferedInputStream;
32 import java.io.IOException;
33 import java.io.InputStream;
34 import java.util.ArrayList;
35 import java.util.List;
36 
37 public class PbapPhonebook {
38     private static final String TAG = PbapPhonebook.class.getSimpleName();
39 
40     // {@link BufferedInputStream#DEFAULT_BUFFER_SIZE} is not public
41     private static final int BIS_DEFAULT_BUFFER_SIZE = 8192;
42 
43     // Phonebooks, including call history. See PBAP 1.2.3, Section 3.1.2
44     public static final String LOCAL_PHONEBOOK_PATH = "telecom/pb.vcf"; // Device phonebook
45     public static final String FAVORITES_PATH = "telecom/fav.vcf"; // Contacts marked as favorite
46     public static final String MCH_PATH = "telecom/mch.vcf"; // Missed Calls
47     public static final String ICH_PATH = "telecom/ich.vcf"; // Incoming Calls
48     public static final String OCH_PATH = "telecom/och.vcf"; // Outgoing Calls
49     public static final String SIM_PHONEBOOK_PATH = "SIM1/telecom/pb.vcf"; // SIM stored phonebook
50     public static final String SIM_MCH_PATH = "SIM1/telecom/mch.vcf"; // SIM stored Missed Calls
51     public static final String SIM_ICH_PATH = "SIM1/telecom/ich.vcf"; // SIM stored Incoming Calls
52     public static final String SIM_OCH_PATH = "SIM1/telecom/och.vcf"; // SIM stored Outgoing Calls
53 
54     // VCard Formats, both are required to be supported by the Server, PBAP 1.2.3, Section 5.1.4.2
55     public static byte FORMAT_VCARD_21 = 0;
56     public static byte FORMAT_VCARD_30 = 1;
57 
58     private final String mPhonebook;
59     private final int mListStartOffset;
60     private final List<VCardEntry> mCards = new ArrayList<VCardEntry>();
61 
62     class CardEntryHandler implements VCardEntryHandler {
63         @Override
onStart()64         public void onStart() {}
65 
66         @Override
onEntryCreated(VCardEntry entry)67         public void onEntryCreated(VCardEntry entry) {
68             mCards.add(entry);
69         }
70 
71         @Override
onEnd()72         public void onEnd() {}
73     }
74 
PbapPhonebook(String phonebook)75     PbapPhonebook(String phonebook) {
76         mPhonebook = phonebook;
77         mListStartOffset = 0;
78     }
79 
PbapPhonebook( String phonebook, byte format, int listStartOffset, InputStream inputStream)80     PbapPhonebook(
81             String phonebook,
82             byte format,
83             int listStartOffset,
84             InputStream inputStream)
85             throws IOException {
86         if (format != FORMAT_VCARD_21 && format != FORMAT_VCARD_30) {
87             throw new IllegalArgumentException("Unsupported vCard version.");
88         }
89         mPhonebook = phonebook;
90         mListStartOffset = listStartOffset;
91         parse(inputStream, format);
92     }
93 
parse(InputStream in, byte format)94     private void parse(InputStream in, byte format) throws IOException {
95         VCardParser parser;
96 
97         if (format == FORMAT_VCARD_30) {
98             parser = new VCardParser_V30();
99         } else {
100             parser = new VCardParser_V21();
101         }
102 
103         VCardEntryConstructor constructor =
104                 new VCardEntryConstructor(VCardConfig.VCARD_TYPE_V21_GENERIC);
105 
106         CardEntryHandler handler = new CardEntryHandler();
107         constructor.addEntryHandler(handler);
108 
109         parser.addInterpreter(constructor);
110 
111         // {@link BufferedInputStream} supports the {@link InputStream#mark} and
112         // {@link InputStream#reset} methods.
113         BufferedInputStream bufferedInput = new BufferedInputStream(in);
114         bufferedInput.mark(BIS_DEFAULT_BUFFER_SIZE /* readlimit */);
115 
116         // If there is a {@link VCardVersionException}, try parsing again with a different
117         // version. Otherwise, parsing either succeeds (i.e., no {@link VCardException}) or it
118         // fails with a different {@link VCardException}.
119         if (parsedWithVcardVersionException(parser, bufferedInput)) {
120             // PBAP v1.2.3 only supports vCard versions 2.1 and 3.0; it's one or the other
121             if (format == FORMAT_VCARD_21) {
122                 parser = new VCardParser_V30();
123                 Log.w(TAG, "vCard version and Parser mismatch; expected v2.1, switching to v3.0");
124             } else {
125                 parser = new VCardParser_V21();
126                 Log.w(TAG, "vCard version and Parser mismatch; expected v3.0, switching to v2.1");
127             }
128             // reset and try again
129             bufferedInput.reset();
130             mCards.clear();
131             constructor.clear();
132             parser.addInterpreter(constructor);
133             if (parsedWithVcardVersionException(parser, bufferedInput)) {
134                 Log.e(TAG, "unsupported vCard version, neither v2.1 nor v3.0");
135             }
136         }
137     }
138 
139     /**
140      * Attempts to parse, with an eye on whether the correct version of Parser is used.
141      *
142      * @param parser -- the {@link VCardParser} to use.
143      * @param in -- the {@link InputStream} to parse.
144      * @return {@code true} if there was a {@link VCardVersionException}; {@code false} if there is
145      *     any other {@link VCardException} or succeeds (i.e., no {@link VCardException}).
146      * @throws IOException if there's an issue reading the {@link InputStream}.
147      */
parsedWithVcardVersionException(VCardParser parser, InputStream in)148     private static boolean parsedWithVcardVersionException(VCardParser parser, InputStream in)
149             throws IOException {
150         try {
151             parser.parse(in);
152         } catch (VCardVersionException e1) {
153             Log.w(TAG, "vCard version and Parser mismatch", e1);
154             return true;
155         } catch (VCardException e2) {
156             Log.e(TAG, "vCard exception", e2);
157         }
158         return false;
159     }
160 
161     /**
162      * Get the phonebook path associated with this PbapPhonebook object
163      *
164      * @return a string representing the path these VCard objects were requested from
165      */
getPhonebook()166     public String getPhonebook() {
167         return mPhonebook;
168     }
169 
170     /**
171      * Get the offset associated with this PbapPhonebook object
172      *
173      * <p>The offset represents the start index of the remote contacts pull
174      *
175      * @return an int representing the offset index where this pull started from
176      */
getOffset()177     public int getOffset() {
178         return mListStartOffset;
179     }
180 
181     /**
182      * Get the total number of contacts contained in this phonebook
183      *
184      * @return an int containing the total number of contacts contained in this phonebook
185      */
getCount()186     public int getCount() {
187         return mCards.size();
188     }
189 
190     /**
191      * Get the list of VCard objects contained in this phonebook
192      *
193      * @return a list of VCard objects in this phonebook
194      */
getList()195     public List<VCardEntry> getList() {
196         return mCards;
197     }
198 
199     @Override
toString()200     public String toString() {
201         return "<" + TAG + "phonebook=" + mPhonebook + " entries=" + getCount() + ">";
202     }
203 }
204