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