• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.internal.telephony;
18 
19 import android.annotation.UnsupportedAppUsage;
20 import android.content.ContentProvider;
21 import android.content.ContentValues;
22 import android.content.UriMatcher;
23 import android.database.Cursor;
24 import android.database.MatrixCursor;
25 import android.database.MergeCursor;
26 import android.net.Uri;
27 import android.os.RemoteException;
28 import android.os.ServiceManager;
29 import android.telephony.Rlog;
30 import android.telephony.SubscriptionInfo;
31 import android.telephony.SubscriptionManager;
32 import android.text.TextUtils;
33 
34 import com.android.internal.telephony.uicc.AdnRecord;
35 import com.android.internal.telephony.uicc.IccConstants;
36 
37 import java.util.List;
38 
39 
40 /**
41  * {@hide}
42  */
43 public class IccProvider extends ContentProvider {
44     private static final String TAG = "IccProvider";
45     @UnsupportedAppUsage
46     private static final boolean DBG = true;
47 
48 
49     @UnsupportedAppUsage
50     private static final String[] ADDRESS_BOOK_COLUMN_NAMES = new String[] {
51         "name",
52         "number",
53         "emails",
54         "_id"
55     };
56 
57     protected static final int ADN = 1;
58     protected static final int ADN_SUB = 2;
59     protected static final int FDN = 3;
60     protected static final int FDN_SUB = 4;
61     protected static final int SDN = 5;
62     protected static final int SDN_SUB = 6;
63     protected static final int ADN_ALL = 7;
64 
65     protected static final String STR_TAG = "tag";
66     protected static final String STR_NUMBER = "number";
67     protected static final String STR_EMAILS = "emails";
68     protected static final String STR_PIN2 = "pin2";
69 
70     private static final UriMatcher URL_MATCHER =
71                             new UriMatcher(UriMatcher.NO_MATCH);
72 
73     static {
74         URL_MATCHER.addURI("icc", "adn", ADN);
75         URL_MATCHER.addURI("icc", "adn/subId/#", ADN_SUB);
76         URL_MATCHER.addURI("icc", "fdn", FDN);
77         URL_MATCHER.addURI("icc", "fdn/subId/#", FDN_SUB);
78         URL_MATCHER.addURI("icc", "sdn", SDN);
79         URL_MATCHER.addURI("icc", "sdn/subId/#", SDN_SUB);
80     }
81 
82     private SubscriptionManager mSubscriptionManager;
83 
84     @Override
onCreate()85     public boolean onCreate() {
86         mSubscriptionManager = SubscriptionManager.from(getContext());
87         return true;
88     }
89 
90     @Override
query(Uri url, String[] projection, String selection, String[] selectionArgs, String sort)91     public Cursor query(Uri url, String[] projection, String selection,
92             String[] selectionArgs, String sort) {
93         if (DBG) log("query");
94 
95         switch (URL_MATCHER.match(url)) {
96             case ADN:
97                 return loadFromEf(IccConstants.EF_ADN,
98                         SubscriptionManager.getDefaultSubscriptionId());
99 
100             case ADN_SUB:
101                 return loadFromEf(IccConstants.EF_ADN, getRequestSubId(url));
102 
103             case FDN:
104                 return loadFromEf(IccConstants.EF_FDN,
105                         SubscriptionManager.getDefaultSubscriptionId());
106 
107             case FDN_SUB:
108                 return loadFromEf(IccConstants.EF_FDN, getRequestSubId(url));
109 
110             case SDN:
111                 return loadFromEf(IccConstants.EF_SDN,
112                         SubscriptionManager.getDefaultSubscriptionId());
113 
114             case SDN_SUB:
115                 return loadFromEf(IccConstants.EF_SDN, getRequestSubId(url));
116 
117             case ADN_ALL:
118                 return loadAllSimContacts(IccConstants.EF_ADN);
119 
120             default:
121                 throw new IllegalArgumentException("Unknown URL " + url);
122         }
123     }
124 
loadAllSimContacts(int efType)125     private Cursor loadAllSimContacts(int efType) {
126         Cursor [] result;
127         List<SubscriptionInfo> subInfoList = mSubscriptionManager
128                 .getActiveSubscriptionInfoList(false);
129 
130         if ((subInfoList == null) || (subInfoList.size() == 0)) {
131             result = new Cursor[0];
132         } else {
133             int subIdCount = subInfoList.size();
134             result = new Cursor[subIdCount];
135             int subId;
136 
137             for (int i = 0; i < subIdCount; i++) {
138                 subId = subInfoList.get(i).getSubscriptionId();
139                 result[i] = loadFromEf(efType, subId);
140                 Rlog.i(TAG,"ADN Records loaded for Subscription ::" + subId);
141             }
142         }
143 
144         return new MergeCursor(result);
145     }
146 
147     @Override
getType(Uri url)148     public String getType(Uri url) {
149         switch (URL_MATCHER.match(url)) {
150             case ADN:
151             case ADN_SUB:
152             case FDN:
153             case FDN_SUB:
154             case SDN:
155             case SDN_SUB:
156             case ADN_ALL:
157                 return "vnd.android.cursor.dir/sim-contact";
158 
159             default:
160                 throw new IllegalArgumentException("Unknown URL " + url);
161         }
162     }
163 
164     @Override
insert(Uri url, ContentValues initialValues)165     public Uri insert(Uri url, ContentValues initialValues) {
166         Uri resultUri;
167         int efType;
168         String pin2 = null;
169         int subId;
170 
171         if (DBG) log("insert");
172 
173         int match = URL_MATCHER.match(url);
174         switch (match) {
175             case ADN:
176                 efType = IccConstants.EF_ADN;
177                 subId = SubscriptionManager.getDefaultSubscriptionId();
178                 break;
179 
180             case ADN_SUB:
181                 efType = IccConstants.EF_ADN;
182                 subId = getRequestSubId(url);
183                 break;
184 
185             case FDN:
186                 efType = IccConstants.EF_FDN;
187                 subId = SubscriptionManager.getDefaultSubscriptionId();
188                 pin2 = initialValues.getAsString("pin2");
189                 break;
190 
191             case FDN_SUB:
192                 efType = IccConstants.EF_FDN;
193                 subId = getRequestSubId(url);
194                 pin2 = initialValues.getAsString("pin2");
195                 break;
196 
197             default:
198                 throw new UnsupportedOperationException(
199                         "Cannot insert into URL: " + url);
200         }
201 
202         String tag = initialValues.getAsString("tag");
203         String number = initialValues.getAsString("number");
204         // TODO(): Read email instead of sending null.
205         boolean success = addIccRecordToEf(efType, tag, number, null, pin2, subId);
206 
207         if (!success) {
208             return null;
209         }
210 
211         StringBuilder buf = new StringBuilder("content://icc/");
212         switch (match) {
213             case ADN:
214                 buf.append("adn/");
215                 break;
216 
217             case ADN_SUB:
218                 buf.append("adn/subId/");
219                 break;
220 
221             case FDN:
222                 buf.append("fdn/");
223                 break;
224 
225             case FDN_SUB:
226                 buf.append("fdn/subId/");
227                 break;
228         }
229 
230         // TODO: we need to find out the rowId for the newly added record
231         buf.append(0);
232 
233         resultUri = Uri.parse(buf.toString());
234 
235         getContext().getContentResolver().notifyChange(url, null);
236         /*
237         // notify interested parties that an insertion happened
238         getContext().getContentResolver().notifyInsert(
239                 resultUri, rowID, null);
240         */
241 
242         return resultUri;
243     }
244 
normalizeValue(String inVal)245     private String normalizeValue(String inVal) {
246         int len = inVal.length();
247         // If name is empty in contact return null to avoid crash.
248         if (len == 0) {
249             if (DBG) log("len of input String is 0");
250             return inVal;
251         }
252         String retVal = inVal;
253 
254         if (inVal.charAt(0) == '\'' && inVal.charAt(len-1) == '\'') {
255             retVal = inVal.substring(1, len-1);
256         }
257 
258         return retVal;
259     }
260 
261     @Override
delete(Uri url, String where, String[] whereArgs)262     public int delete(Uri url, String where, String[] whereArgs) {
263         int efType;
264         int subId;
265 
266         int match = URL_MATCHER.match(url);
267         switch (match) {
268             case ADN:
269                 efType = IccConstants.EF_ADN;
270                 subId = SubscriptionManager.getDefaultSubscriptionId();
271                 break;
272 
273             case ADN_SUB:
274                 efType = IccConstants.EF_ADN;
275                 subId = getRequestSubId(url);
276                 break;
277 
278             case FDN:
279                 efType = IccConstants.EF_FDN;
280                 subId = SubscriptionManager.getDefaultSubscriptionId();
281                 break;
282 
283             case FDN_SUB:
284                 efType = IccConstants.EF_FDN;
285                 subId = getRequestSubId(url);
286                 break;
287 
288             default:
289                 throw new UnsupportedOperationException(
290                         "Cannot insert into URL: " + url);
291         }
292 
293         if (DBG) log("delete");
294 
295         // parse where clause
296         String tag = null;
297         String number = null;
298         String[] emails = null;
299         String pin2 = null;
300 
301         String[] tokens = where.split(" AND ");
302         int n = tokens.length;
303 
304         while (--n >= 0) {
305             String param = tokens[n];
306             if (DBG) log("parsing '" + param + "'");
307 
308             String[] pair = param.split("=", 2);
309 
310             if (pair.length != 2) {
311                 Rlog.e(TAG, "resolve: bad whereClause parameter: " + param);
312                 continue;
313             }
314             String key = pair[0].trim();
315             String val = pair[1].trim();
316 
317             if (STR_TAG.equals(key)) {
318                 tag = normalizeValue(val);
319             } else if (STR_NUMBER.equals(key)) {
320                 number = normalizeValue(val);
321             } else if (STR_EMAILS.equals(key)) {
322                 //TODO(): Email is null.
323                 emails = null;
324             } else if (STR_PIN2.equals(key)) {
325                 pin2 = normalizeValue(val);
326             }
327         }
328 
329         if (efType == FDN && TextUtils.isEmpty(pin2)) {
330             return 0;
331         }
332 
333         boolean success = deleteIccRecordFromEf(efType, tag, number, emails, pin2, subId);
334         if (!success) {
335             return 0;
336         }
337 
338         getContext().getContentResolver().notifyChange(url, null);
339         return 1;
340     }
341 
342     @Override
update(Uri url, ContentValues values, String where, String[] whereArgs)343     public int update(Uri url, ContentValues values, String where, String[] whereArgs) {
344         String pin2 = null;
345         int efType;
346         int subId;
347 
348         if (DBG) log("update");
349 
350         int match = URL_MATCHER.match(url);
351         switch (match) {
352             case ADN:
353                 efType = IccConstants.EF_ADN;
354                 subId = SubscriptionManager.getDefaultSubscriptionId();
355                 break;
356 
357             case ADN_SUB:
358                 efType = IccConstants.EF_ADN;
359                 subId = getRequestSubId(url);
360                 break;
361 
362             case FDN:
363                 efType = IccConstants.EF_FDN;
364                 subId = SubscriptionManager.getDefaultSubscriptionId();
365                 pin2 = values.getAsString("pin2");
366                 break;
367 
368             case FDN_SUB:
369                 efType = IccConstants.EF_FDN;
370                 subId = getRequestSubId(url);
371                 pin2 = values.getAsString("pin2");
372                 break;
373 
374             default:
375                 throw new UnsupportedOperationException(
376                         "Cannot insert into URL: " + url);
377         }
378 
379         String tag = values.getAsString("tag");
380         String number = values.getAsString("number");
381         String[] emails = null;
382         String newTag = values.getAsString("newTag");
383         String newNumber = values.getAsString("newNumber");
384         String[] newEmails = null;
385         // TODO(): Update for email.
386         boolean success = updateIccRecordInEf(efType, tag, number,
387                 newTag, newNumber, pin2, subId);
388 
389         if (!success) {
390             return 0;
391         }
392 
393         getContext().getContentResolver().notifyChange(url, null);
394         return 1;
395     }
396 
loadFromEf(int efType, int subId)397     private MatrixCursor loadFromEf(int efType, int subId) {
398         if (DBG) log("loadFromEf: efType=0x" +
399                 Integer.toHexString(efType).toUpperCase() + ", subscription=" + subId);
400 
401         List<AdnRecord> adnRecords = null;
402         try {
403             IIccPhoneBook iccIpb = IIccPhoneBook.Stub.asInterface(
404                     ServiceManager.getService("simphonebook"));
405             if (iccIpb != null) {
406                 adnRecords = iccIpb.getAdnRecordsInEfForSubscriber(subId, efType);
407             }
408         } catch (RemoteException ex) {
409             // ignore it
410         } catch (SecurityException ex) {
411             if (DBG) log(ex.toString());
412         }
413 
414         if (adnRecords != null) {
415             // Load the results
416             final int N = adnRecords.size();
417             final MatrixCursor cursor = new MatrixCursor(ADDRESS_BOOK_COLUMN_NAMES, N);
418             if (DBG) log("adnRecords.size=" + N);
419             for (int i = 0; i < N ; i++) {
420                 loadRecord(adnRecords.get(i), cursor, i);
421             }
422             return cursor;
423         } else {
424             // No results to load
425             Rlog.w(TAG, "Cannot load ADN records");
426             return new MatrixCursor(ADDRESS_BOOK_COLUMN_NAMES);
427         }
428     }
429 
430     private boolean
addIccRecordToEf(int efType, String name, String number, String[] emails, String pin2, int subId)431     addIccRecordToEf(int efType, String name, String number, String[] emails,
432             String pin2, int subId) {
433         if (DBG) log("addIccRecordToEf: efType=0x" + Integer.toHexString(efType).toUpperCase() +
434                 ", name=" + Rlog.pii(TAG, name) + ", number=" + Rlog.pii(TAG, number) +
435                 ", emails=" + Rlog.pii(TAG, emails) + ", subscription=" + subId);
436 
437         boolean success = false;
438 
439         // TODO: do we need to call getAdnRecordsInEf() before calling
440         // updateAdnRecordsInEfBySearch()? In any case, we will leave
441         // the UI level logic to fill that prereq if necessary. But
442         // hopefully, we can remove this requirement.
443 
444         try {
445             IIccPhoneBook iccIpb = IIccPhoneBook.Stub.asInterface(
446                     ServiceManager.getService("simphonebook"));
447             if (iccIpb != null) {
448                 success = iccIpb.updateAdnRecordsInEfBySearchForSubscriber(subId, efType,
449                         "", "", name, number, pin2);
450             }
451         } catch (RemoteException ex) {
452             // ignore it
453         } catch (SecurityException ex) {
454             if (DBG) log(ex.toString());
455         }
456         if (DBG) log("addIccRecordToEf: " + success);
457         return success;
458     }
459 
460     private boolean
updateIccRecordInEf(int efType, String oldName, String oldNumber, String newName, String newNumber, String pin2, int subId)461     updateIccRecordInEf(int efType, String oldName, String oldNumber,
462             String newName, String newNumber, String pin2, int subId) {
463         if (DBG) log("updateIccRecordInEf: efType=0x" + Integer.toHexString(efType).toUpperCase() +
464                 ", oldname=" + Rlog.pii(TAG, oldName) + ", oldnumber=" + Rlog.pii(TAG, oldNumber) +
465                 ", newname=" + Rlog.pii(TAG, newName) + ", newnumber=" + Rlog.pii(TAG, newName) +
466                 ", subscription=" + subId);
467 
468         boolean success = false;
469 
470         try {
471             IIccPhoneBook iccIpb = IIccPhoneBook.Stub.asInterface(
472                     ServiceManager.getService("simphonebook"));
473             if (iccIpb != null) {
474                 success = iccIpb.updateAdnRecordsInEfBySearchForSubscriber(subId, efType, oldName,
475                         oldNumber, newName, newNumber, pin2);
476             }
477         } catch (RemoteException ex) {
478             // ignore it
479         } catch (SecurityException ex) {
480             if (DBG) log(ex.toString());
481         }
482         if (DBG) log("updateIccRecordInEf: " + success);
483         return success;
484     }
485 
486 
deleteIccRecordFromEf(int efType, String name, String number, String[] emails, String pin2, int subId)487     private boolean deleteIccRecordFromEf(int efType, String name, String number, String[] emails,
488             String pin2, int subId) {
489         if (DBG) log("deleteIccRecordFromEf: efType=0x" +
490                 Integer.toHexString(efType).toUpperCase() + ", name=" + Rlog.pii(TAG, name) +
491                 ", number=" + Rlog.pii(TAG, number) + ", emails=" + Rlog.pii(TAG, emails) +
492                 ", pin2=" + Rlog.pii(TAG, pin2) + ", subscription=" + subId);
493 
494         boolean success = false;
495 
496         try {
497             IIccPhoneBook iccIpb = IIccPhoneBook.Stub.asInterface(
498                     ServiceManager.getService("simphonebook"));
499             if (iccIpb != null) {
500                 success = iccIpb.updateAdnRecordsInEfBySearchForSubscriber(subId, efType,
501                           name, number, "", "", pin2);
502             }
503         } catch (RemoteException ex) {
504             // ignore it
505         } catch (SecurityException ex) {
506             if (DBG) log(ex.toString());
507         }
508         if (DBG) log("deleteIccRecordFromEf: " + success);
509         return success;
510     }
511 
512     /**
513      * Loads an AdnRecord into a MatrixCursor. Must be called with mLock held.
514      *
515      * @param record the ADN record to load from
516      * @param cursor the cursor to receive the results
517      */
518     @UnsupportedAppUsage
loadRecord(AdnRecord record, MatrixCursor cursor, int id)519     private void loadRecord(AdnRecord record, MatrixCursor cursor, int id) {
520         if (!record.isEmpty()) {
521             Object[] contact = new Object[4];
522             String alphaTag = record.getAlphaTag();
523             String number = record.getNumber();
524 
525             if (DBG) log("loadRecord: " + alphaTag + ", " + Rlog.pii(TAG, number));
526             contact[0] = alphaTag;
527             contact[1] = number;
528 
529             String[] emails = record.getEmails();
530             if (emails != null) {
531                 StringBuilder emailString = new StringBuilder();
532                 for (String email: emails) {
533                     log("Adding email:" + Rlog.pii(TAG, email));
534                     emailString.append(email);
535                     emailString.append(",");
536                 }
537                 contact[2] = emailString.toString();
538             }
539             contact[3] = id;
540             cursor.addRow(contact);
541         }
542     }
543 
544     @UnsupportedAppUsage
log(String msg)545     private void log(String msg) {
546         Rlog.d(TAG, "[IccProvider] " + msg);
547     }
548 
getRequestSubId(Uri url)549     private int getRequestSubId(Uri url) {
550         if (DBG) log("getRequestSubId url: " + url);
551 
552         try {
553             return Integer.parseInt(url.getLastPathSegment());
554         } catch (NumberFormatException ex) {
555             throw new IllegalArgumentException("Unknown URL " + url);
556         }
557     }
558 }
559