Home | History | Annotate | Download | only in uicc
      1 /*
      2  * Copyright (C) 2011 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.uicc;
     18 
     19 import android.content.Context;
     20 import android.os.AsyncResult;
     21 import android.os.Handler;
     22 import android.os.Message;
     23 import android.telephony.Rlog;
     24 
     25 import com.android.internal.telephony.CommandsInterface;
     26 import com.android.internal.telephony.gsm.SimTlv;
     27 //import com.android.internal.telephony.gsm.VoiceMailConstants;
     28 
     29 import java.io.FileDescriptor;
     30 import java.io.PrintWriter;
     31 import java.nio.charset.Charset;
     32 import java.util.ArrayList;
     33 import java.util.Arrays;
     34 
     35 import static com.android.internal.telephony.uicc.IccConstants.EF_DOMAIN;
     36 import static com.android.internal.telephony.uicc.IccConstants.EF_IMPI;
     37 import static com.android.internal.telephony.uicc.IccConstants.EF_IMPU;
     38 
     39 /**
     40  * {@hide}
     41  */
     42 public final class IsimUiccRecords extends IccRecords implements IsimRecords {
     43     protected static final String LOG_TAG = "IsimUiccRecords";
     44 
     45     private static final boolean DBG = true;
     46     private static final boolean DUMP_RECORDS = false;   // Note: PII is logged when this is true
     47 
     48     private static final int EVENT_APP_READY = 1;
     49 
     50     // ISIM EF records (see 3GPP TS 31.103)
     51     private String mIsimImpi;               // IMS private user identity
     52     private String mIsimDomain;             // IMS home network domain name
     53     private String[] mIsimImpu;             // IMS public user identity(s)
     54 
     55     private static final int TAG_ISIM_VALUE = 0x80;     // From 3GPP TS 31.103
     56 
     57     @Override
     58     public String toString() {
     59         return "IsimUiccRecords: " + super.toString()
     60                 + " mIsimImpi=" + mIsimImpi
     61                 + " mIsimDomain=" + mIsimDomain
     62                 + " mIsimImpu=" + mIsimImpu;
     63     }
     64 
     65     public IsimUiccRecords(UiccCardApplication app, Context c, CommandsInterface ci) {
     66         super(app, c, ci);
     67 
     68         mRecordsRequested = false;  // No load request is made till SIM ready
     69 
     70         // recordsToLoad is set to 0 because no requests are made yet
     71         mRecordsToLoad = 0;
     72 
     73         mParentApp.registerForReady(this, EVENT_APP_READY, null);
     74         if (DBG) log("IsimUiccRecords X ctor this=" + this);
     75     }
     76 
     77     @Override
     78     public void dispose() {
     79         log("Disposing " + this);
     80         //Unregister for all events
     81         mParentApp.unregisterForReady(this);
     82         resetRecords();
     83         super.dispose();
     84     }
     85 
     86     // ***** Overridden from Handler
     87     public void handleMessage(Message msg) {
     88         if (mDestroyed.get()) {
     89             Rlog.e(LOG_TAG, "Received message " + msg +
     90                     "[" + msg.what + "] while being destroyed. Ignoring.");
     91             return;
     92         }
     93 
     94         try {
     95             switch (msg.what) {
     96                 case EVENT_APP_READY:
     97                     onReady();
     98                     break;
     99 
    100                 default:
    101                     super.handleMessage(msg);   // IccRecords handles generic record load responses
    102 
    103             }
    104         } catch (RuntimeException exc) {
    105             // I don't want these exceptions to be fatal
    106             Rlog.w(LOG_TAG, "Exception parsing SIM record", exc);
    107         }
    108     }
    109 
    110     protected void fetchIsimRecords() {
    111         mRecordsRequested = true;
    112 
    113         mFh.loadEFTransparent(EF_IMPI, obtainMessage(
    114                 IccRecords.EVENT_GET_ICC_RECORD_DONE, new EfIsimImpiLoaded()));
    115         mRecordsToLoad++;
    116 
    117         mFh.loadEFLinearFixedAll(EF_IMPU, obtainMessage(
    118                 IccRecords.EVENT_GET_ICC_RECORD_DONE, new EfIsimImpuLoaded()));
    119         mRecordsToLoad++;
    120 
    121         mFh.loadEFTransparent(EF_DOMAIN, obtainMessage(
    122                 IccRecords.EVENT_GET_ICC_RECORD_DONE, new EfIsimDomainLoaded()));
    123         mRecordsToLoad++;
    124 
    125         log("fetchIsimRecords " + mRecordsToLoad);
    126     }
    127 
    128     protected void resetRecords() {
    129         // recordsRequested is set to false indicating that the SIM
    130         // read requests made so far are not valid. This is set to
    131         // true only when fresh set of read requests are made.
    132         mRecordsRequested = false;
    133     }
    134 
    135     private class EfIsimImpiLoaded implements IccRecords.IccRecordLoaded {
    136         public String getEfName() {
    137             return "EF_ISIM_IMPI";
    138         }
    139         public void onRecordLoaded(AsyncResult ar) {
    140             byte[] data = (byte[]) ar.result;
    141             mIsimImpi = isimTlvToString(data);
    142             if (DUMP_RECORDS) log("EF_IMPI=" + mIsimImpi);
    143         }
    144     }
    145 
    146     private class EfIsimImpuLoaded implements IccRecords.IccRecordLoaded {
    147         public String getEfName() {
    148             return "EF_ISIM_IMPU";
    149         }
    150         public void onRecordLoaded(AsyncResult ar) {
    151             ArrayList<byte[]> impuList = (ArrayList<byte[]>) ar.result;
    152             if (DBG) log("EF_IMPU record count: " + impuList.size());
    153             mIsimImpu = new String[impuList.size()];
    154             int i = 0;
    155             for (byte[] identity : impuList) {
    156                 String impu = isimTlvToString(identity);
    157                 if (DUMP_RECORDS) log("EF_IMPU[" + i + "]=" + impu);
    158                 mIsimImpu[i++] = impu;
    159             }
    160         }
    161     }
    162 
    163     private class EfIsimDomainLoaded implements IccRecords.IccRecordLoaded {
    164         public String getEfName() {
    165             return "EF_ISIM_DOMAIN";
    166         }
    167         public void onRecordLoaded(AsyncResult ar) {
    168             byte[] data = (byte[]) ar.result;
    169             mIsimDomain = isimTlvToString(data);
    170             if (DUMP_RECORDS) log("EF_DOMAIN=" + mIsimDomain);
    171         }
    172     }
    173 
    174     /**
    175      * ISIM records for IMS are stored inside a Tag-Length-Value record as a UTF-8 string
    176      * with tag value 0x80.
    177      * @param record the byte array containing the IMS data string
    178      * @return the decoded String value, or null if the record can't be decoded
    179      */
    180     private static String isimTlvToString(byte[] record) {
    181         SimTlv tlv = new SimTlv(record, 0, record.length);
    182         do {
    183             if (tlv.getTag() == TAG_ISIM_VALUE) {
    184                 return new String(tlv.getData(), Charset.forName("UTF-8"));
    185             }
    186         } while (tlv.nextObject());
    187 
    188         Rlog.e(LOG_TAG, "[ISIM] can't find TLV tag in ISIM record, returning null");
    189         return null;
    190     }
    191 
    192     @Override
    193     protected void onRecordLoaded() {
    194         // One record loaded successfully or failed, In either case
    195         // we need to update the recordsToLoad count
    196         mRecordsToLoad -= 1;
    197 
    198         if (mRecordsToLoad == 0 && mRecordsRequested == true) {
    199             onAllRecordsLoaded();
    200         } else if (mRecordsToLoad < 0) {
    201             loge("recordsToLoad <0, programmer error suspected");
    202             mRecordsToLoad = 0;
    203         }
    204     }
    205 
    206     @Override
    207     protected void onAllRecordsLoaded() {
    208         mRecordsLoadedRegistrants.notifyRegistrants(
    209                 new AsyncResult(null, null, null));
    210     }
    211 
    212     /**
    213      * Return the IMS private user identity (IMPI).
    214      * Returns null if the IMPI hasn't been loaded or isn't present on the ISIM.
    215      * @return the IMS private user identity string, or null if not available
    216      */
    217     @Override
    218     public String getIsimImpi() {
    219         return mIsimImpi;
    220     }
    221 
    222     /**
    223      * Return the IMS home network domain name.
    224      * Returns null if the IMS domain hasn't been loaded or isn't present on the ISIM.
    225      * @return the IMS home network domain name, or null if not available
    226      */
    227     @Override
    228     public String getIsimDomain() {
    229         return mIsimDomain;
    230     }
    231 
    232     /**
    233      * Return an array of IMS public user identities (IMPU).
    234      * Returns null if the IMPU hasn't been loaded or isn't present on the ISIM.
    235      * @return an array of IMS public user identity strings, or null if not available
    236      */
    237     @Override
    238     public String[] getIsimImpu() {
    239         return (mIsimImpu != null) ? mIsimImpu.clone() : null;
    240     }
    241 
    242     @Override
    243     public int getDisplayRule(String plmn) {
    244         // Not applicable to Isim
    245         return 0;
    246     }
    247 
    248     @Override
    249     public void onReady() {
    250         fetchIsimRecords();
    251     }
    252 
    253     @Override
    254     public void onRefresh(boolean fileChanged, int[] fileList) {
    255         // We do not handle it in Isim
    256     }
    257 
    258     @Override
    259     public void setVoiceMailNumber(String alphaTag, String voiceNumber,
    260             Message onComplete) {
    261         // Not applicable to Isim
    262     }
    263 
    264     @Override
    265     public void setVoiceMessageWaiting(int line, int countWaiting) {
    266         // Not applicable to Isim
    267     }
    268 
    269     @Override
    270     protected void log(String s) {
    271         if (DBG) Rlog.d(LOG_TAG, "[ISIM] " + s);
    272     }
    273 
    274     @Override
    275     protected void loge(String s) {
    276         if (DBG) Rlog.e(LOG_TAG, "[ISIM] " + s);
    277     }
    278 
    279     @Override
    280     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
    281         pw.println("IsimRecords: " + this);
    282         pw.println(" extends:");
    283         super.dump(fd, pw, args);
    284         pw.println(" mIsimImpi=" + mIsimImpi);
    285         pw.println(" mIsimDomain=" + mIsimDomain);
    286         pw.println(" mIsimImpu[]=" + Arrays.toString(mIsimImpu));
    287         pw.flush();
    288     }
    289 }
    290