1 /* 2 * Copyright (C) 2006 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; 18 19 import android.compat.annotation.UnsupportedAppUsage; 20 import android.content.ContentProvider; 21 import android.content.ContentValues; 22 import android.content.UriMatcher; 23 import android.database.Cursor; 24 import android.database.MatrixCursor; 25 import android.database.MergeCursor; 26 import android.net.Uri; 27 import android.os.Build; 28 import android.os.RemoteException; 29 import android.telephony.SubscriptionInfo; 30 import android.telephony.SubscriptionManager; 31 import android.telephony.TelephonyFrameworkInitializer; 32 import android.text.TextUtils; 33 34 import com.android.internal.annotations.VisibleForTesting; 35 import com.android.internal.telephony.uicc.AdnRecord; 36 import com.android.internal.telephony.uicc.IccConstants; 37 import com.android.telephony.Rlog; 38 39 import java.util.List; 40 41 /** 42 * {@hide} 43 */ 44 public class IccProvider extends ContentProvider { 45 private static final String TAG = "IccProvider"; 46 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 47 private static final boolean DBG = true; 48 49 50 @UnsupportedAppUsage 51 private static final String[] ADDRESS_BOOK_COLUMN_NAMES = new String[] { 52 "name", 53 "number", 54 "emails", 55 "anrs", 56 "_id" 57 }; 58 59 protected static final int ADN = 1; 60 protected static final int ADN_SUB = 2; 61 protected static final int FDN = 3; 62 protected static final int FDN_SUB = 4; 63 protected static final int SDN = 5; 64 protected static final int SDN_SUB = 6; 65 protected static final int ADN_ALL = 7; 66 67 @VisibleForTesting 68 public static final String STR_TAG = "tag"; 69 @VisibleForTesting 70 public static final String STR_NUMBER = "number"; 71 @VisibleForTesting 72 public static final String STR_EMAILS = "emails"; 73 @VisibleForTesting 74 public static final String STR_ANRS = "anrs"; 75 @VisibleForTesting 76 public static final String STR_NEW_TAG = "newTag"; 77 @VisibleForTesting 78 public static final String STR_NEW_NUMBER = "newNumber"; 79 @VisibleForTesting 80 public static final String STR_NEW_EMAILS = "newEmails"; 81 @VisibleForTesting 82 public static final String STR_NEW_ANRS = "newAnrs"; 83 @VisibleForTesting 84 public static final String STR_PIN2 = "pin2"; 85 86 private static final UriMatcher URL_MATCHER = 87 new UriMatcher(UriMatcher.NO_MATCH); 88 89 static { 90 URL_MATCHER.addURI("icc", "adn", ADN); 91 URL_MATCHER.addURI("icc", "adn/subId/#", ADN_SUB); 92 URL_MATCHER.addURI("icc", "fdn", FDN); 93 URL_MATCHER.addURI("icc", "fdn/subId/#", FDN_SUB); 94 URL_MATCHER.addURI("icc", "sdn", SDN); 95 URL_MATCHER.addURI("icc", "sdn/subId/#", SDN_SUB); 96 } 97 98 private SubscriptionManager mSubscriptionManager; 99 100 @UnsupportedAppUsage IccProvider()101 public IccProvider() { 102 } 103 104 @Override onCreate()105 public boolean onCreate() { 106 mSubscriptionManager = SubscriptionManager.from(getContext()); 107 return true; 108 } 109 110 @Override query(Uri url, String[] projection, String selection, String[] selectionArgs, String sort)111 public Cursor query(Uri url, String[] projection, String selection, 112 String[] selectionArgs, String sort) { 113 if (DBG) log("query"); 114 115 switch (URL_MATCHER.match(url)) { 116 case ADN: 117 return loadFromEf(IccConstants.EF_ADN, 118 SubscriptionManager.getDefaultSubscriptionId()); 119 120 case ADN_SUB: 121 return loadFromEf(IccConstants.EF_ADN, getRequestSubId(url)); 122 123 case FDN: 124 return loadFromEf(IccConstants.EF_FDN, 125 SubscriptionManager.getDefaultSubscriptionId()); 126 127 case FDN_SUB: 128 return loadFromEf(IccConstants.EF_FDN, getRequestSubId(url)); 129 130 case SDN: 131 return loadFromEf(IccConstants.EF_SDN, 132 SubscriptionManager.getDefaultSubscriptionId()); 133 134 case SDN_SUB: 135 return loadFromEf(IccConstants.EF_SDN, getRequestSubId(url)); 136 137 case ADN_ALL: 138 return loadAllSimContacts(IccConstants.EF_ADN); 139 140 default: 141 throw new IllegalArgumentException("Unknown URL " + url); 142 } 143 } 144 loadAllSimContacts(int efType)145 private Cursor loadAllSimContacts(int efType) { 146 Cursor [] result; 147 List<SubscriptionInfo> subInfoList = mSubscriptionManager 148 .getActiveSubscriptionInfoList(false); 149 150 if ((subInfoList == null) || (subInfoList.size() == 0)) { 151 result = new Cursor[0]; 152 } else { 153 int subIdCount = subInfoList.size(); 154 result = new Cursor[subIdCount]; 155 int subId; 156 157 for (int i = 0; i < subIdCount; i++) { 158 subId = subInfoList.get(i).getSubscriptionId(); 159 result[i] = loadFromEf(efType, subId); 160 Rlog.i(TAG,"ADN Records loaded for Subscription ::" + subId); 161 } 162 } 163 164 return new MergeCursor(result); 165 } 166 167 @Override getType(Uri url)168 public String getType(Uri url) { 169 switch (URL_MATCHER.match(url)) { 170 case ADN: 171 case ADN_SUB: 172 case FDN: 173 case FDN_SUB: 174 case SDN: 175 case SDN_SUB: 176 case ADN_ALL: 177 return "vnd.android.cursor.dir/sim-contact"; 178 179 default: 180 throw new IllegalArgumentException("Unknown URL " + url); 181 } 182 } 183 184 @Override insert(Uri url, ContentValues initialValues)185 public Uri insert(Uri url, ContentValues initialValues) { 186 Uri resultUri; 187 int efType; 188 String pin2 = null; 189 int subId; 190 191 if (DBG) log("insert"); 192 193 int match = URL_MATCHER.match(url); 194 switch (match) { 195 case ADN: 196 efType = IccConstants.EF_ADN; 197 subId = SubscriptionManager.getDefaultSubscriptionId(); 198 break; 199 200 case ADN_SUB: 201 efType = IccConstants.EF_ADN; 202 subId = getRequestSubId(url); 203 break; 204 205 case FDN: 206 efType = IccConstants.EF_FDN; 207 subId = SubscriptionManager.getDefaultSubscriptionId(); 208 pin2 = initialValues.getAsString("pin2"); 209 break; 210 211 case FDN_SUB: 212 efType = IccConstants.EF_FDN; 213 subId = getRequestSubId(url); 214 pin2 = initialValues.getAsString("pin2"); 215 break; 216 217 default: 218 throw new UnsupportedOperationException( 219 "Cannot insert into URL: " + url); 220 } 221 222 // We're not using the incoming initialValues 223 // so we can check/gate the arguments. 224 String tag = initialValues.getAsString(STR_TAG); 225 String number = initialValues.getAsString(STR_NUMBER); 226 String emails = initialValues.getAsString(STR_EMAILS); 227 String anrs = initialValues.getAsString(STR_ANRS); 228 229 ContentValues values = new ContentValues(); 230 values.put(STR_NEW_TAG, tag); 231 values.put(STR_NEW_NUMBER, number); 232 values.put(STR_NEW_EMAILS, emails); 233 values.put(STR_NEW_ANRS, anrs); 234 boolean success = updateIccRecordInEf(efType, values, pin2, subId); 235 236 if (!success) { 237 return null; 238 } 239 240 StringBuilder buf = new StringBuilder("content://icc/"); 241 switch (match) { 242 case ADN: 243 buf.append("adn/"); 244 break; 245 246 case ADN_SUB: 247 buf.append("adn/subId/"); 248 break; 249 250 case FDN: 251 buf.append("fdn/"); 252 break; 253 254 case FDN_SUB: 255 buf.append("fdn/subId/"); 256 break; 257 } 258 259 // TODO: we need to find out the rowId for the newly added record 260 buf.append(0); 261 262 resultUri = Uri.parse(buf.toString()); 263 264 getContext().getContentResolver().notifyChange(url, null); 265 /* 266 // notify interested parties that an insertion happened 267 getContext().getContentResolver().notifyInsert( 268 resultUri, rowID, null); 269 */ 270 271 return resultUri; 272 } 273 normalizeValue(String inVal)274 private String normalizeValue(String inVal) { 275 int len = inVal.length(); 276 // If name is empty in contact return null to avoid crash. 277 if (len == 0) { 278 if (DBG) log("len of input String is 0"); 279 return inVal; 280 } 281 String retVal = inVal; 282 283 if (inVal.charAt(0) == '\'' && inVal.charAt(len-1) == '\'') { 284 retVal = inVal.substring(1, len-1); 285 } 286 287 return retVal; 288 } 289 290 @Override delete(Uri url, String where, String[] whereArgs)291 public int delete(Uri url, String where, String[] whereArgs) { 292 int efType; 293 int subId; 294 295 int match = URL_MATCHER.match(url); 296 switch (match) { 297 case ADN: 298 efType = IccConstants.EF_ADN; 299 subId = SubscriptionManager.getDefaultSubscriptionId(); 300 break; 301 302 case ADN_SUB: 303 efType = IccConstants.EF_ADN; 304 subId = getRequestSubId(url); 305 break; 306 307 case FDN: 308 efType = IccConstants.EF_FDN; 309 subId = SubscriptionManager.getDefaultSubscriptionId(); 310 break; 311 312 case FDN_SUB: 313 efType = IccConstants.EF_FDN; 314 subId = getRequestSubId(url); 315 break; 316 317 default: 318 throw new UnsupportedOperationException( 319 "Cannot insert into URL: " + url); 320 } 321 322 if (DBG) log("delete"); 323 324 // parse where clause 325 String tag = null; 326 String number = null; 327 String emails = null; 328 String anrs = null; 329 String pin2 = null; 330 331 String[] tokens = where.split(" AND "); 332 int n = tokens.length; 333 334 while (--n >= 0) { 335 String param = tokens[n]; 336 if (DBG) log("parsing '" + param + "'"); 337 338 String[] pair = param.split("=", 2); 339 340 if (pair.length != 2) { 341 Rlog.e(TAG, "resolve: bad whereClause parameter: " + param); 342 continue; 343 } 344 String key = pair[0].trim(); 345 String val = pair[1].trim(); 346 347 if (STR_TAG.equals(key)) { 348 tag = normalizeValue(val); 349 } else if (STR_NUMBER.equals(key)) { 350 number = normalizeValue(val); 351 } else if (STR_EMAILS.equals(key)) { 352 emails = normalizeValue(val); 353 } else if (STR_ANRS.equals(key)) { 354 anrs = normalizeValue(val); 355 } else if (STR_PIN2.equals(key)) { 356 pin2 = normalizeValue(val); 357 } 358 } 359 360 ContentValues values = new ContentValues(); 361 values.put(STR_TAG, tag); 362 values.put(STR_NUMBER, number); 363 values.put(STR_EMAILS, emails); 364 values.put(STR_ANRS, anrs); 365 if ((efType == FDN) && TextUtils.isEmpty(pin2)) { 366 return 0; 367 } 368 if (DBG) log("delete mvalues= " + values); 369 boolean success = updateIccRecordInEf(efType, values, pin2, subId); 370 if (!success) { 371 return 0; 372 } 373 374 getContext().getContentResolver().notifyChange(url, null); 375 return 1; 376 } 377 378 @Override update(Uri url, ContentValues values, String where, String[] whereArgs)379 public int update(Uri url, ContentValues values, String where, String[] whereArgs) { 380 String pin2 = null; 381 int efType; 382 int subId; 383 384 if (DBG) log("update"); 385 386 int match = URL_MATCHER.match(url); 387 switch (match) { 388 case ADN: 389 efType = IccConstants.EF_ADN; 390 subId = SubscriptionManager.getDefaultSubscriptionId(); 391 break; 392 393 case ADN_SUB: 394 efType = IccConstants.EF_ADN; 395 subId = getRequestSubId(url); 396 break; 397 398 case FDN: 399 efType = IccConstants.EF_FDN; 400 subId = SubscriptionManager.getDefaultSubscriptionId(); 401 pin2 = values.getAsString("pin2"); 402 break; 403 404 case FDN_SUB: 405 efType = IccConstants.EF_FDN; 406 subId = getRequestSubId(url); 407 pin2 = values.getAsString("pin2"); 408 break; 409 410 default: 411 throw new UnsupportedOperationException( 412 "Cannot insert into URL: " + url); 413 } 414 415 boolean success = updateIccRecordInEf(efType, values, pin2, subId); 416 417 if (!success) { 418 return 0; 419 } 420 421 getContext().getContentResolver().notifyChange(url, null); 422 return 1; 423 } 424 loadFromEf(int efType, int subId)425 private MatrixCursor loadFromEf(int efType, int subId) { 426 if (DBG) log("loadFromEf: efType=0x" + 427 Integer.toHexString(efType).toUpperCase() + ", subscription=" + subId); 428 429 List<AdnRecord> adnRecords = null; 430 try { 431 IIccPhoneBook iccIpb = IIccPhoneBook.Stub.asInterface( 432 TelephonyFrameworkInitializer 433 .getTelephonyServiceManager() 434 .getIccPhoneBookServiceRegisterer() 435 .get()); 436 if (iccIpb != null) { 437 adnRecords = iccIpb.getAdnRecordsInEfForSubscriber(subId, efType); 438 } 439 } catch (RemoteException ex) { 440 // ignore it 441 } catch (SecurityException ex) { 442 if (DBG) log(ex.toString()); 443 } 444 445 if (adnRecords != null) { 446 // Load the results 447 final int N = adnRecords.size(); 448 final MatrixCursor cursor = new MatrixCursor(ADDRESS_BOOK_COLUMN_NAMES, N); 449 if (DBG) log("adnRecords.size=" + N); 450 for (int i = 0; i < N ; i++) { 451 loadRecord(adnRecords.get(i), cursor, i); 452 } 453 return cursor; 454 } else { 455 // No results to load 456 Rlog.w(TAG, "Cannot load ADN records"); 457 return new MatrixCursor(ADDRESS_BOOK_COLUMN_NAMES); 458 } 459 } 460 461 private boolean updateIccRecordInEf(int efType, ContentValues values, String pin2, int subId)462 updateIccRecordInEf(int efType, ContentValues values, String pin2, int subId) { 463 boolean success = false; 464 if (DBG) log("updateIccRecordInEf: efType=" + efType + 465 ", values: [ "+ values + " ], subId:" + subId); 466 try { 467 IIccPhoneBook iccIpb = IIccPhoneBook.Stub.asInterface( 468 TelephonyFrameworkInitializer 469 .getTelephonyServiceManager() 470 .getIccPhoneBookServiceRegisterer() 471 .get()); 472 if (iccIpb != null) { 473 success = iccIpb 474 .updateAdnRecordsInEfBySearchForSubscriber( 475 subId, efType, values, pin2); 476 } 477 } catch (RemoteException ex) { 478 // ignore it 479 } catch (SecurityException ex) { 480 if (DBG) log(ex.toString()); 481 } 482 if (DBG) log("updateIccRecordInEf: " + success); 483 return success; 484 } 485 486 /** 487 * Loads an AdnRecord into a MatrixCursor. Must be called with mLock held. 488 * 489 * @param record the ADN record to load from 490 * @param cursor the cursor to receive the results 491 */ 492 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) loadRecord(AdnRecord record, MatrixCursor cursor, int id)493 private void loadRecord(AdnRecord record, MatrixCursor cursor, int id) { 494 if (!record.isEmpty()) { 495 Object[] contact = new Object[5]; 496 String alphaTag = record.getAlphaTag(); 497 String number = record.getNumber(); 498 499 if (DBG) log("loadRecord: " + alphaTag + ", " + Rlog.pii(TAG, number)); 500 contact[0] = alphaTag; 501 contact[1] = number; 502 503 String[] emails = record.getEmails(); 504 if (emails != null) { 505 StringBuilder emailString = new StringBuilder(); 506 for (String email: emails) { 507 log("Adding email:" + Rlog.pii(TAG, email)); 508 emailString.append(email); 509 emailString.append(","); 510 } 511 contact[2] = emailString.toString(); 512 } 513 514 String[] anrs = record.getAdditionalNumbers(); 515 if (anrs != null) { 516 StringBuilder anrString = new StringBuilder(); 517 for (String anr : anrs) { 518 if (DBG) log("Adding anr:" + anr); 519 anrString.append(anr); 520 anrString.append(":"); 521 } 522 contact[3] = anrString.toString(); 523 } 524 525 contact[4] = id; 526 cursor.addRow(contact); 527 } 528 } 529 530 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) log(String msg)531 private void log(String msg) { 532 Rlog.d(TAG, "[IccProvider] " + msg); 533 } 534 getRequestSubId(Uri url)535 private int getRequestSubId(Uri url) { 536 if (DBG) log("getRequestSubId url: " + url); 537 538 try { 539 return Integer.parseInt(url.getLastPathSegment()); 540 } catch (NumberFormatException ex) { 541 throw new IllegalArgumentException("Unknown URL " + url); 542 } 543 } 544 } 545