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