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