| /* |
| ** Copyright 2007, The Android Open Source Project |
| ** |
| ** Licensed under the Apache License, Version 2.0 (the "License"); |
| ** you may not use this file except in compliance with the License. |
| ** You may obtain a copy of the License at |
| ** |
| ** http://www.apache.org/licenses/LICENSE-2.0 |
| ** |
| ** Unless required by applicable law or agreed to in writing, software |
| ** distributed under the License is distributed on an "AS IS" BASIS, |
| ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| ** See the License for the specific language governing permissions and |
| ** limitations under the License. |
| */ |
| |
| package com.android.internal.telephony.gsm; |
| |
| import android.content.pm.PackageManager; |
| import android.os.AsyncResult; |
| import android.os.Handler; |
| import android.os.Looper; |
| import android.os.Message; |
| import android.os.ServiceManager; |
| import android.telephony.PhoneNumberUtils; |
| import android.util.Log; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| /** |
| * SimPhoneBookInterfaceManager to provide an inter-process communication to |
| * access ADN-like SIM records. |
| */ |
| public class SimPhoneBookInterfaceManager extends ISimPhoneBook.Stub { |
| static final String LOG_TAG = "GSM"; |
| static final boolean DBG = false; |
| |
| private GSMPhone phone; |
| private AdnRecordCache adnCache; |
| private final Object mLock = new Object(); |
| private int recordSize[]; |
| private boolean success; |
| private List<AdnRecord> records; |
| |
| private static final boolean ALLOW_SIM_OP_IN_UI_THREAD = false; |
| |
| private static final int EVENT_GET_SIZE_DONE = 1; |
| private static final int EVENT_LOAD_DONE = 2; |
| private static final int EVENT_UPDATE_DONE = 3; |
| |
| Handler mHandler = new Handler() { |
| @Override |
| public void handleMessage(Message msg) { |
| AsyncResult ar; |
| |
| switch (msg.what) { |
| case EVENT_GET_SIZE_DONE: |
| ar = (AsyncResult) msg.obj; |
| synchronized (mLock) { |
| if (ar.exception == null) { |
| recordSize = (int[])ar.result; |
| // recordSize[0] is the record length |
| // recordSize[1] is the total length of the EF file |
| // recordSize[2] is the number of records in the EF file |
| log("GET_RECORD_SIZE Size " + recordSize[0] + |
| " total " + recordSize[1] + |
| " #record " + recordSize[2]); |
| mLock.notifyAll(); |
| } |
| } |
| break; |
| case EVENT_UPDATE_DONE: |
| ar = (AsyncResult) msg.obj; |
| synchronized (mLock) { |
| success = (ar.exception == null); |
| mLock.notifyAll(); |
| } |
| break; |
| case EVENT_LOAD_DONE: |
| ar = (AsyncResult)msg.obj; |
| synchronized (mLock) { |
| if (ar.exception == null) { |
| records = (List<AdnRecord>) |
| ((ArrayList<AdnRecord>) ar.result); |
| } else { |
| if(DBG) log("Cannot load ADN records"); |
| if (records != null) { |
| records.clear(); |
| } |
| } |
| mLock.notifyAll(); |
| } |
| break; |
| } |
| } |
| }; |
| |
| public SimPhoneBookInterfaceManager(GSMPhone phone) { |
| this.phone = phone; |
| adnCache = phone.mSIMRecords.getAdnCache(); |
| publish(); |
| } |
| |
| private void publish() { |
| ServiceManager.addService("simphonebook", this); |
| } |
| |
| /** |
| * Replace oldAdn with newAdn in ADN-like record in EF |
| * |
| * getAdnRecordsInEf must be called at least once before this function, |
| * otherwise an error will be returned |
| * throws SecurityException if no WRITE_CONTACTS permission |
| * |
| * @param efid must be one among EF_ADN, EF_FDN, and EF_SDN |
| * @param oldTag adn tag to be replaced |
| * @param oldPhoneNumber adn number to be replaced |
| * Set both oldTag and oldPhoneNubmer to "" means to replace an |
| * empty record, aka, insert new record |
| * @param newTag adn tag to be stored |
| * @param newPhoneNumber adn number ot be stored |
| * Set both newTag and newPhoneNubmer to "" means to replace the old |
| * record with empty one, aka, delete old record |
| * @param pin2 required to update EF_FDN, otherwise must be null |
| * @return true for success |
| */ |
| public boolean |
| updateAdnRecordsInEfBySearch (int efid, |
| String oldTag, String oldPhoneNumber, |
| String newTag, String newPhoneNumber, String pin2) { |
| |
| |
| if (phone.getContext().checkCallingOrSelfPermission( |
| android.Manifest.permission.WRITE_CONTACTS) |
| != PackageManager.PERMISSION_GRANTED) { |
| throw new SecurityException( |
| "Requires android.permission.WRITE_CONTACTS permission"); |
| } |
| |
| |
| if (DBG) log("updateAdnRecordsInEfBySearch: efid=" + efid + |
| " ("+ oldTag + "," + oldPhoneNumber + ")"+ "==>" + |
| " ("+ newTag + "," + newPhoneNumber + ")"+ " pin2=" + pin2); |
| synchronized(mLock) { |
| checkThread(); |
| success = false; |
| Message response = mHandler.obtainMessage(EVENT_UPDATE_DONE); |
| AdnRecord oldAdn = new AdnRecord(oldTag, oldPhoneNumber); |
| AdnRecord newAdn = new AdnRecord(newTag, newPhoneNumber); |
| adnCache.updateAdnBySearch(efid, oldAdn, newAdn, pin2, response); |
| try { |
| mLock.wait(); |
| } catch (InterruptedException e) { |
| log("interrupted while trying to update by search"); |
| } |
| } |
| return success; |
| } |
| |
| /** |
| * Update an ADN-like EF record by record index |
| * |
| * This is useful for iteration the whole ADN file, such as write the whole |
| * phone book or erase/format the whole phonebook |
| * throws SecurityException if no WRITE_CONTACTS permission |
| * |
| * @param efid must be one among EF_ADN, EF_FDN, and EF_SDN |
| * @param newTag adn tag to be stored |
| * @param newPhoneNumber adn number to be stored |
| * Set both newTag and newPhoneNubmer to "" means to replace the old |
| * record with empty one, aka, delete old record |
| * @param index is 1-based adn record index to be updated |
| * @param pin2 required to update EF_FDN, otherwise must be null |
| * @return true for success |
| */ |
| public boolean |
| updateAdnRecordsInEfByIndex(int efid, String newTag, |
| String newPhoneNumber, int index, String pin2) { |
| |
| if (phone.getContext().checkCallingOrSelfPermission( |
| android.Manifest.permission.WRITE_CONTACTS) |
| != PackageManager.PERMISSION_GRANTED) { |
| throw new SecurityException( |
| "Requires android.permission.WRITE_CONTACTS permission"); |
| } |
| |
| if (DBG) log("updateAdnRecordsInEfByIndex: efid=" + efid + |
| " Index=" + index + " ==> " + |
| "("+ newTag + "," + newPhoneNumber + ")"+ " pin2=" + pin2); |
| synchronized(mLock) { |
| checkThread(); |
| success = false; |
| Message response = mHandler.obtainMessage(EVENT_UPDATE_DONE); |
| AdnRecord newAdn = new AdnRecord(newTag, newPhoneNumber); |
| adnCache.updateAdnByIndex(efid, newAdn, index, pin2, response); |
| try { |
| mLock.wait(); |
| } catch (InterruptedException e) { |
| log("interrupted while trying to update by index"); |
| } |
| } |
| return success; |
| } |
| |
| /** |
| * Get the capacity of records in efid |
| * |
| * @param efid the EF id of a ADN-like SIM |
| * @return int[3] array |
| * recordSizes[0] is the single record length |
| * recordSizes[1] is the total length of the EF file |
| * recordSizes[2] is the number of records in the EF file |
| */ |
| public int[] getAdnRecordsSize(int efid) { |
| if (DBG) log("getAdnRecordsSize: efid=" + efid); |
| synchronized(mLock) { |
| checkThread(); |
| recordSize = new int[3]; |
| Message response = mHandler.obtainMessage(EVENT_GET_SIZE_DONE); |
| phone.mSIMFileHandler.getEFLinearRecordSize(efid, response); |
| try { |
| mLock.wait(); |
| } catch (InterruptedException e) { |
| log("interrupted while trying to load from the SIM"); |
| } |
| } |
| |
| return recordSize; |
| } |
| |
| /** |
| * Loads the AdnRecords in efid and returns them as a |
| * List of AdnRecords |
| * |
| * throws SecurityException if no READ_CONTACTS permission |
| * |
| * @param efid the EF id of a ADN-like SIM |
| * @return List of AdnRecord |
| */ |
| public List<AdnRecord> getAdnRecordsInEf(int efid) { |
| |
| if (phone.getContext().checkCallingOrSelfPermission( |
| android.Manifest.permission.READ_CONTACTS) |
| != PackageManager.PERMISSION_GRANTED) { |
| throw new SecurityException( |
| "Requires android.permission.READ_CONTACTS permission"); |
| } |
| |
| if (DBG) log("getAdnRecordsInEF: efid=" + efid); |
| |
| synchronized(mLock) { |
| checkThread(); |
| Message response = mHandler.obtainMessage(EVENT_LOAD_DONE); |
| adnCache.requestLoadAllAdnLike(efid, response); |
| try { |
| mLock.wait(); |
| } catch (InterruptedException e) { |
| log("interrupted while trying to load from the SIM"); |
| } |
| } |
| return records; |
| } |
| |
| private void checkThread() { |
| if (!ALLOW_SIM_OP_IN_UI_THREAD) { |
| // Make sure this isn't the UI thread, since it will block |
| if (mHandler.getLooper().equals(Looper.myLooper())) { |
| Log.e(LOG_TAG, "query() called on the main UI thread!"); |
| throw new IllegalStateException("You cannot call query on this provder from the main UI thread."); |
| } |
| } |
| } |
| |
| private void log(String msg) { |
| Log.d(LOG_TAG, "[SpbInterfaceManager] " + msg); |
| } |
| } |