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