• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.compat.annotation.UnsupportedAppUsage;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.os.AsyncResult;
23 import android.os.Build;
24 import android.os.Message;
25 import android.telephony.SubscriptionManager;
26 
27 import com.android.internal.telephony.CommandsInterface;
28 import com.android.internal.telephony.gsm.SimTlv;
29 import com.android.telephony.Rlog;
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 /**
38  * {@hide}
39  */
40 public class IsimUiccRecords extends IccRecords implements IsimRecords {
41     protected static final String LOG_TAG = "IsimUiccRecords";
42 
43     private static final boolean DBG = true;
44     private static final boolean VDBG = false; // STOPSHIP if true
45     private static final boolean DUMP_RECORDS = false;  // Note: PII is logged when this is true
46                                                         // STOPSHIP if true
47     public static final String INTENT_ISIM_REFRESH = "com.android.intent.isim_refresh";
48 
49     // ISIM EF records (see 3GPP TS 31.103)
50     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
51     private String mIsimImpi;               // IMS private user identity
52     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
53     private String mIsimDomain;             // IMS home network domain name
54     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
55     private String[] mIsimImpu;             // IMS public user identity(s)
56     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
57     private String mIsimIst;                // IMS Service Table
58     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
59     private String[] mIsimPcscf;            // IMS Proxy Call Session Control Function
60     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
61     private String auth_rsp;
62 
63     private static final int TAG_ISIM_VALUE = 0x80;     // From 3GPP TS 31.103
64 
65     @Override
toString()66     public String toString() {
67         return "IsimUiccRecords: " + super.toString()
68                 + (DUMP_RECORDS ? (" mIsimImpi=" + mIsimImpi
69                 + " mIsimDomain=" + mIsimDomain
70                 + " mIsimImpu=" + mIsimImpu
71                 + " mIsimIst=" + mIsimIst
72                 + " mIsimPcscf=" + mIsimPcscf) : "");
73     }
74 
IsimUiccRecords(UiccCardApplication app, Context c, CommandsInterface ci)75     public IsimUiccRecords(UiccCardApplication app, Context c, CommandsInterface ci) {
76         super(app, c, ci);
77 
78         mRecordsRequested = false;  // No load request is made till SIM ready
79         //todo: currently locked state for ISIM is not handled well and may cause app state to not
80         //be broadcast
81         mLockedRecordsReqReason = LOCKED_RECORDS_REQ_REASON_NONE;
82 
83         // recordsToLoad is set to 0 because no requests are made yet
84         mRecordsToLoad = 0;
85         // Start off by setting empty state
86         resetRecords();
87         if (DBG) log("IsimUiccRecords X ctor this=" + this);
88     }
89 
90     @Override
dispose()91     public void dispose() {
92         log("Disposing " + this);
93         resetRecords();
94         super.dispose();
95     }
96 
97     // ***** Overridden from Handler
handleMessage(Message msg)98     public void handleMessage(Message msg) {
99         AsyncResult ar;
100 
101         if (mDestroyed.get()) {
102             Rlog.e(LOG_TAG, "Received message " + msg +
103                     "[" + msg.what + "] while being destroyed. Ignoring.");
104             return;
105         }
106         loge("IsimUiccRecords: handleMessage " + msg + "[" + msg.what + "] ");
107 
108         try {
109             switch (msg.what) {
110                 case EVENT_REFRESH:
111                     broadcastRefresh();
112                     super.handleMessage(msg);
113                     break;
114 
115                 default:
116                     super.handleMessage(msg);   // IccRecords handles generic record load responses
117 
118             }
119         } catch (RuntimeException exc) {
120             // I don't want these exceptions to be fatal
121             Rlog.w(LOG_TAG, "Exception parsing SIM record", exc);
122         }
123     }
124 
125     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
fetchIsimRecords()126     protected void fetchIsimRecords() {
127         mRecordsRequested = true;
128 
129         mFh.loadEFTransparent(EF_IMPI, obtainMessage(
130                 IccRecords.EVENT_GET_ICC_RECORD_DONE, new EfIsimImpiLoaded()));
131         mRecordsToLoad++;
132 
133         mFh.loadEFLinearFixedAll(EF_IMPU, obtainMessage(
134                 IccRecords.EVENT_GET_ICC_RECORD_DONE, new EfIsimImpuLoaded()));
135         mRecordsToLoad++;
136 
137         mFh.loadEFTransparent(EF_DOMAIN, obtainMessage(
138                 IccRecords.EVENT_GET_ICC_RECORD_DONE, new EfIsimDomainLoaded()));
139         mRecordsToLoad++;
140         mFh.loadEFTransparent(EF_IST, obtainMessage(
141                     IccRecords.EVENT_GET_ICC_RECORD_DONE, new EfIsimIstLoaded()));
142         mRecordsToLoad++;
143         mFh.loadEFLinearFixedAll(EF_PCSCF, obtainMessage(
144                     IccRecords.EVENT_GET_ICC_RECORD_DONE, new EfIsimPcscfLoaded()));
145         mRecordsToLoad++;
146 
147         if (DBG) log("fetchIsimRecords " + mRecordsToLoad + " requested: " + mRecordsRequested);
148     }
149 
resetRecords()150     protected void resetRecords() {
151         // recordsRequested is set to false indicating that the SIM
152         // read requests made so far are not valid. This is set to
153         // true only when fresh set of read requests are made.
154         mIsimImpi = null;
155         mIsimDomain = null;
156         mIsimImpu = null;
157         mIsimIst = null;
158         mIsimPcscf = null;
159         auth_rsp = null;
160 
161         mRecordsRequested = false;
162         mLockedRecordsReqReason = LOCKED_RECORDS_REQ_REASON_NONE;
163         mLoaded.set(false);
164     }
165 
166     private class EfIsimImpiLoaded implements IccRecords.IccRecordLoaded {
getEfName()167         public String getEfName() {
168             return "EF_ISIM_IMPI";
169         }
onRecordLoaded(AsyncResult ar)170         public void onRecordLoaded(AsyncResult ar) {
171             byte[] data = (byte[]) ar.result;
172             mIsimImpi = isimTlvToString(data);
173             if (DUMP_RECORDS) log("EF_IMPI=" + mIsimImpi);
174         }
175     }
176 
177     private class EfIsimImpuLoaded implements IccRecords.IccRecordLoaded {
getEfName()178         public String getEfName() {
179             return "EF_ISIM_IMPU";
180         }
onRecordLoaded(AsyncResult ar)181         public void onRecordLoaded(AsyncResult ar) {
182             ArrayList<byte[]> impuList = (ArrayList<byte[]>) ar.result;
183             if (DBG) log("EF_IMPU record count: " + impuList.size());
184             mIsimImpu = new String[impuList.size()];
185             int i = 0;
186             for (byte[] identity : impuList) {
187                 String impu = isimTlvToString(identity);
188                 if (DUMP_RECORDS) log("EF_IMPU[" + i + "]=" + impu);
189                 mIsimImpu[i++] = impu;
190             }
191         }
192     }
193 
194     private class EfIsimDomainLoaded implements IccRecords.IccRecordLoaded {
getEfName()195         public String getEfName() {
196             return "EF_ISIM_DOMAIN";
197         }
onRecordLoaded(AsyncResult ar)198         public void onRecordLoaded(AsyncResult ar) {
199             byte[] data = (byte[]) ar.result;
200             mIsimDomain = isimTlvToString(data);
201             if (DUMP_RECORDS) log("EF_DOMAIN=" + mIsimDomain);
202         }
203     }
204 
205     private class EfIsimIstLoaded implements IccRecords.IccRecordLoaded {
getEfName()206         public String getEfName() {
207             return "EF_ISIM_IST";
208         }
onRecordLoaded(AsyncResult ar)209         public void onRecordLoaded(AsyncResult ar) {
210             byte[] data = (byte[]) ar.result;
211             mIsimIst = IccUtils.bytesToHexString(data);
212             if (DUMP_RECORDS) log("EF_IST=" + mIsimIst);
213         }
214     }
215     private class EfIsimPcscfLoaded implements IccRecords.IccRecordLoaded {
getEfName()216         public String getEfName() {
217             return "EF_ISIM_PCSCF";
218         }
onRecordLoaded(AsyncResult ar)219         public void onRecordLoaded(AsyncResult ar) {
220             ArrayList<byte[]> pcscflist = (ArrayList<byte[]>) ar.result;
221             if (DBG) log("EF_PCSCF record count: " + pcscflist.size());
222             mIsimPcscf = new String[pcscflist.size()];
223             int i = 0;
224             for (byte[] identity : pcscflist) {
225                 String pcscf = isimTlvToString(identity);
226                 if (DUMP_RECORDS) log("EF_PCSCF[" + i + "]=" + pcscf);
227                 mIsimPcscf[i++] = pcscf;
228             }
229         }
230     }
231 
232     /**
233      * ISIM records for IMS are stored inside a Tag-Length-Value record as a UTF-8 string
234      * with tag value 0x80.
235      * @param record the byte array containing the IMS data string
236      * @return the decoded String value, or null if the record can't be decoded
237      */
238     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
isimTlvToString(byte[] record)239     private static String isimTlvToString(byte[] record) {
240         SimTlv tlv = new SimTlv(record, 0, record.length);
241         do {
242             if (tlv.getTag() == TAG_ISIM_VALUE) {
243                 return new String(tlv.getData(), Charset.forName("UTF-8"));
244             }
245         } while (tlv.nextObject());
246 
247         if (VDBG) {
248             Rlog.d(LOG_TAG, "[ISIM] can't find TLV. record = " + IccUtils.bytesToHexString(record));
249         }
250         return null;
251     }
252 
253     @Override
onRecordLoaded()254     protected void onRecordLoaded() {
255         // One record loaded successfully or failed, In either case
256         // we need to update the recordsToLoad count
257         mRecordsToLoad -= 1;
258         if (DBG) log("onRecordLoaded " + mRecordsToLoad + " requested: " + mRecordsRequested);
259 
260         if (getRecordsLoaded()) {
261             onAllRecordsLoaded();
262         } else if (getLockedRecordsLoaded() || getNetworkLockedRecordsLoaded()) {
263             onLockedAllRecordsLoaded();
264         } else if (mRecordsToLoad < 0) {
265             loge("recordsToLoad <0, programmer error suspected");
266             mRecordsToLoad = 0;
267         }
268     }
269 
onLockedAllRecordsLoaded()270     private void onLockedAllRecordsLoaded() {
271         if (DBG) log("SIM locked; record load complete");
272         if (mLockedRecordsReqReason == LOCKED_RECORDS_REQ_REASON_LOCKED) {
273             mLockedRecordsLoadedRegistrants.notifyRegistrants(new AsyncResult(null, null, null));
274         } else if (mLockedRecordsReqReason == LOCKED_RECORDS_REQ_REASON_NETWORK_LOCKED) {
275             mNetworkLockedRecordsLoadedRegistrants.notifyRegistrants(
276                     new AsyncResult(null, null, null));
277         } else {
278             loge("onLockedAllRecordsLoaded: unexpected mLockedRecordsReqReason "
279                     + mLockedRecordsReqReason);
280         }
281     }
282 
283     @Override
onAllRecordsLoaded()284     protected void onAllRecordsLoaded() {
285        if (DBG) log("record load complete");
286         mLoaded.set(true);
287         mRecordsLoadedRegistrants.notifyRegistrants(new AsyncResult(null, null, null));
288     }
289 
290     @Override
handleFileUpdate(int efid)291     protected void handleFileUpdate(int efid) {
292         switch (efid) {
293             case EF_IMPI:
294                 mFh.loadEFTransparent(EF_IMPI, obtainMessage(
295                             IccRecords.EVENT_GET_ICC_RECORD_DONE, new EfIsimImpiLoaded()));
296                 mRecordsToLoad++;
297                 break;
298 
299             case EF_IMPU:
300                 mFh.loadEFLinearFixedAll(EF_IMPU, obtainMessage(
301                             IccRecords.EVENT_GET_ICC_RECORD_DONE, new EfIsimImpuLoaded()));
302                 mRecordsToLoad++;
303             break;
304 
305             case EF_DOMAIN:
306                 mFh.loadEFTransparent(EF_DOMAIN, obtainMessage(
307                             IccRecords.EVENT_GET_ICC_RECORD_DONE, new EfIsimDomainLoaded()));
308                 mRecordsToLoad++;
309             break;
310 
311             case EF_IST:
312                 mFh.loadEFTransparent(EF_IST, obtainMessage(
313                             IccRecords.EVENT_GET_ICC_RECORD_DONE, new EfIsimIstLoaded()));
314                 mRecordsToLoad++;
315             break;
316 
317             case EF_PCSCF:
318                 mFh.loadEFLinearFixedAll(EF_PCSCF, obtainMessage(
319                             IccRecords.EVENT_GET_ICC_RECORD_DONE, new EfIsimPcscfLoaded()));
320                 mRecordsToLoad++;
321 
322             default:
323                 mLoaded.set(false);
324                 fetchIsimRecords();
325                 break;
326         }
327     }
328 
broadcastRefresh()329     private void broadcastRefresh() {
330         Intent intent = new Intent(INTENT_ISIM_REFRESH);
331         log("send ISim REFRESH: " + INTENT_ISIM_REFRESH);
332         SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mParentApp.getPhoneId());
333         mContext.sendBroadcast(intent);
334     }
335 
336     /**
337      * Return the IMS private user identity (IMPI).
338      * Returns null if the IMPI hasn't been loaded or isn't present on the ISIM.
339      * @return the IMS private user identity string, or null if not available
340      */
341     @Override
getIsimImpi()342     public String getIsimImpi() {
343         return mIsimImpi;
344     }
345 
346     /**
347      * Return the IMS home network domain name.
348      * Returns null if the IMS domain hasn't been loaded or isn't present on the ISIM.
349      * @return the IMS home network domain name, or null if not available
350      */
351     @Override
getIsimDomain()352     public String getIsimDomain() {
353         return mIsimDomain;
354     }
355 
356     /**
357      * Return an array of IMS public user identities (IMPU).
358      * Returns null if the IMPU hasn't been loaded or isn't present on the ISIM.
359      * @return an array of IMS public user identity strings, or null if not available
360      */
361     @Override
getIsimImpu()362     public String[] getIsimImpu() {
363         return (mIsimImpu != null) ? mIsimImpu.clone() : null;
364     }
365 
366     /**
367      * Returns the IMS Service Table (IST) that was loaded from the ISIM.
368      * @return IMS Service Table or null if not present or not loaded
369      */
370     @Override
getIsimIst()371     public String getIsimIst() {
372         return mIsimIst;
373     }
374 
375     /**
376      * Returns the IMS Proxy Call Session Control Function(PCSCF) that were loaded from the ISIM.
377      * @return an array of  PCSCF strings with one PCSCF per string, or null if
378      *      not present or not loaded
379      */
380     @Override
getIsimPcscf()381     public String[] getIsimPcscf() {
382         return (mIsimPcscf != null) ? mIsimPcscf.clone() : null;
383     }
384 
385     @Override
onReady()386     public void onReady() {
387         fetchIsimRecords();
388     }
389 
390     @Override
onRefresh(boolean fileChanged, int[] fileList)391     public void onRefresh(boolean fileChanged, int[] fileList) {
392         if (fileChanged) {
393             // A future optimization would be to inspect fileList and
394             // only reload those files that we care about.  For now,
395             // just re-fetch all SIM records that we cache.
396             fetchIsimRecords();
397         }
398     }
399 
400     @Override
setVoiceMailNumber(String alphaTag, String voiceNumber, Message onComplete)401     public void setVoiceMailNumber(String alphaTag, String voiceNumber,
402             Message onComplete) {
403         // Not applicable to Isim
404     }
405 
406     @Override
setVoiceMessageWaiting(int line, int countWaiting)407     public void setVoiceMessageWaiting(int line, int countWaiting) {
408         // Not applicable to Isim
409     }
410 
411     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
412     @Override
log(String s)413     protected void log(String s) {
414         if (mParentApp != null) {
415             Rlog.d(LOG_TAG, "[ISIM-" + mParentApp.getPhoneId() + "] " + s);
416         } else {
417             Rlog.d(LOG_TAG, "[ISIM] " + s);
418         }
419     }
420 
421     @Override
loge(String s)422     protected void loge(String s) {
423         if (mParentApp != null) {
424             Rlog.e(LOG_TAG, "[ISIM-" + mParentApp.getPhoneId() + "] " + s);
425         } else {
426             Rlog.e(LOG_TAG, "[ISIM] " + s);
427         }
428     }
429 
430     @Override
dump(FileDescriptor fd, PrintWriter pw, String[] args)431     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
432         pw.println("IsimRecords: " + this);
433         pw.println(" extends:");
434         super.dump(fd, pw, args);
435         if (DUMP_RECORDS) {
436             pw.println(" mIsimImpi=" + mIsimImpi);
437             pw.println(" mIsimDomain=" + mIsimDomain);
438             pw.println(" mIsimImpu[]=" + Arrays.toString(mIsimImpu));
439             pw.println(" mIsimIst" + mIsimIst);
440             pw.println(" mIsimPcscf" + mIsimPcscf);
441         }
442         pw.flush();
443     }
444 
445     @Override
getVoiceMessageCount()446     public int getVoiceMessageCount() {
447         return 0; // Not applicable to Isim
448     }
449 
450 }
451