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 48 public PhoneSubInfo(Phone phone) { 49 mPhone = phone; 50 mContext = phone.getContext(); 51 mAppOps = mContext.getSystemService(AppOpsManager.class); 52 } 53 54 public void dispose() { 55 } 56 57 @Override 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 346 private void log(String s) { 347 Rlog.d(LOG_TAG, s); 348 } 349 350 private void loge(String s, Throwable e) { 351 Rlog.e(LOG_TAG, s, e); 352 } 353 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 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 */ 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