/* ** 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.Context; import android.content.pm.PackageManager; import android.os.AsyncResult; import android.os.Binder; import android.os.Handler; import android.os.Message; import android.util.Log; import com.android.internal.telephony.IccConstants; import com.android.internal.telephony.IccSmsInterfaceManager; import com.android.internal.telephony.IccUtils; import com.android.internal.telephony.IntRangeManager; import com.android.internal.telephony.SMSDispatcher; import com.android.internal.telephony.SmsRawData; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Set; import static android.telephony.SmsManager.STATUS_ON_ICC_FREE; /** * SimSmsInterfaceManager to provide an inter-process communication to * access Sms in Sim. */ public class SimSmsInterfaceManager extends IccSmsInterfaceManager { static final String LOG_TAG = "GSM"; static final boolean DBG = true; private final Object mLock = new Object(); private boolean mSuccess; private List mSms; private HashMap> mCellBroadcastSubscriptions = new HashMap>(); private CellBroadcastRangeManager mCellBroadcastRangeManager = new CellBroadcastRangeManager(); private static final int EVENT_LOAD_DONE = 1; private static final int EVENT_UPDATE_DONE = 2; private static final int EVENT_SET_BROADCAST_ACTIVATION_DONE = 3; private static final int EVENT_SET_BROADCAST_CONFIG_DONE = 4; private static final int SMS_CB_CODE_SCHEME_MIN = 0; private static final int SMS_CB_CODE_SCHEME_MAX = 255; Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { AsyncResult ar; switch (msg.what) { case EVENT_UPDATE_DONE: ar = (AsyncResult) msg.obj; synchronized (mLock) { mSuccess = (ar.exception == null); mLock.notifyAll(); } break; case EVENT_LOAD_DONE: ar = (AsyncResult)msg.obj; synchronized (mLock) { if (ar.exception == null) { mSms = buildValidRawData((ArrayList) ar.result); } else { if(DBG) log("Cannot load Sms records"); if (mSms != null) mSms.clear(); } mLock.notifyAll(); } break; case EVENT_SET_BROADCAST_ACTIVATION_DONE: case EVENT_SET_BROADCAST_CONFIG_DONE: ar = (AsyncResult) msg.obj; synchronized (mLock) { mSuccess = (ar.exception == null); mLock.notifyAll(); } break; } } }; public SimSmsInterfaceManager(GSMPhone phone, SMSDispatcher dispatcher) { super(phone); mDispatcher = dispatcher; } public void dispose() { } @Override protected void finalize() { try { super.finalize(); } catch (Throwable throwable) { Log.e(LOG_TAG, "Error while finalizing:", throwable); } if(DBG) Log.d(LOG_TAG, "SimSmsInterfaceManager finalized"); } /** * Update the specified message on the SIM. * * @param index record index of message to update * @param status new message status (STATUS_ON_ICC_READ, * STATUS_ON_ICC_UNREAD, STATUS_ON_ICC_SENT, * STATUS_ON_ICC_UNSENT, STATUS_ON_ICC_FREE) * @param pdu the raw PDU to store * @return success or not * */ public boolean updateMessageOnIccEf(int index, int status, byte[] pdu) { if (DBG) log("updateMessageOnIccEf: index=" + index + " status=" + status + " ==> " + "("+ Arrays.toString(pdu) + ")"); enforceReceiveAndSend("Updating message on SIM"); synchronized(mLock) { mSuccess = false; Message response = mHandler.obtainMessage(EVENT_UPDATE_DONE); if (status == STATUS_ON_ICC_FREE) { // Special case FREE: call deleteSmsOnSim instead of // manipulating the SIM record mPhone.mCM.deleteSmsOnSim(index, response); } else { byte[] record = makeSmsRecordData(status, pdu); mPhone.getIccFileHandler().updateEFLinearFixed( IccConstants.EF_SMS, index, record, null, response); } try { mLock.wait(); } catch (InterruptedException e) { log("interrupted while trying to update by index"); } } return mSuccess; } /** * Copy a raw SMS PDU to the SIM. * * @param pdu the raw PDU to store * @param status message status (STATUS_ON_ICC_READ, STATUS_ON_ICC_UNREAD, * STATUS_ON_ICC_SENT, STATUS_ON_ICC_UNSENT) * @return success or not * */ public boolean copyMessageToIccEf(int status, byte[] pdu, byte[] smsc) { if (DBG) log("copyMessageToIccEf: status=" + status + " ==> " + "pdu=("+ Arrays.toString(pdu) + "), smsm=(" + Arrays.toString(smsc) +")"); enforceReceiveAndSend("Copying message to SIM"); synchronized(mLock) { mSuccess = false; Message response = mHandler.obtainMessage(EVENT_UPDATE_DONE); mPhone.mCM.writeSmsToSim(status, IccUtils.bytesToHexString(smsc), IccUtils.bytesToHexString(pdu), response); try { mLock.wait(); } catch (InterruptedException e) { log("interrupted while trying to update by index"); } } return mSuccess; } /** * Retrieves all messages currently stored on ICC. * * @return list of SmsRawData of all sms on ICC */ public List getAllMessagesFromIccEf() { if (DBG) log("getAllMessagesFromEF"); Context context = mPhone.getContext(); context.enforceCallingPermission( "android.permission.RECEIVE_SMS", "Reading messages from SIM"); synchronized(mLock) { Message response = mHandler.obtainMessage(EVENT_LOAD_DONE); mPhone.getIccFileHandler().loadEFLinearFixedAll(IccConstants.EF_SMS, response); try { mLock.wait(); } catch (InterruptedException e) { log("interrupted while trying to load from the SIM"); } } return mSms; } public boolean enableCellBroadcast(int messageIdentifier) { return enableCellBroadcastRange(messageIdentifier, messageIdentifier); } public boolean disableCellBroadcast(int messageIdentifier) { return disableCellBroadcastRange(messageIdentifier, messageIdentifier); } public boolean enableCellBroadcastRange(int startMessageId, int endMessageId) { if (DBG) log("enableCellBroadcastRange"); Context context = mPhone.getContext(); context.enforceCallingPermission( "android.permission.RECEIVE_SMS", "Enabling cell broadcast SMS"); String client = context.getPackageManager().getNameForUid( Binder.getCallingUid()); if (!mCellBroadcastRangeManager.enableRange(startMessageId, endMessageId, client)) { log("Failed to add cell broadcast subscription for MID range " + startMessageId + " to " + endMessageId + " from client " + client); return false; } if (DBG) log("Added cell broadcast subscription for MID range " + startMessageId + " to " + endMessageId + " from client " + client); setCellBroadcastActivation(!mCellBroadcastRangeManager.isEmpty()); return true; } public boolean disableCellBroadcastRange(int startMessageId, int endMessageId) { if (DBG) log("disableCellBroadcastRange"); Context context = mPhone.getContext(); context.enforceCallingPermission( "android.permission.RECEIVE_SMS", "Disabling cell broadcast SMS"); String client = context.getPackageManager().getNameForUid( Binder.getCallingUid()); if (!mCellBroadcastRangeManager.disableRange(startMessageId, endMessageId, client)) { log("Failed to remove cell broadcast subscription for MID range " + startMessageId + " to " + endMessageId + " from client " + client); return false; } if (DBG) log("Removed cell broadcast subscription for MID range " + startMessageId + " to " + endMessageId + " from client " + client); setCellBroadcastActivation(!mCellBroadcastRangeManager.isEmpty()); return true; } class CellBroadcastRangeManager extends IntRangeManager { private ArrayList mConfigList = new ArrayList(); /** * Called when the list of enabled ranges has changed. This will be * followed by zero or more calls to {@link #addRange} followed by * a call to {@link #finishUpdate}. */ protected void startUpdate() { mConfigList.clear(); } /** * Called after {@link #startUpdate} to indicate a range of enabled * values. * @param startId the first id included in the range * @param endId the last id included in the range */ protected void addRange(int startId, int endId, boolean selected) { mConfigList.add(new SmsBroadcastConfigInfo(startId, endId, SMS_CB_CODE_SCHEME_MIN, SMS_CB_CODE_SCHEME_MAX, selected)); } /** * Called to indicate the end of a range update started by the * previous call to {@link #startUpdate}. * @return true if successful, false otherwise */ protected boolean finishUpdate() { if (mConfigList.isEmpty()) { return true; } else { SmsBroadcastConfigInfo[] configs = mConfigList.toArray(new SmsBroadcastConfigInfo[mConfigList.size()]); return setCellBroadcastConfig(configs); } } } private boolean setCellBroadcastConfig(SmsBroadcastConfigInfo[] configs) { if (DBG) log("Calling setGsmBroadcastConfig with " + configs.length + " configurations"); synchronized (mLock) { Message response = mHandler.obtainMessage(EVENT_SET_BROADCAST_CONFIG_DONE); mSuccess = false; mPhone.mCM.setGsmBroadcastConfig(configs, response); try { mLock.wait(); } catch (InterruptedException e) { log("interrupted while trying to set cell broadcast config"); } } return mSuccess; } private boolean setCellBroadcastActivation(boolean activate) { if (DBG) log("Calling setCellBroadcastActivation(" + activate + ')'); synchronized (mLock) { Message response = mHandler.obtainMessage(EVENT_SET_BROADCAST_ACTIVATION_DONE); mSuccess = false; mPhone.mCM.setGsmBroadcastActivation(activate, response); try { mLock.wait(); } catch (InterruptedException e) { log("interrupted while trying to set cell broadcast activation"); } } return mSuccess; } @Override protected void log(String msg) { Log.d(LOG_TAG, "[SimSmsInterfaceManager] " + msg); } }