| /* |
| ** 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.app.PendingIntent; |
| import android.content.Context; |
| import android.os.AsyncResult; |
| import android.os.Handler; |
| import android.os.Message; |
| import android.os.ServiceManager; |
| import android.telephony.gsm.SmsManager; |
| import android.util.Log; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| /** |
| * SimSmsInterfaceManager to provide an inter-process communication to |
| * access Sms in Sim. |
| */ |
| public class SimSmsInterfaceManager extends ISms.Stub { |
| static final String LOG_TAG = "GSM"; |
| static final boolean DBG = false; |
| |
| private GSMPhone mPhone; |
| private final Object mLock = new Object(); |
| private boolean mSuccess; |
| private List<SmsRawData> mSms; |
| |
| private static final int EVENT_LOAD_DONE = 1; |
| private static final int EVENT_UPDATE_DONE = 2; |
| |
| 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 = (List<SmsRawData>) |
| buildValidRawData((ArrayList<byte[]>) ar.result); |
| } else { |
| if(DBG) log("Cannot load Sms records"); |
| if (mSms != null) |
| mSms.clear(); |
| } |
| mLock.notifyAll(); |
| } |
| break; |
| } |
| } |
| }; |
| |
| public SimSmsInterfaceManager(GSMPhone phone) { |
| this.mPhone = phone; |
| ServiceManager.addService("isms", this); |
| } |
| |
| private void enforceReceiveAndSend(String message) { |
| Context context = mPhone.getContext(); |
| |
| context.enforceCallingPermission( |
| "android.permission.RECEIVE_SMS", message); |
| context.enforceCallingPermission( |
| "android.permission.SEND_SMS", message); |
| } |
| |
| /** |
| * Update the specified message on the SIM. |
| * |
| * @param index record index of message to update |
| * @param status new message status (STATUS_ON_SIM_READ, |
| * STATUS_ON_SIM_UNREAD, STATUS_ON_SIM_SENT, |
| * STATUS_ON_SIM_UNSENT, STATUS_ON_SIM_FREE) |
| * @param pdu the raw PDU to store |
| * @return success or not |
| * |
| */ |
| public boolean |
| updateMessageOnSimEf(int index, int status, byte[] pdu) { |
| if (DBG) log("updateMessageOnSimEf: index=" + index + |
| " status=" + status + " ==> " + |
| "("+ pdu + ")"); |
| enforceReceiveAndSend("Updating message on SIM"); |
| synchronized(mLock) { |
| mSuccess = false; |
| Message response = mHandler.obtainMessage(EVENT_UPDATE_DONE); |
| |
| if (status == SmsManager.STATUS_ON_SIM_FREE) { |
| // Special case FREE: call deleteSmsOnSim instead of |
| // manipulating the SIM record |
| mPhone.mCM.deleteSmsOnSim(index, response); |
| } else { |
| byte[] record = makeSmsRecordData(status, pdu); |
| mPhone.mSIMFileHandler.updateEFLinearFixed( SimConstants.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_SIM_READ, STATUS_ON_SIM_UNREAD, |
| * STATUS_ON_SIM_SENT, STATUS_ON_SIM_UNSENT) |
| * @return success or not |
| * |
| */ |
| public boolean copyMessageToSimEf(int status, byte[] pdu, byte[] smsc) { |
| if (DBG) log("copyMessageToSimEf: status=" + status + " ==> " + |
| "pdu=("+ pdu + "), smsm=(" + smsc +")"); |
| enforceReceiveAndSend("Copying message to SIM"); |
| synchronized(mLock) { |
| mSuccess = false; |
| Message response = mHandler.obtainMessage(EVENT_UPDATE_DONE); |
| |
| mPhone.mCM.writeSmsToSim(status, SimUtils.bytesToHexString(smsc), |
| SimUtils.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 SIM. |
| * |
| * @return list of SmsRawData of all sms on SIM |
| */ |
| public List<SmsRawData> getAllMessagesFromSimEf() { |
| 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.mSIMFileHandler.loadEFLinearFixedAll(SimConstants.EF_SMS, |
| response); |
| |
| try { |
| mLock.wait(); |
| } catch (InterruptedException e) { |
| log("interrupted while trying to load from the SIM"); |
| } |
| } |
| return mSms; |
| } |
| |
| /** |
| * Send a Raw PDU SMS |
| * |
| * @param smsc the SMSC to send the message through, or NULL for the |
| * defatult SMSC |
| * @param pdu the raw PDU to send |
| * @param sentIntent if not NULL this <code>Intent</code> is |
| * broadcast when the message is sucessfully sent, or failed. |
| * The result code will be <code>Activity.RESULT_OK<code> for success, |
| * or one of these errors: |
| * <code>RESULT_ERROR_GENERIC_FAILURE</code> |
| * <code>RESULT_ERROR_RADIO_OFF</code> |
| * <code>RESULT_ERROR_NULL_PDU</code>. |
| * @param deliveryIntent if not NULL this <code>Intent</code> is |
| * broadcast when the message is delivered to the recipient. The |
| * raw pdu of the status report is in the extended data ("pdu"). |
| */ |
| public void sendRawPdu(byte[] smsc, byte[] pdu, PendingIntent sentIntent, |
| PendingIntent deliveryIntent) { |
| Context context = mPhone.getContext(); |
| |
| context.enforceCallingPermission( |
| "android.permission.SEND_SMS", |
| "Sending SMS message"); |
| if (DBG) log("sendRawPdu: smsc=" + smsc + |
| " pdu="+ pdu + " sentIntent" + sentIntent + |
| " deliveryIntent" + deliveryIntent); |
| mPhone.mSMS.sendRawPdu(smsc, pdu, sentIntent, deliveryIntent); |
| } |
| |
| /** |
| * Send a multi-part text based SMS. |
| * |
| * @param destinationAddress the address to send the message to |
| * @param scAddress is the service center address or null to use |
| * the current default SMSC |
| * @param parts an <code>ArrayList</code> of strings that, in order, |
| * comprise the original message |
| * @param sentIntents if not null, an <code>ArrayList</code> of |
| * <code>PendingIntent</code>s (one for each message part) that is |
| * broadcast when the corresponding message part has been sent. |
| * The result code will be <code>Activity.RESULT_OK<code> for success, |
| * or one of these errors: |
| * <code>RESULT_ERROR_GENERIC_FAILURE</code> |
| * <code>RESULT_ERROR_RADIO_OFF</code> |
| * <code>RESULT_ERROR_NULL_PDU</code>. |
| * @param deliveryIntents if not null, an <code>ArrayList</code> of |
| * <code>PendingIntent</code>s (one for each message part) that is |
| * broadcast when the corresponding message part has been delivered |
| * to the recipient. The raw pdu of the status report is in the |
| * extended data ("pdu"). |
| */ |
| public void sendMultipartText(String destinationAddress, String scAddress, List<String> parts, |
| List<PendingIntent> sentIntents, List<PendingIntent> deliveryIntents) { |
| Context context = mPhone.getContext(); |
| |
| context.enforceCallingPermission( |
| "android.permission.SEND_SMS", |
| "Sending SMS message"); |
| if (DBG) log("sendMultipartText"); |
| mPhone.mSMS.sendMultipartText(destinationAddress, scAddress, (ArrayList<String>) parts, |
| (ArrayList<PendingIntent>) sentIntents, (ArrayList<PendingIntent>) deliveryIntents); |
| } |
| |
| /** |
| * Generates an EF_SMS record from status and raw PDU. |
| * |
| * @param status Message status. See TS 51.011 10.5.3. |
| * @param pdu Raw message PDU. |
| * @return byte array for the record. |
| */ |
| private byte[] makeSmsRecordData(int status, byte[] pdu) { |
| byte[] data = new byte[SimConstants.SMS_RECORD_LENGTH]; |
| |
| // Status bits for this record. See TS 51.011 10.5.3 |
| data[0] = (byte)(status & 7); |
| |
| System.arraycopy(pdu, 0, data, 1, pdu.length); |
| |
| // Pad out with 0xFF's. |
| for (int j = pdu.length+1; j < SimConstants.SMS_RECORD_LENGTH; j++) { |
| data[j] = -1; |
| } |
| |
| return data; |
| } |
| |
| /** |
| * create SmsRawData lists from all sms record byte[] |
| * Use null to indicate "free" record |
| * |
| * @param messages List of message records from EF_SMS. |
| * @return SmsRawData list of all in-used records |
| */ |
| private ArrayList<SmsRawData> buildValidRawData(ArrayList<byte[]> messages) { |
| int count = messages.size(); |
| ArrayList<SmsRawData> ret; |
| |
| ret = new ArrayList<SmsRawData>(count); |
| |
| for (int i = 0; i < count; i++) { |
| byte[] ba = messages.get(i); |
| if (ba[0] == 0) { |
| ret.add(null); |
| } else { |
| ret.add(new SmsRawData(messages.get(i))); |
| } |
| } |
| |
| return ret; |
| } |
| |
| private void log(String msg) { |
| Log.d(LOG_TAG, "[SmsInterfaceManager] " + msg); |
| } |
| } |