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