1 /*
2  * Copyright (C) 2007 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 package com.android.internal.telephony;
17 
18 import java.io.FileDescriptor;
19 import java.io.PrintWriter;
20 
21 import android.app.AppOpsManager;
22 import android.content.Context;
23 import android.content.pm.PackageManager;
24 import android.os.Binder;
25 import android.telephony.PhoneNumberUtils;
26 import android.telephony.Rlog;
27 
28 import com.android.internal.telephony.uicc.IsimRecords;
29 import com.android.internal.telephony.uicc.UiccCard;
30 import com.android.internal.telephony.uicc.UiccCardApplication;
31 
32 public class PhoneSubInfo {
33     static final String LOG_TAG = "PhoneSubInfo";
34     private static final boolean DBG = true;
35     private static final boolean VDBG = false; // STOPSHIP if true
36 
37     private Phone mPhone;
38     private Context mContext;
39     private AppOpsManager mAppOps;
40     private static final String READ_PHONE_STATE =
41         android.Manifest.permission.READ_PHONE_STATE;
42     // TODO: change getCompleteVoiceMailNumber() to require READ_PRIVILEGED_PHONE_STATE
43     private static final String CALL_PRIVILEGED =
44         android.Manifest.permission.CALL_PRIVILEGED;
45     private static final String READ_PRIVILEGED_PHONE_STATE =
46         android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE;
47 
PhoneSubInfo(Phone phone)48     public PhoneSubInfo(Phone phone) {
49         mPhone = phone;
50         mContext = phone.getContext();
51         mAppOps = mContext.getSystemService(AppOpsManager.class);
52     }
53 
dispose()54     public void dispose() {
55     }
56 
57     @Override
finalize()58     protected void finalize() {
59         try {
60             super.finalize();
61         } catch (Throwable throwable) {
62             loge("Error while finalizing:", throwable);
63         }
64         if (DBG) log("PhoneSubInfo finalized");
65     }
66 
67     /**
68      * Retrieves the unique device ID, e.g., IMEI for GSM phones and MEID for CDMA phones.
69      */
getDeviceId(String callingPackage)70     public String getDeviceId(String callingPackage) {
71         if (!checkReadPhoneState(callingPackage, "getDeviceId")) {
72             return null;
73         }
74         return mPhone.getDeviceId();
75     }
76 
77     /**
78      * Retrieves the IMEI.
79      */
getImei(String callingPackage)80     public String getImei(String callingPackage) {
81         if (!checkReadPhoneState(callingPackage, "getImei")) {
82             return null;
83         }
84         return mPhone.getImei();
85     }
86 
87     /**
88      * Retrieves the NAI.
89      */
getNai(String callingPackage)90     public String getNai(String callingPackage) {
91         if (!checkReadPhoneState(callingPackage, "getNai")) {
92             return null;
93         }
94         return mPhone.getNai();
95     }
96 
97     /**
98      * Retrieves the software version number for the device, e.g., IMEI/SV
99      * for GSM phones.
100      */
getDeviceSvn(String callingPackage)101     public String getDeviceSvn(String callingPackage) {
102         if (!checkReadPhoneState(callingPackage, "getDeviceSvn")) {
103             return null;
104         }
105 
106         return mPhone.getDeviceSvn();
107     }
108 
109     /**
110      * Retrieves the unique subscriber ID, e.g., IMSI for GSM phones.
111      */
getSubscriberId(String callingPackage)112     public String getSubscriberId(String callingPackage) {
113         if (!checkReadPhoneState(callingPackage, "getSubscriberId")) {
114             return null;
115         }
116         return mPhone.getSubscriberId();
117     }
118 
119     /**
120      * Retrieves the Group Identifier Level1 for GSM phones.
121      */
getGroupIdLevel1(String callingPackage)122     public String getGroupIdLevel1(String callingPackage) {
123         if (!checkReadPhoneState(callingPackage, "getGroupIdLevel1")) {
124             return null;
125         }
126         return mPhone.getGroupIdLevel1();
127     }
128 
129     /**
130      * Retrieves the serial number of the ICC, if applicable.
131      */
getIccSerialNumber(String callingPackage)132     public String getIccSerialNumber(String callingPackage) {
133         if (!checkReadPhoneState(callingPackage, "getIccSerialNumber")) {
134             return null;
135         }
136         return mPhone.getIccSerialNumber();
137     }
138 
139     /**
140      * Retrieves the phone number string for line 1.
141      */
getLine1Number(String callingPackage)142     public String getLine1Number(String callingPackage) {
143         // This is open to apps with WRITE_SMS.
144         if (!checkReadPhoneNumber(callingPackage, "getLine1Number")) {
145             return null;
146         }
147         return mPhone.getLine1Number();
148     }
149 
150     /**
151      * Retrieves the alpha identifier for line 1.
152      */
getLine1AlphaTag(String callingPackage)153     public String getLine1AlphaTag(String callingPackage) {
154         if (!checkReadPhoneState(callingPackage, "getLine1AlphaTag")) {
155             return null;
156         }
157         return mPhone.getLine1AlphaTag();
158     }
159 
160     /**
161      * Retrieves the MSISDN string.
162      */
getMsisdn(String callingPackage)163     public String getMsisdn(String callingPackage) {
164         if (!checkReadPhoneState(callingPackage, "getMsisdn")) {
165             return null;
166         }
167         return mPhone.getMsisdn();
168     }
169 
170     /**
171      * Retrieves the voice mail number.
172      */
getVoiceMailNumber(String callingPackage)173     public String getVoiceMailNumber(String callingPackage) {
174         if (!checkReadPhoneState(callingPackage, "getVoiceMailNumber")) {
175             return null;
176         }
177         String number = PhoneNumberUtils.extractNetworkPortion(mPhone.getVoiceMailNumber());
178         if (VDBG) log("VM: PhoneSubInfo.getVoiceMailNUmber: " + number);
179         return number;
180     }
181 
182     /**
183      * Retrieves the complete voice mail number.
184      *
185      * @hide
186      */
getCompleteVoiceMailNumber()187     public String getCompleteVoiceMailNumber() {
188         mContext.enforceCallingOrSelfPermission(CALL_PRIVILEGED,
189                 "Requires CALL_PRIVILEGED");
190         String number = mPhone.getVoiceMailNumber();
191         if (VDBG) log("VM: PhoneSubInfo.getCompleteVoiceMailNUmber: " + number);
192         return number;
193     }
194 
195     /**
196      * Retrieves the alpha identifier associated with the voice mail number.
197      */
getVoiceMailAlphaTag(String callingPackage)198     public String getVoiceMailAlphaTag(String callingPackage) {
199         if (!checkReadPhoneState(callingPackage, "getVoiceMailAlphaTag")) {
200             return null;
201         }
202         return mPhone.getVoiceMailAlphaTag();
203     }
204 
205     /**
206      * Returns the IMS private user identity (IMPI) that was loaded from the ISIM.
207      * @return the IMPI, or null if not present or not loaded
208      */
getIsimImpi()209     public String getIsimImpi() {
210         mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE,
211                 "Requires READ_PRIVILEGED_PHONE_STATE");
212         IsimRecords isim = mPhone.getIsimRecords();
213         if (isim != null) {
214             return isim.getIsimImpi();
215         } else {
216             return null;
217         }
218     }
219 
220     /**
221      * Returns the IMS home network domain name that was loaded from the ISIM.
222      * @return the IMS domain name, or null if not present or not loaded
223      */
getIsimDomain()224     public String getIsimDomain() {
225         mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE,
226                 "Requires READ_PRIVILEGED_PHONE_STATE");
227         IsimRecords isim = mPhone.getIsimRecords();
228         if (isim != null) {
229             return isim.getIsimDomain();
230         } else {
231             return null;
232         }
233     }
234 
235     /**
236      * Returns the IMS public user identities (IMPU) that were loaded from the ISIM.
237      * @return an array of IMPU strings, with one IMPU per string, or null if
238      *      not present or not loaded
239      */
getIsimImpu()240     public String[] getIsimImpu() {
241         mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE,
242                 "Requires READ_PRIVILEGED_PHONE_STATE");
243         IsimRecords isim = mPhone.getIsimRecords();
244         if (isim != null) {
245             return isim.getIsimImpu();
246         } else {
247             return null;
248         }
249     }
250 
251     /**
252      * Returns the IMS Service Table (IST) that was loaded from the ISIM.
253      * @return IMS Service Table or null if not present or not loaded
254      */
getIsimIst()255     public String getIsimIst(){
256         mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE,
257                 "Requires READ_PRIVILEGED_PHONE_STATE");
258         IsimRecords isim = mPhone.getIsimRecords();
259         if (isim != null) {
260             return isim.getIsimIst();
261         } else {
262             return null;
263         }
264      }
265 
266     /**
267      * Returns the IMS Proxy Call Session Control Function(PCSCF) that were loaded from the ISIM.
268      * @return an array of  PCSCF strings with one PCSCF per string, or null if
269      *      not present or not loaded
270      */
getIsimPcscf()271     public String[] getIsimPcscf() {
272         mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE,
273                 "Requires READ_PRIVILEGED_PHONE_STATE");
274         IsimRecords isim = mPhone.getIsimRecords();
275         if (isim != null) {
276             return isim.getIsimPcscf();
277         } else {
278             return null;
279         }
280     }
281 
282     /**
283      * Returns the response of ISIM Authetification through RIL.
284      * Returns null if the Authentification hasn't been successed or isn't present iphonesubinfo.
285      * @return the response of ISIM Authetification, or null if not available
286      */
getIsimChallengeResponse(String nonce)287     public String getIsimChallengeResponse(String nonce){
288         mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE,
289                 "Requires READ_PRIVILEGED_PHONE_STATE");
290         IsimRecords isim = mPhone.getIsimRecords();
291         if (isim != null) {
292             return isim.getIsimChallengeResponse(nonce);
293         } else {
294             return null;
295         }
296     }
297 
298     /**
299      * Returns the response of the SIM application on the UICC to authentication
300      * challenge/response algorithm. The data string and challenge response are
301      * Base64 encoded Strings.
302      * Can support EAP-SIM, EAP-AKA with results encoded per 3GPP TS 31.102.
303      *
304      * @param appType ICC application family (@see com.android.internal.telephony.PhoneConstants#APPTYPE_xxx)
305      * @param data authentication challenge data
306      * @return challenge response
307      */
getIccSimChallengeResponse(int subId, int appType, String data)308     public String getIccSimChallengeResponse(int subId, int appType, String data) {
309         // FIXME: use subId!!
310         mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE,
311                 "Requires READ_PRIVILEGED_PHONE_STATE");
312 
313         UiccCard uiccCard = mPhone.getUiccCard();
314         if (uiccCard == null) {
315             Rlog.e(LOG_TAG, "getIccSimChallengeResponse() UiccCard is null");
316             return null;
317         }
318 
319         UiccCardApplication uiccApp = uiccCard.getApplicationByType(appType);
320         if (uiccApp == null) {
321             Rlog.e(LOG_TAG, "getIccSimChallengeResponse() no app with specified type -- " +
322                     appType);
323             return null;
324         } else {
325             Rlog.e(LOG_TAG, "getIccSimChallengeResponse() found app " + uiccApp.getAid()
326                     + "specified type -- " + appType);
327         }
328 
329         int authContext = uiccApp.getAuthContext();
330 
331         if (data.length() < 32) {
332             /* must use EAP_SIM context */
333             Rlog.e(LOG_TAG, "data is too small to use EAP_AKA, using EAP_SIM instead");
334             authContext = UiccCardApplication.AUTH_CONTEXT_EAP_SIM;
335         }
336 
337         if(authContext == UiccCardApplication.AUTH_CONTEXT_UNDEFINED) {
338             Rlog.e(LOG_TAG, "getIccSimChallengeResponse() authContext undefined for app type " +
339                     appType);
340             return null;
341         }
342 
343         return uiccApp.getIccRecords().getIccSimChallengeResponse(authContext, data);
344     }
345 
log(String s)346     private void log(String s) {
347         Rlog.d(LOG_TAG, s);
348     }
349 
loge(String s, Throwable e)350     private void loge(String s, Throwable e) {
351         Rlog.e(LOG_TAG, s, e);
352     }
353 
dump(FileDescriptor fd, PrintWriter pw, String[] args)354     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
355         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
356                 != PackageManager.PERMISSION_GRANTED) {
357             pw.println("Permission Denial: can't dump PhoneSubInfo from from pid="
358                     + Binder.getCallingPid()
359                     + ", uid=" + Binder.getCallingUid());
360             return;
361         }
362 
363         pw.println("Phone Subscriber Info:");
364         pw.println("  Phone Type = " + mPhone.getPhoneName());
365         pw.println("  Device ID = " + mPhone.getDeviceId());
366     }
367 
checkReadPhoneState(String callingPackage, String message)368     private boolean checkReadPhoneState(String callingPackage, String message) {
369         try {
370             mContext.enforceCallingOrSelfPermission(
371                     android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, message);
372 
373             // SKIP checking run-time OP_READ_PHONE_STATE since self or using PRIVILEGED
374             return true;
375         } catch (SecurityException e) {
376             mContext.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PHONE_STATE,
377                     message);
378         }
379 
380         return mAppOps.noteOp(AppOpsManager.OP_READ_PHONE_STATE, Binder.getCallingUid(),
381             callingPackage) == AppOpsManager.MODE_ALLOWED;
382     }
383 
384 
385     /**
386      * Besides READ_PHONE_STATE, WRITE_SMS also allows apps to get phone numbers.
387      */
checkReadPhoneNumber(String callingPackage, String message)388     private boolean checkReadPhoneNumber(String callingPackage, String message) {
389         // Default SMS app can always read it.
390         if (mAppOps.noteOp(AppOpsManager.OP_WRITE_SMS,
391                 Binder.getCallingUid(), callingPackage) == AppOpsManager.MODE_ALLOWED) {
392             return true;
393         }
394         try {
395             return checkReadPhoneState(callingPackage, message);
396         } catch (SecurityException e) {
397             // Can be read with READ_SMS too.
398             mContext.enforceCallingOrSelfPermission(android.Manifest.permission.READ_SMS, message);
399             return mAppOps.noteOp(AppOpsManager.OP_READ_SMS,
400                     Binder.getCallingUid(), callingPackage) == AppOpsManager.MODE_ALLOWED;
401         }
402     }
403 }
404