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.pm.PackageManager;
     20 import android.os.AsyncResult;
     21 import android.os.Handler;
     22 import android.os.Looper;
     23 import android.os.Message;
     24 import android.os.ServiceManager;
     25 
     26 import java.util.List;
     27 
     28 /**
     29  * SimPhoneBookInterfaceManager to provide an inter-process communication to
     30  * access ADN-like SIM records.
     31  */
     32 public abstract class IccPhoneBookInterfaceManager extends IIccPhoneBook.Stub {
     33     protected static final boolean DBG = true;
     34 
     35     protected PhoneBase phone;
     36     protected AdnRecordCache adnCache;
     37     protected final Object mLock = new Object();
     38     protected int recordSize[];
     39     protected boolean success;
     40     protected List<AdnRecord> records;
     41 
     42     protected static final boolean ALLOW_SIM_OP_IN_UI_THREAD = false;
     43 
     44     protected static final int EVENT_GET_SIZE_DONE = 1;
     45     protected static final int EVENT_LOAD_DONE = 2;
     46     protected static final int EVENT_UPDATE_DONE = 3;
     47 
     48     protected Handler mBaseHandler = new Handler() {
     49         @Override
     50         public void handleMessage(Message msg) {
     51             AsyncResult ar;
     52 
     53             switch (msg.what) {
     54                 case EVENT_GET_SIZE_DONE:
     55                     ar = (AsyncResult) msg.obj;
     56                     synchronized (mLock) {
     57                         if (ar.exception == null) {
     58                             recordSize = (int[])ar.result;
     59                             // recordSize[0]  is the record length
     60                             // recordSize[1]  is the total length of the EF file
     61                             // recordSize[2]  is the number of records in the EF file
     62                             logd("GET_RECORD_SIZE Size " + recordSize[0] +
     63                                     " total " + recordSize[1] +
     64                                     " #record " + recordSize[2]);
     65                             mLock.notifyAll();
     66                         }
     67                     }
     68                     break;
     69                 case EVENT_UPDATE_DONE:
     70                     ar = (AsyncResult) msg.obj;
     71                     synchronized (mLock) {
     72                         success = (ar.exception == null);
     73                         mLock.notifyAll();
     74                     }
     75                     break;
     76                 case EVENT_LOAD_DONE:
     77                     ar = (AsyncResult)msg.obj;
     78                     synchronized (mLock) {
     79                         if (ar.exception == null) {
     80                             records = (List<AdnRecord>) ar.result;
     81                         } else {
     82                             if(DBG) logd("Cannot load ADN records");
     83                             if (records != null) {
     84                                 records.clear();
     85                             }
     86                         }
     87                         mLock.notifyAll();
     88                     }
     89                     break;
     90             }
     91         }
     92     };
     93 
     94     public IccPhoneBookInterfaceManager(PhoneBase phone) {
     95         this.phone = phone;
     96     }
     97 
     98     public void dispose() {
     99     }
    100 
    101     protected void publish() {
    102         //NOTE service "simphonebook" added by IccSmsInterfaceManagerProxy
    103         ServiceManager.addService("simphonebook", this);
    104     }
    105 
    106     protected abstract void logd(String msg);
    107 
    108     protected abstract void loge(String msg);
    109 
    110     /**
    111      * Replace oldAdn with newAdn in ADN-like record in EF
    112      *
    113      * getAdnRecordsInEf must be called at least once before this function,
    114      * otherwise an error will be returned. Currently the email field
    115      * if set in the ADN record is ignored.
    116      * throws SecurityException if no WRITE_CONTACTS permission
    117      *
    118      * @param efid must be one among EF_ADN, EF_FDN, and EF_SDN
    119      * @param oldTag adn tag to be replaced
    120      * @param oldPhoneNumber adn number to be replaced
    121      *        Set both oldTag and oldPhoneNubmer to "" means to replace an
    122      *        empty record, aka, insert new record
    123      * @param newTag adn tag to be stored
    124      * @param newPhoneNumber adn number ot be stored
    125      *        Set both newTag and newPhoneNubmer to "" means to replace the old
    126      *        record with empty one, aka, delete old record
    127      * @param pin2 required to update EF_FDN, otherwise must be null
    128      * @return true for success
    129      */
    130     public boolean
    131     updateAdnRecordsInEfBySearch (int efid,
    132             String oldTag, String oldPhoneNumber,
    133             String newTag, String newPhoneNumber, String pin2) {
    134 
    135 
    136         if (phone.getContext().checkCallingOrSelfPermission(
    137                 android.Manifest.permission.WRITE_CONTACTS)
    138             != PackageManager.PERMISSION_GRANTED) {
    139             throw new SecurityException(
    140                     "Requires android.permission.WRITE_CONTACTS permission");
    141         }
    142 
    143 
    144         if (DBG) logd("updateAdnRecordsInEfBySearch: efid=" + efid +
    145                 " ("+ oldTag + "," + oldPhoneNumber + ")"+ "==>" +
    146                 " ("+ newTag + "," + newPhoneNumber + ")"+ " pin2=" + pin2);
    147         synchronized(mLock) {
    148             checkThread();
    149             success = false;
    150             Message response = mBaseHandler.obtainMessage(EVENT_UPDATE_DONE);
    151             AdnRecord oldAdn = new AdnRecord(oldTag, oldPhoneNumber);
    152             AdnRecord newAdn = new AdnRecord(newTag, newPhoneNumber);
    153             adnCache.updateAdnBySearch(efid, oldAdn, newAdn, pin2, response);
    154             try {
    155                 mLock.wait();
    156             } catch (InterruptedException e) {
    157                 logd("interrupted while trying to update by search");
    158             }
    159         }
    160         return success;
    161     }
    162 
    163     /**
    164      * Update an ADN-like EF record by record index
    165      *
    166      * This is useful for iteration the whole ADN file, such as write the whole
    167      * phone book or erase/format the whole phonebook. Currently the email field
    168      * if set in the ADN record is ignored.
    169      * throws SecurityException if no WRITE_CONTACTS permission
    170      *
    171      * @param efid must be one among EF_ADN, EF_FDN, and EF_SDN
    172      * @param newTag adn tag to be stored
    173      * @param newPhoneNumber adn number to be stored
    174      *        Set both newTag and newPhoneNubmer to "" means to replace the old
    175      *        record with empty one, aka, delete old record
    176      * @param index is 1-based adn record index to be updated
    177      * @param pin2 required to update EF_FDN, otherwise must be null
    178      * @return true for success
    179      */
    180     public boolean
    181     updateAdnRecordsInEfByIndex(int efid, String newTag,
    182             String newPhoneNumber, int index, String pin2) {
    183 
    184         if (phone.getContext().checkCallingOrSelfPermission(
    185                 android.Manifest.permission.WRITE_CONTACTS)
    186                 != PackageManager.PERMISSION_GRANTED) {
    187             throw new SecurityException(
    188                     "Requires android.permission.WRITE_CONTACTS permission");
    189         }
    190 
    191         if (DBG) logd("updateAdnRecordsInEfByIndex: efid=" + efid +
    192                 " Index=" + index + " ==> " +
    193                 "("+ newTag + "," + newPhoneNumber + ")"+ " pin2=" + pin2);
    194         synchronized(mLock) {
    195             checkThread();
    196             success = false;
    197             Message response = mBaseHandler.obtainMessage(EVENT_UPDATE_DONE);
    198             AdnRecord newAdn = new AdnRecord(newTag, newPhoneNumber);
    199             adnCache.updateAdnByIndex(efid, newAdn, index, pin2, response);
    200             try {
    201                 mLock.wait();
    202             } catch (InterruptedException e) {
    203                 logd("interrupted while trying to update by index");
    204             }
    205         }
    206         return success;
    207     }
    208 
    209     /**
    210      * Get the capacity of records in efid
    211      *
    212      * @param efid the EF id of a ADN-like ICC
    213      * @return  int[3] array
    214      *            recordSizes[0]  is the single record length
    215      *            recordSizes[1]  is the total length of the EF file
    216      *            recordSizes[2]  is the number of records in the EF file
    217      */
    218     public abstract int[] getAdnRecordsSize(int efid);
    219 
    220     /**
    221      * Loads the AdnRecords in efid and returns them as a
    222      * List of AdnRecords
    223      *
    224      * throws SecurityException if no READ_CONTACTS permission
    225      *
    226      * @param efid the EF id of a ADN-like ICC
    227      * @return List of AdnRecord
    228      */
    229     public List<AdnRecord> getAdnRecordsInEf(int efid) {
    230 
    231         if (phone.getContext().checkCallingOrSelfPermission(
    232                 android.Manifest.permission.READ_CONTACTS)
    233                 != PackageManager.PERMISSION_GRANTED) {
    234             throw new SecurityException(
    235                     "Requires android.permission.READ_CONTACTS permission");
    236         }
    237 
    238         efid = updateEfForIccType(efid);
    239         if (DBG) logd("getAdnRecordsInEF: efid=" + efid);
    240 
    241         synchronized(mLock) {
    242             checkThread();
    243             Message response = mBaseHandler.obtainMessage(EVENT_LOAD_DONE);
    244             adnCache.requestLoadAllAdnLike(efid, adnCache.extensionEfForEf(efid), response);
    245             try {
    246                 mLock.wait();
    247             } catch (InterruptedException e) {
    248                 logd("interrupted while trying to load from the SIM");
    249             }
    250         }
    251             return records;
    252     }
    253 
    254     protected void checkThread() {
    255         if (!ALLOW_SIM_OP_IN_UI_THREAD) {
    256             // Make sure this isn't the UI thread, since it will block
    257             if (mBaseHandler.getLooper().equals(Looper.myLooper())) {
    258                 loge("query() called on the main UI thread!");
    259                 throw new IllegalStateException(
    260                         "You cannot call query on this provder from the main UI thread.");
    261             }
    262         }
    263     }
    264 
    265     private int updateEfForIccType(int efid) {
    266         // Check if we are trying to read ADN records
    267         if (efid == IccConstants.EF_ADN) {
    268             if (phone.getIccCard().isApplicationOnIcc(IccCardApplication.AppType.APPTYPE_USIM)) {
    269                 return IccConstants.EF_PBR;
    270             }
    271         }
    272         return efid;
    273     }
    274 }
    275 
    276