blob: 8d0e5d3eefda6621e8f36212bb4f0d53284bc2e9 [file] [log] [blame]
/*
** 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<SmsRawData> mSms;
private HashMap<Integer, HashSet<String>> mCellBroadcastSubscriptions =
new HashMap<Integer, HashSet<String>>();
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<byte[]>) 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<SmsRawData> 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);
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);
return true;
}
class CellBroadcastRangeManager extends IntRangeManager {
private ArrayList<SmsBroadcastConfigInfo> mConfigList =
new ArrayList<SmsBroadcastConfigInfo>();
/**
* 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}.
*/
protected boolean finishUpdate() {
if (mConfigList.isEmpty()) {
return setCellBroadcastActivation(false);
} else {
SmsBroadcastConfigInfo[] configs =
mConfigList.toArray(new SmsBroadcastConfigInfo[mConfigList.size()]);
return setCellBroadcastConfig(configs) && setCellBroadcastActivation(true);
}
}
}
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);
}
}