Revert "Telephony: Voice mail notification related changes"
[android/platform/frameworks/opt/telephony.git] / src / java / com / android / internal / telephony / uicc / IsimUiccRecords.java
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 import android.content.Intent;
25
26
27 import com.android.internal.telephony.CommandsInterface;
28 import com.android.internal.telephony.gsm.SimTlv;
29 //import com.android.internal.telephony.gsm.VoiceMailConstants;
30
31 import java.io.FileDescriptor;
32 import java.io.PrintWriter;
33 import java.nio.charset.Charset;
34 import java.util.ArrayList;
35 import java.util.Arrays;
36
37 import static com.android.internal.telephony.uicc.IccConstants.EF_DOMAIN;
38 import static com.android.internal.telephony.uicc.IccConstants.EF_IMPI;
39 import static com.android.internal.telephony.uicc.IccConstants.EF_IMPU;
40 import static com.android.internal.telephony.uicc.IccConstants.EF_IST;
41 import static com.android.internal.telephony.uicc.IccConstants.EF_PCSCF;
42
43 /**
44  * {@hide}
45  */
46 public final class IsimUiccRecords extends IccRecords implements IsimRecords {
47     protected static final String LOG_TAG = "IsimUiccRecords";
48
49     private static final boolean DBG = true;
50     private static final boolean DUMP_RECORDS = true;   // Note: PII is logged when this is true
51     public static final String INTENT_ISIM_REFRESH = "com.android.intent.isim_refresh";
52
53     private static final int EVENT_APP_READY = 1;
54     private static final int EVENT_ISIM_REFRESH = 31;
55     private static final int EVENT_AKA_AUTHENTICATE_DONE          = 90;
56
57     // ISIM EF records (see 3GPP TS 31.103)
58     private String mIsimImpi;               // IMS private user identity
59     private String mIsimDomain;             // IMS home network domain name
60     private String[] mIsimImpu;             // IMS public user identity(s)
61     private String mIsimIst;             // IMS Service Table
62     private String[] mIsimPcscf;             // IMS Proxy Call Session Control Function
63     private String auth_rsp;
64
65     private final Object mLock = new Object();
66
67     private static final int TAG_ISIM_VALUE = 0x80;     // From 3GPP TS 31.103
68
69     @Override
70     public String toString() {
71         return "IsimUiccRecords: " + super.toString()
72                 + " mIsimImpi=" + mIsimImpi
73                 + " mIsimDomain=" + mIsimDomain
74                 + " mIsimImpu=" + mIsimImpu
75                 + " mIsimIst=" + mIsimIst
76                 + " mIsimPcscf=" + mIsimPcscf;
77     }
78
79     public IsimUiccRecords(UiccCardApplication app, Context c, CommandsInterface ci) {
80         super(app, c, ci);
81
82         mRecordsRequested = false;  // No load request is made till SIM ready
83
84         // recordsToLoad is set to 0 because no requests are made yet
85         mRecordsToLoad = 0;
86         // Start off by setting empty state
87         resetRecords();
88         mCi.registerForIccRefresh(this, EVENT_ISIM_REFRESH, null);
89
90         mParentApp.registerForReady(this, EVENT_APP_READY, null);
91         if (DBG) log("IsimUiccRecords X ctor this=" + this);
92     }
93
94     @Override
95     public void dispose() {
96         log("Disposing " + this);
97         //Unregister for all events
98         mCi.unregisterForIccRefresh(this);
99         mParentApp.unregisterForReady(this);
100         resetRecords();
101         super.dispose();
102     }
103
104     // ***** Overridden from Handler
105     public void handleMessage(Message msg) {
106         AsyncResult ar;
107
108         if (mDestroyed.get()) {
109             Rlog.e(LOG_TAG, "Received message " + msg +
110                     "[" + msg.what + "] while being destroyed. Ignoring.");
111             return;
112         }
113         loge("IsimUiccRecords: handleMessage " + msg + "[" + msg.what + "] ");
114
115         try {
116             switch (msg.what) {
117                 case EVENT_APP_READY:
118                     onReady();
119                     break;
120
121                 case EVENT_ISIM_REFRESH:
122                     ar = (AsyncResult)msg.obj;
123                     loge("ISim REFRESH(EVENT_ISIM_REFRESH) with exception: " + ar.exception);
124                     if (ar.exception == null) {
125                         Intent intent = new Intent(INTENT_ISIM_REFRESH);
126                         loge("send ISim REFRESH: " + INTENT_ISIM_REFRESH);
127                         mContext.sendBroadcast(intent);
128                         handleIsimRefresh((IccRefreshResponse)ar.result);
129                     }
130                     break;
131
132                 case EVENT_AKA_AUTHENTICATE_DONE:
133                     ar = (AsyncResult)msg.obj;
134                     log("EVENT_AKA_AUTHENTICATE_DONE");
135                     if (ar.exception != null) {
136                         log("Exception ISIM AKA: " + ar.exception);
137                     } else {
138                         try {
139                             auth_rsp = (String)ar.result;
140                             log("ISIM AKA: auth_rsp = " + auth_rsp);
141                         } catch (Exception e) {
142                             log("Failed to parse ISIM AKA contents: " + e);
143                         }
144                     }
145                     synchronized (mLock) {
146                         mLock.notifyAll();
147                     }
148
149                     break;
150
151                 default:
152                     super.handleMessage(msg);   // IccRecords handles generic record load responses
153
154             }
155         } catch (RuntimeException exc) {
156             // I don't want these exceptions to be fatal
157             Rlog.w(LOG_TAG, "Exception parsing SIM record", exc);
158         }
159     }
160
161     protected void fetchIsimRecords() {
162         mRecordsRequested = true;
163
164         mFh.loadEFTransparent(EF_IMPI, obtainMessage(
165                 IccRecords.EVENT_GET_ICC_RECORD_DONE, new EfIsimImpiLoaded()));
166         mRecordsToLoad++;
167
168         mFh.loadEFLinearFixedAll(EF_IMPU, obtainMessage(
169                 IccRecords.EVENT_GET_ICC_RECORD_DONE, new EfIsimImpuLoaded()));
170         mRecordsToLoad++;
171
172         mFh.loadEFTransparent(EF_DOMAIN, obtainMessage(
173                 IccRecords.EVENT_GET_ICC_RECORD_DONE, new EfIsimDomainLoaded()));
174         mRecordsToLoad++;
175         mFh.loadEFTransparent(EF_IST, obtainMessage(
176                     IccRecords.EVENT_GET_ICC_RECORD_DONE, new EfIsimIstLoaded()));
177         mRecordsToLoad++;
178         mFh.loadEFLinearFixedAll(EF_PCSCF, obtainMessage(
179                     IccRecords.EVENT_GET_ICC_RECORD_DONE, new EfIsimPcscfLoaded()));
180         mRecordsToLoad++;
181
182         if (DBG) log("fetchIsimRecords " + mRecordsToLoad + " requested: " + mRecordsRequested);
183     }
184
185     protected void resetRecords() {
186         // recordsRequested is set to false indicating that the SIM
187         // read requests made so far are not valid. This is set to
188         // true only when fresh set of read requests are made.
189         mIsimImpi = null;
190         mIsimDomain = null;
191         mIsimImpu = null;
192         mIsimIst = null;
193         mIsimPcscf = null;
194         auth_rsp = null;
195
196         mRecordsRequested = false;
197     }
198
199     private class EfIsimImpiLoaded implements IccRecords.IccRecordLoaded {
200         public String getEfName() {
201             return "EF_ISIM_IMPI";
202         }
203         public void onRecordLoaded(AsyncResult ar) {
204             byte[] data = (byte[]) ar.result;
205             mIsimImpi = isimTlvToString(data);
206             if (DUMP_RECORDS) log("EF_IMPI=" + mIsimImpi);
207         }
208     }
209
210     private class EfIsimImpuLoaded implements IccRecords.IccRecordLoaded {
211         public String getEfName() {
212             return "EF_ISIM_IMPU";
213         }
214         public void onRecordLoaded(AsyncResult ar) {
215             ArrayList<byte[]> impuList = (ArrayList<byte[]>) ar.result;
216             if (DBG) log("EF_IMPU record count: " + impuList.size());
217             mIsimImpu = new String[impuList.size()];
218             int i = 0;
219             for (byte[] identity : impuList) {
220                 String impu = isimTlvToString(identity);
221                 if (DUMP_RECORDS) log("EF_IMPU[" + i + "]=" + impu);
222                 mIsimImpu[i++] = impu;
223             }
224         }
225     }
226
227     private class EfIsimDomainLoaded implements IccRecords.IccRecordLoaded {
228         public String getEfName() {
229             return "EF_ISIM_DOMAIN";
230         }
231         public void onRecordLoaded(AsyncResult ar) {
232             byte[] data = (byte[]) ar.result;
233             mIsimDomain = isimTlvToString(data);
234             if (DUMP_RECORDS) log("EF_DOMAIN=" + mIsimDomain);
235         }
236     }
237
238     private class EfIsimIstLoaded implements IccRecords.IccRecordLoaded {
239         public String getEfName() {
240             return "EF_ISIM_IST";
241         }
242         public void onRecordLoaded(AsyncResult ar) {
243             byte[] data = (byte[]) ar.result;
244             mIsimIst = IccUtils.bytesToHexString(data);
245             if (DUMP_RECORDS) log("EF_IST=" + mIsimIst);
246         }
247     }
248     private class EfIsimPcscfLoaded implements IccRecords.IccRecordLoaded {
249         public String getEfName() {
250             return "EF_ISIM_PCSCF";
251         }
252         public void onRecordLoaded(AsyncResult ar) {
253             ArrayList<byte[]> pcscflist = (ArrayList<byte[]>) ar.result;
254             if (DBG) log("EF_PCSCF record count: " + pcscflist.size());
255             mIsimPcscf = new String[pcscflist.size()];
256             int i = 0;
257             for (byte[] identity : pcscflist) {
258                 String pcscf = isimTlvToString(identity);
259                 if (DUMP_RECORDS) log("EF_PCSCF[" + i + "]=" + pcscf);
260                 mIsimPcscf[i++] = pcscf;
261             }
262         }
263     }
264
265     /**
266      * ISIM records for IMS are stored inside a Tag-Length-Value record as a UTF-8 string
267      * with tag value 0x80.
268      * @param record the byte array containing the IMS data string
269      * @return the decoded String value, or null if the record can't be decoded
270      */
271     private static String isimTlvToString(byte[] record) {
272         SimTlv tlv = new SimTlv(record, 0, record.length);
273         do {
274             if (tlv.getTag() == TAG_ISIM_VALUE) {
275                 return new String(tlv.getData(), Charset.forName("UTF-8"));
276             }
277         } while (tlv.nextObject());
278
279         Rlog.e(LOG_TAG, "[ISIM] can't find TLV tag in ISIM record, returning null");
280         return null;
281     }
282
283     @Override
284     protected void onRecordLoaded() {
285         // One record loaded successfully or failed, In either case
286         // we need to update the recordsToLoad count
287         mRecordsToLoad -= 1;
288         if (DBG) log("onRecordLoaded " + mRecordsToLoad + " requested: " + mRecordsRequested);
289
290         if (mRecordsToLoad == 0 && mRecordsRequested == true) {
291             onAllRecordsLoaded();
292         } else if (mRecordsToLoad < 0) {
293             loge("recordsToLoad <0, programmer error suspected");
294             mRecordsToLoad = 0;
295         }
296     }
297
298     @Override
299     protected void onAllRecordsLoaded() {
300        if (DBG) log("record load complete");
301         mRecordsLoadedRegistrants.notifyRegistrants(
302                 new AsyncResult(null, null, null));
303     }
304
305     private void handleFileUpdate(int efid) {
306         switch (efid) {
307             case EF_IMPI:
308                 mFh.loadEFTransparent(EF_IMPI, obtainMessage(
309                             IccRecords.EVENT_GET_ICC_RECORD_DONE, new EfIsimImpiLoaded()));
310                 mRecordsToLoad++;
311                 break;
312
313             case EF_IMPU:
314                 mFh.loadEFLinearFixedAll(EF_IMPU, obtainMessage(
315                             IccRecords.EVENT_GET_ICC_RECORD_DONE, new EfIsimImpuLoaded()));
316                 mRecordsToLoad++;
317             break;
318
319             case EF_DOMAIN:
320                 mFh.loadEFTransparent(EF_DOMAIN, obtainMessage(
321                             IccRecords.EVENT_GET_ICC_RECORD_DONE, new EfIsimDomainLoaded()));
322                 mRecordsToLoad++;
323             break;
324
325             case EF_IST:
326                 mFh.loadEFTransparent(EF_IST, obtainMessage(
327                             IccRecords.EVENT_GET_ICC_RECORD_DONE, new EfIsimIstLoaded()));
328                 mRecordsToLoad++;
329             break;
330
331             case EF_PCSCF:
332                 mFh.loadEFLinearFixedAll(EF_PCSCF, obtainMessage(
333                             IccRecords.EVENT_GET_ICC_RECORD_DONE, new EfIsimPcscfLoaded()));
334                 mRecordsToLoad++;
335
336             default:
337                 fetchIsimRecords();
338                 break;
339         }
340     }
341
342     private void handleIsimRefresh(IccRefreshResponse refreshResponse) {
343         if (refreshResponse == null) {
344             if (DBG) log("handleIsimRefresh received without input");
345             return;
346         }
347
348         if (refreshResponse.aid != null &&
349                 !refreshResponse.aid.equals(mParentApp.getAid())) {
350             // This is for different app. Ignore.
351             if (DBG) log("handleIsimRefresh received different app");
352             return;
353         }
354
355         switch (refreshResponse.refreshResult) {
356             case IccRefreshResponse.REFRESH_RESULT_FILE_UPDATE:
357                 if (DBG) log("handleIsimRefresh with REFRESH_RESULT_FILE_UPDATE");
358                 handleFileUpdate(refreshResponse.efId);
359                 break;
360
361             case IccRefreshResponse.REFRESH_RESULT_INIT:
362                 if (DBG) log("handleIsimRefresh with REFRESH_RESULT_INIT");
363                 // need to reload all files (that we care about)
364                 // onIccRefreshInit();
365                 fetchIsimRecords();
366                 break;
367
368             case IccRefreshResponse.REFRESH_RESULT_RESET:
369                 if (DBG) log("handleIsimRefresh with REFRESH_RESULT_RESET");
370                 // need to reload all files (that we care about)
371                 if (requirePowerOffOnSimRefreshReset()) {
372                     mCi.setRadioPower(false, null);
373                     /* Note: no need to call setRadioPower(true).  Assuming the desired
374                     * radio power state is still ON (as tracked by ServiceStateTracker),
375                     * ServiceStateTracker will call setRadioPower when it receives the
376                     * RADIO_STATE_CHANGED notification for the power off.  And if the
377                     * desired power state has changed in the interim, we don't want to
378                     * override it with an unconditional power on.
379                     */
380                 }
381                 break;
382
383             default:
384                 // unknown refresh operation
385                 if (DBG) log("handleIsimRefresh with unknown operation");
386                 break;
387         }
388     }
389
390     /**
391      * Return the IMS private user identity (IMPI).
392      * Returns null if the IMPI hasn't been loaded or isn't present on the ISIM.
393      * @return the IMS private user identity string, or null if not available
394      */
395     @Override
396     public String getIsimImpi() {
397         return mIsimImpi;
398     }
399
400     /**
401      * Return the IMS home network domain name.
402      * Returns null if the IMS domain hasn't been loaded or isn't present on the ISIM.
403      * @return the IMS home network domain name, or null if not available
404      */
405     @Override
406     public String getIsimDomain() {
407         return mIsimDomain;
408     }
409
410     /**
411      * Return an array of IMS public user identities (IMPU).
412      * Returns null if the IMPU hasn't been loaded or isn't present on the ISIM.
413      * @return an array of IMS public user identity strings, or null if not available
414      */
415     @Override
416     public String[] getIsimImpu() {
417         return (mIsimImpu != null) ? mIsimImpu.clone() : null;
418     }
419
420     /**
421      * Returns the IMS Service Table (IST) that was loaded from the ISIM.
422      * @return IMS Service Table or null if not present or not loaded
423      */
424     @Override
425     public String getIsimIst() {
426         return mIsimIst;
427     }
428
429     /**
430      * Returns the IMS Proxy Call Session Control Function(PCSCF) that were loaded from the ISIM.
431      * @return an array of  PCSCF strings with one PCSCF per string, or null if
432      *      not present or not loaded
433      */
434     @Override
435     public String[] getIsimPcscf() {
436         return (mIsimPcscf != null) ? mIsimPcscf.clone() : null;
437     }
438
439     /**
440      * Returns the response of ISIM Authetification through RIL.
441      * Returns null if the Authentification hasn't been successed or isn't present iphonesubinfo.
442      * @return the response of ISIM Authetification, or null if not available
443      */
444     @Override
445     public String getIsimChallengeResponse(String nonce){
446         if (DBG) log("getIsimChallengeResponse-nonce:"+nonce);
447         try {
448             synchronized(mLock) {
449                 mCi.requestIsimAuthentication(nonce,obtainMessage(EVENT_AKA_AUTHENTICATE_DONE));
450                 try {
451                     mLock.wait();
452                 } catch (InterruptedException e) {
453                     log("interrupted while trying to request Isim Auth");
454                 }
455             }
456         } catch(Exception e) {
457             if (DBG) log( "Fail while trying to request Isim Auth");
458             return null;
459         }
460
461         if (DBG) log("getIsimChallengeResponse-auth_rsp"+auth_rsp);
462
463         return auth_rsp;
464     }
465
466     @Override
467     public int getDisplayRule(String plmn) {
468         // Not applicable to Isim
469         return 0;
470     }
471
472     @Override
473     public void onReady() {
474         fetchIsimRecords();
475     }
476
477     @Override
478     public void onRefresh(boolean fileChanged, int[] fileList) {
479         if (fileChanged) {
480             // A future optimization would be to inspect fileList and
481             // only reload those files that we care about.  For now,
482             // just re-fetch all SIM records that we cache.
483             fetchIsimRecords();
484         }
485     }
486
487     @Override
488     public void setVoiceMailNumber(String alphaTag, String voiceNumber,
489             Message onComplete) {
490         // Not applicable to Isim
491     }
492
493     @Override
494     public void setVoiceMessageWaiting(int line, int countWaiting) {
495         // Not applicable to Isim
496     }
497
498     @Override
499     protected void log(String s) {
500         if (DBG) Rlog.d(LOG_TAG, "[ISIM] " + s);
501     }
502
503     @Override
504     protected void loge(String s) {
505         if (DBG) Rlog.e(LOG_TAG, "[ISIM] " + s);
506     }
507
508     @Override
509     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
510         pw.println("IsimRecords: " + this);
511         pw.println(" extends:");
512         super.dump(fd, pw, args);
513         pw.println(" mIsimImpi=" + mIsimImpi);
514         pw.println(" mIsimDomain=" + mIsimDomain);
515         pw.println(" mIsimImpu[]=" + Arrays.toString(mIsimImpu));
516         pw.println(" mIsimIst" + mIsimIst);
517         pw.println(" mIsimPcscf"+mIsimPcscf);
518         pw.flush();
519     }
520 }