1/*
2 * Copyright (C) 2006 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
17package com.android.internal.telephony;
18
19import android.content.ContentProvider;
20import android.content.UriMatcher;
21import android.content.ContentValues;
22import android.database.Cursor;
23import android.database.MatrixCursor;
24import android.net.Uri;
25import android.os.RemoteException;
26import android.os.ServiceManager;
27import android.text.TextUtils;
28import android.util.Log;
29
30import java.util.List;
31
32import com.android.internal.telephony.IccConstants;
33import com.android.internal.telephony.AdnRecord;
34import com.android.internal.telephony.IIccPhoneBook;
35
36
37/**
38 * {@hide}
39 */
40public class IccProvider extends ContentProvider {
41    private static final String TAG = "IccProvider";
42    private static final boolean DBG = false;
43
44
45    private static final String[] ADDRESS_BOOK_COLUMN_NAMES = new String[] {
46        "name",
47        "number",
48        "emails",
49        "_id"
50    };
51
52    private static final int ADN = 1;
53    private static final int FDN = 2;
54    private static final int SDN = 3;
55
56    private static final String STR_TAG = "tag";
57    private static final String STR_NUMBER = "number";
58    private static final String STR_EMAILS = "emails";
59    private static final String STR_PIN2 = "pin2";
60
61    private static final UriMatcher URL_MATCHER =
62                            new UriMatcher(UriMatcher.NO_MATCH);
63
64    static {
65        URL_MATCHER.addURI("icc", "adn", ADN);
66        URL_MATCHER.addURI("icc", "fdn", FDN);
67        URL_MATCHER.addURI("icc", "sdn", SDN);
68    }
69
70
71    @Override
72    public boolean onCreate() {
73        return true;
74    }
75
76    @Override
77    public Cursor query(Uri url, String[] projection, String selection,
78            String[] selectionArgs, String sort) {
79        switch (URL_MATCHER.match(url)) {
80            case ADN:
81                return loadFromEf(IccConstants.EF_ADN);
82
83            case FDN:
84                return loadFromEf(IccConstants.EF_FDN);
85
86            case SDN:
87                return loadFromEf(IccConstants.EF_SDN);
88
89            default:
90                throw new IllegalArgumentException("Unknown URL " + url);
91        }
92    }
93
94    @Override
95    public String getType(Uri url) {
96        switch (URL_MATCHER.match(url)) {
97            case ADN:
98            case FDN:
99            case SDN:
100                return "vnd.android.cursor.dir/sim-contact";
101
102            default:
103                throw new IllegalArgumentException("Unknown URL " + url);
104        }
105    }
106
107    @Override
108    public Uri insert(Uri url, ContentValues initialValues) {
109        Uri resultUri;
110        int efType;
111        String pin2 = null;
112
113        if (DBG) log("insert");
114
115        int match = URL_MATCHER.match(url);
116        switch (match) {
117            case ADN:
118                efType = IccConstants.EF_ADN;
119                break;
120
121            case FDN:
122                efType = IccConstants.EF_FDN;
123                pin2 = initialValues.getAsString("pin2");
124                break;
125
126            default:
127                throw new UnsupportedOperationException(
128                        "Cannot insert into URL: " + url);
129        }
130
131        String tag = initialValues.getAsString("tag");
132        String number = initialValues.getAsString("number");
133        // TODO(): Read email instead of sending null.
134        boolean success = addIccRecordToEf(efType, tag, number, null, pin2);
135
136        if (!success) {
137            return null;
138        }
139
140        StringBuilder buf = new StringBuilder("content://icc/");
141        switch (match) {
142            case ADN:
143                buf.append("adn/");
144                break;
145
146            case FDN:
147                buf.append("fdn/");
148                break;
149        }
150
151        // TODO: we need to find out the rowId for the newly added record
152        buf.append(0);
153
154        resultUri = Uri.parse(buf.toString());
155
156        /*
157        // notify interested parties that an insertion happened
158        getContext().getContentResolver().notifyInsert(
159                resultUri, rowID, null);
160        */
161
162        return resultUri;
163    }
164
165    private String normalizeValue(String inVal) {
166        int len = inVal.length();
167        String retVal = inVal;
168
169        if (inVal.charAt(0) == '\'' && inVal.charAt(len-1) == '\'') {
170            retVal = inVal.substring(1, len-1);
171        }
172
173        return retVal;
174    }
175
176    @Override
177    public int delete(Uri url, String where, String[] whereArgs) {
178        int efType;
179
180        if (DBG) log("delete");
181
182        int match = URL_MATCHER.match(url);
183        switch (match) {
184            case ADN:
185                efType = IccConstants.EF_ADN;
186                break;
187
188            case FDN:
189                efType = IccConstants.EF_FDN;
190                break;
191
192            default:
193                throw new UnsupportedOperationException(
194                        "Cannot insert into URL: " + url);
195        }
196
197        // parse where clause
198        String tag = null;
199        String number = null;
200        String[] emails = null;
201        String pin2 = null;
202
203        String[] tokens = where.split("AND");
204        int n = tokens.length;
205
206        while (--n >= 0) {
207            String param = tokens[n];
208            if (DBG) log("parsing '" + param + "'");
209
210            String[] pair = param.split("=");
211
212            if (pair.length != 2) {
213                Log.e(TAG, "resolve: bad whereClause parameter: " + param);
214                continue;
215            }
216
217            String key = pair[0].trim();
218            String val = pair[1].trim();
219
220            if (STR_TAG.equals(key)) {
221                tag = normalizeValue(val);
222            } else if (STR_NUMBER.equals(key)) {
223                number = normalizeValue(val);
224            } else if (STR_EMAILS.equals(key)) {
225                //TODO(): Email is null.
226                emails = null;
227            } else if (STR_PIN2.equals(key)) {
228                pin2 = normalizeValue(val);
229            }
230        }
231
232        if (TextUtils.isEmpty(number)) {
233            return 0;
234        }
235
236        if (efType == IccConstants.EF_FDN && TextUtils.isEmpty(pin2)) {
237            return 0;
238        }
239
240        boolean success = deleteIccRecordFromEf(efType, tag, number, emails, pin2);
241        if (!success) {
242            return 0;
243        }
244
245        return 1;
246    }
247
248    @Override
249    public int update(Uri url, ContentValues values, String where, String[] whereArgs) {
250        int efType;
251        String pin2 = null;
252
253        if (DBG) log("update");
254
255        int match = URL_MATCHER.match(url);
256        switch (match) {
257            case ADN:
258                efType = IccConstants.EF_ADN;
259                break;
260
261            case FDN:
262                efType = IccConstants.EF_FDN;
263                pin2 = values.getAsString("pin2");
264                break;
265
266            default:
267                throw new UnsupportedOperationException(
268                        "Cannot insert into URL: " + url);
269        }
270
271        String tag = values.getAsString("tag");
272        String number = values.getAsString("number");
273        String[] emails = null;
274        String newTag = values.getAsString("newTag");
275        String newNumber = values.getAsString("newNumber");
276        String[] newEmails = null;
277        // TODO(): Update for email.
278        boolean success = updateIccRecordInEf(efType, tag, number,
279                newTag, newNumber, pin2);
280
281        if (!success) {
282            return 0;
283        }
284
285        return 1;
286    }
287
288    private MatrixCursor loadFromEf(int efType) {
289        if (DBG) log("loadFromEf: efType=" + efType);
290
291        List<AdnRecord> adnRecords = null;
292        try {
293            IIccPhoneBook iccIpb = IIccPhoneBook.Stub.asInterface(
294                    ServiceManager.getService("simphonebook"));
295            if (iccIpb != null) {
296                adnRecords = iccIpb.getAdnRecordsInEf(efType);
297            }
298        } catch (RemoteException ex) {
299            // ignore it
300        } catch (SecurityException ex) {
301            if (DBG) log(ex.toString());
302        }
303
304        if (adnRecords != null) {
305            // Load the results
306            final int N = adnRecords.size();
307            final MatrixCursor cursor = new MatrixCursor(ADDRESS_BOOK_COLUMN_NAMES, N);
308            if (DBG) log("adnRecords.size=" + N);
309            for (int i = 0; i < N ; i++) {
310                loadRecord(adnRecords.get(i), cursor, i);
311            }
312            return cursor;
313        } else {
314            // No results to load
315            Log.w(TAG, "Cannot load ADN records");
316            return new MatrixCursor(ADDRESS_BOOK_COLUMN_NAMES);
317        }
318    }
319
320    private boolean
321    addIccRecordToEf(int efType, String name, String number, String[] emails, String pin2) {
322        if (DBG) log("addIccRecordToEf: efType=" + efType + ", name=" + name +
323                ", number=" + number + ", emails=" + emails);
324
325        boolean success = false;
326
327        // TODO: do we need to call getAdnRecordsInEf() before calling
328        // updateAdnRecordsInEfBySearch()? In any case, we will leave
329        // the UI level logic to fill that prereq if necessary. But
330        // hopefully, we can remove this requirement.
331
332        try {
333            IIccPhoneBook iccIpb = IIccPhoneBook.Stub.asInterface(
334                    ServiceManager.getService("simphonebook"));
335            if (iccIpb != null) {
336                success = iccIpb.updateAdnRecordsInEfBySearch(efType, "", "",
337                        name, number, pin2);
338            }
339        } catch (RemoteException ex) {
340            // ignore it
341        } catch (SecurityException ex) {
342            if (DBG) log(ex.toString());
343        }
344        if (DBG) log("addIccRecordToEf: " + success);
345        return success;
346    }
347
348    private boolean
349    updateIccRecordInEf(int efType, String oldName, String oldNumber,
350            String newName, String newNumber, String pin2) {
351        if (DBG) log("updateIccRecordInEf: efType=" + efType +
352                ", oldname=" + oldName + ", oldnumber=" + oldNumber +
353                ", newname=" + newName + ", newnumber=" + newNumber);
354        boolean success = false;
355
356        try {
357            IIccPhoneBook iccIpb = IIccPhoneBook.Stub.asInterface(
358                    ServiceManager.getService("simphonebook"));
359            if (iccIpb != null) {
360                success = iccIpb.updateAdnRecordsInEfBySearch(efType,
361                        oldName, oldNumber, newName, newNumber, pin2);
362            }
363        } catch (RemoteException ex) {
364            // ignore it
365        } catch (SecurityException ex) {
366            if (DBG) log(ex.toString());
367        }
368        if (DBG) log("updateIccRecordInEf: " + success);
369        return success;
370    }
371
372
373    private boolean deleteIccRecordFromEf(int efType, String name, String number, String[] emails,
374            String pin2) {
375        if (DBG) log("deleteIccRecordFromEf: efType=" + efType +
376                ", name=" + name + ", number=" + number + ", emails=" + emails + ", pin2=" + pin2);
377
378        boolean success = false;
379
380        try {
381            IIccPhoneBook iccIpb = IIccPhoneBook.Stub.asInterface(
382                    ServiceManager.getService("simphonebook"));
383            if (iccIpb != null) {
384                success = iccIpb.updateAdnRecordsInEfBySearch(efType,
385                        name, number, "", "", pin2);
386            }
387        } catch (RemoteException ex) {
388            // ignore it
389        } catch (SecurityException ex) {
390            if (DBG) log(ex.toString());
391        }
392        if (DBG) log("deleteIccRecordFromEf: " + success);
393        return success;
394    }
395
396    /**
397     * Loads an AdnRecord into a MatrixCursor. Must be called with mLock held.
398     *
399     * @param record the ADN record to load from
400     * @param cursor the cursor to receive the results
401     */
402    private void loadRecord(AdnRecord record, MatrixCursor cursor, int id) {
403        if (!record.isEmpty()) {
404            Object[] contact = new Object[4];
405            String alphaTag = record.getAlphaTag();
406            String number = record.getNumber();
407
408            if (DBG) log("loadRecord: " + alphaTag + ", " + number + ",");
409            contact[0] = alphaTag;
410            contact[1] = number;
411
412            String[] emails = record.getEmails();
413            if (emails != null) {
414                StringBuilder emailString = new StringBuilder();
415                for (String email: emails) {
416                    if (DBG) log("Adding email:" + email);
417                    emailString.append(email);
418                    emailString.append(",");
419                }
420                contact[2] = emailString.toString();
421            }
422            contact[3] = id;
423            cursor.addRow(contact);
424        }
425    }
426
427    private void log(String msg) {
428        Log.d(TAG, "[IccProvider] " + msg);
429    }
430
431}
432