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 17package com.android.internal.telephony; 18 19import android.content.ContentProvider; 20import android.content.UriMatcher; 21import android.content.ContentValues; 22import android.database.Cursor; 23import android.database.MatrixCursor; 24import android.net.Uri; 25import android.os.RemoteException; 26import android.os.ServiceManager; 27import android.text.TextUtils; 28import android.util.Log; 29 30import java.util.List; 31 32import com.android.internal.telephony.IccConstants; 33import com.android.internal.telephony.AdnRecord; 34import com.android.internal.telephony.IIccPhoneBook; 35 36 37/** 38 * {@hide} 39 */ 40public class IccProvider extends ContentProvider { 41 private static final String TAG = "IccProvider"; 42 private static final boolean DBG = false; 43 44 45 private static final String[] ADDRESS_BOOK_COLUMN_NAMES = new String[] { 46 "name", 47 "number", 48 "emails", 49 "_id" 50 }; 51 52 private static final int ADN = 1; 53 private static final int FDN = 2; 54 private static final int SDN = 3; 55 56 private static final String STR_TAG = "tag"; 57 private static final String STR_NUMBER = "number"; 58 private static final String STR_EMAILS = "emails"; 59 private static final String STR_PIN2 = "pin2"; 60 61 private static final UriMatcher URL_MATCHER = 62 new UriMatcher(UriMatcher.NO_MATCH); 63 64 static { 65 URL_MATCHER.addURI("icc", "adn", ADN); 66 URL_MATCHER.addURI("icc", "fdn", FDN); 67 URL_MATCHER.addURI("icc", "sdn", SDN); 68 } 69 70 71 @Override 72 public boolean onCreate() { 73 return true; 74 } 75 76 @Override 77 public Cursor query(Uri url, String[] projection, String selection, 78 String[] selectionArgs, String sort) { 79 switch (URL_MATCHER.match(url)) { 80 case ADN: 81 return loadFromEf(IccConstants.EF_ADN); 82 83 case FDN: 84 return loadFromEf(IccConstants.EF_FDN); 85 86 case SDN: 87 return loadFromEf(IccConstants.EF_SDN); 88 89 default: 90 throw new IllegalArgumentException("Unknown URL " + url); 91 } 92 } 93 94 @Override 95 public String getType(Uri url) { 96 switch (URL_MATCHER.match(url)) { 97 case ADN: 98 case FDN: 99 case SDN: 100 return "vnd.android.cursor.dir/sim-contact"; 101 102 default: 103 throw new IllegalArgumentException("Unknown URL " + url); 104 } 105 } 106 107 @Override 108 public Uri insert(Uri url, ContentValues initialValues) { 109 Uri resultUri; 110 int efType; 111 String pin2 = null; 112 113 if (DBG) log("insert"); 114 115 int match = URL_MATCHER.match(url); 116 switch (match) { 117 case ADN: 118 efType = IccConstants.EF_ADN; 119 break; 120 121 case FDN: 122 efType = IccConstants.EF_FDN; 123 pin2 = initialValues.getAsString("pin2"); 124 break; 125 126 default: 127 throw new UnsupportedOperationException( 128 "Cannot insert into URL: " + url); 129 } 130 131 String tag = initialValues.getAsString("tag"); 132 String number = initialValues.getAsString("number"); 133 // TODO(): Read email instead of sending null. 134 boolean success = addIccRecordToEf(efType, tag, number, null, pin2); 135 136 if (!success) { 137 return null; 138 } 139 140 StringBuilder buf = new StringBuilder("content://icc/"); 141 switch (match) { 142 case ADN: 143 buf.append("adn/"); 144 break; 145 146 case FDN: 147 buf.append("fdn/"); 148 break; 149 } 150 151 // TODO: we need to find out the rowId for the newly added record 152 buf.append(0); 153 154 resultUri = Uri.parse(buf.toString()); 155 156 /* 157 // notify interested parties that an insertion happened 158 getContext().getContentResolver().notifyInsert( 159 resultUri, rowID, null); 160 */ 161 162 return resultUri; 163 } 164 165 private String normalizeValue(String inVal) { 166 int len = inVal.length(); 167 String retVal = inVal; 168 169 if (inVal.charAt(0) == '\'' && inVal.charAt(len-1) == '\'') { 170 retVal = inVal.substring(1, len-1); 171 } 172 173 return retVal; 174 } 175 176 @Override 177 public int delete(Uri url, String where, String[] whereArgs) { 178 int efType; 179 180 if (DBG) log("delete"); 181 182 int match = URL_MATCHER.match(url); 183 switch (match) { 184 case ADN: 185 efType = IccConstants.EF_ADN; 186 break; 187 188 case FDN: 189 efType = IccConstants.EF_FDN; 190 break; 191 192 default: 193 throw new UnsupportedOperationException( 194 "Cannot insert into URL: " + url); 195 } 196 197 // parse where clause 198 String tag = null; 199 String number = null; 200 String[] emails = null; 201 String pin2 = null; 202 203 String[] tokens = where.split("AND"); 204 int n = tokens.length; 205 206 while (--n >= 0) { 207 String param = tokens[n]; 208 if (DBG) log("parsing '" + param + "'"); 209 210 String[] pair = param.split("="); 211 212 if (pair.length != 2) { 213 Log.e(TAG, "resolve: bad whereClause parameter: " + param); 214 continue; 215 } 216 217 String key = pair[0].trim(); 218 String val = pair[1].trim(); 219 220 if (STR_TAG.equals(key)) { 221 tag = normalizeValue(val); 222 } else if (STR_NUMBER.equals(key)) { 223 number = normalizeValue(val); 224 } else if (STR_EMAILS.equals(key)) { 225 //TODO(): Email is null. 226 emails = null; 227 } else if (STR_PIN2.equals(key)) { 228 pin2 = normalizeValue(val); 229 } 230 } 231 232 if (TextUtils.isEmpty(number)) { 233 return 0; 234 } 235 236 if (efType == IccConstants.EF_FDN && TextUtils.isEmpty(pin2)) { 237 return 0; 238 } 239 240 boolean success = deleteIccRecordFromEf(efType, tag, number, emails, pin2); 241 if (!success) { 242 return 0; 243 } 244 245 return 1; 246 } 247 248 @Override 249 public int update(Uri url, ContentValues values, String where, String[] whereArgs) { 250 int efType; 251 String pin2 = null; 252 253 if (DBG) log("update"); 254 255 int match = URL_MATCHER.match(url); 256 switch (match) { 257 case ADN: 258 efType = IccConstants.EF_ADN; 259 break; 260 261 case FDN: 262 efType = IccConstants.EF_FDN; 263 pin2 = values.getAsString("pin2"); 264 break; 265 266 default: 267 throw new UnsupportedOperationException( 268 "Cannot insert into URL: " + url); 269 } 270 271 String tag = values.getAsString("tag"); 272 String number = values.getAsString("number"); 273 String[] emails = null; 274 String newTag = values.getAsString("newTag"); 275 String newNumber = values.getAsString("newNumber"); 276 String[] newEmails = null; 277 // TODO(): Update for email. 278 boolean success = updateIccRecordInEf(efType, tag, number, 279 newTag, newNumber, pin2); 280 281 if (!success) { 282 return 0; 283 } 284 285 return 1; 286 } 287 288 private MatrixCursor loadFromEf(int efType) { 289 if (DBG) log("loadFromEf: efType=" + efType); 290 291 List<AdnRecord> adnRecords = null; 292 try { 293 IIccPhoneBook iccIpb = IIccPhoneBook.Stub.asInterface( 294 ServiceManager.getService("simphonebook")); 295 if (iccIpb != null) { 296 adnRecords = iccIpb.getAdnRecordsInEf(efType); 297 } 298 } catch (RemoteException ex) { 299 // ignore it 300 } catch (SecurityException ex) { 301 if (DBG) log(ex.toString()); 302 } 303 304 if (adnRecords != null) { 305 // Load the results 306 final int N = adnRecords.size(); 307 final MatrixCursor cursor = new MatrixCursor(ADDRESS_BOOK_COLUMN_NAMES, N); 308 if (DBG) log("adnRecords.size=" + N); 309 for (int i = 0; i < N ; i++) { 310 loadRecord(adnRecords.get(i), cursor, i); 311 } 312 return cursor; 313 } else { 314 // No results to load 315 Log.w(TAG, "Cannot load ADN records"); 316 return new MatrixCursor(ADDRESS_BOOK_COLUMN_NAMES); 317 } 318 } 319 320 private boolean 321 addIccRecordToEf(int efType, String name, String number, String[] emails, String pin2) { 322 if (DBG) log("addIccRecordToEf: efType=" + efType + ", name=" + name + 323 ", number=" + number + ", emails=" + emails); 324 325 boolean success = false; 326 327 // TODO: do we need to call getAdnRecordsInEf() before calling 328 // updateAdnRecordsInEfBySearch()? In any case, we will leave 329 // the UI level logic to fill that prereq if necessary. But 330 // hopefully, we can remove this requirement. 331 332 try { 333 IIccPhoneBook iccIpb = IIccPhoneBook.Stub.asInterface( 334 ServiceManager.getService("simphonebook")); 335 if (iccIpb != null) { 336 success = iccIpb.updateAdnRecordsInEfBySearch(efType, "", "", 337 name, number, pin2); 338 } 339 } catch (RemoteException ex) { 340 // ignore it 341 } catch (SecurityException ex) { 342 if (DBG) log(ex.toString()); 343 } 344 if (DBG) log("addIccRecordToEf: " + success); 345 return success; 346 } 347 348 private boolean 349 updateIccRecordInEf(int efType, String oldName, String oldNumber, 350 String newName, String newNumber, String pin2) { 351 if (DBG) log("updateIccRecordInEf: efType=" + efType + 352 ", oldname=" + oldName + ", oldnumber=" + oldNumber + 353 ", newname=" + newName + ", newnumber=" + newNumber); 354 boolean success = false; 355 356 try { 357 IIccPhoneBook iccIpb = IIccPhoneBook.Stub.asInterface( 358 ServiceManager.getService("simphonebook")); 359 if (iccIpb != null) { 360 success = iccIpb.updateAdnRecordsInEfBySearch(efType, 361 oldName, oldNumber, newName, newNumber, pin2); 362 } 363 } catch (RemoteException ex) { 364 // ignore it 365 } catch (SecurityException ex) { 366 if (DBG) log(ex.toString()); 367 } 368 if (DBG) log("updateIccRecordInEf: " + success); 369 return success; 370 } 371 372 373 private boolean deleteIccRecordFromEf(int efType, String name, String number, String[] emails, 374 String pin2) { 375 if (DBG) log("deleteIccRecordFromEf: efType=" + efType + 376 ", name=" + name + ", number=" + number + ", emails=" + emails + ", pin2=" + pin2); 377 378 boolean success = false; 379 380 try { 381 IIccPhoneBook iccIpb = IIccPhoneBook.Stub.asInterface( 382 ServiceManager.getService("simphonebook")); 383 if (iccIpb != null) { 384 success = iccIpb.updateAdnRecordsInEfBySearch(efType, 385 name, number, "", "", pin2); 386 } 387 } catch (RemoteException ex) { 388 // ignore it 389 } catch (SecurityException ex) { 390 if (DBG) log(ex.toString()); 391 } 392 if (DBG) log("deleteIccRecordFromEf: " + success); 393 return success; 394 } 395 396 /** 397 * Loads an AdnRecord into a MatrixCursor. Must be called with mLock held. 398 * 399 * @param record the ADN record to load from 400 * @param cursor the cursor to receive the results 401 */ 402 private void loadRecord(AdnRecord record, MatrixCursor cursor, int id) { 403 if (!record.isEmpty()) { 404 Object[] contact = new Object[4]; 405 String alphaTag = record.getAlphaTag(); 406 String number = record.getNumber(); 407 408 if (DBG) log("loadRecord: " + alphaTag + ", " + number + ","); 409 contact[0] = alphaTag; 410 contact[1] = number; 411 412 String[] emails = record.getEmails(); 413 if (emails != null) { 414 StringBuilder emailString = new StringBuilder(); 415 for (String email: emails) { 416 if (DBG) log("Adding email:" + email); 417 emailString.append(email); 418 emailString.append(","); 419 } 420 contact[2] = emailString.toString(); 421 } 422 contact[3] = id; 423 cursor.addRow(contact); 424 } 425 } 426 427 private void log(String msg) { 428 Log.d(TAG, "[IccProvider] " + msg); 429 } 430 431} 432