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