Home | History | Annotate | Download | only in telephony
      1 /*
      2  * Copyright (C) 2008 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 
     17 package com.android.internal.telephony;
     18 
     19 import android.Manifest;
     20 import android.app.AppOpsManager;
     21 import android.app.PendingIntent;
     22 import android.content.Context;
     23 import android.os.AsyncResult;
     24 import android.os.Binder;
     25 import android.os.Handler;
     26 import android.os.Message;
     27 import android.telephony.Rlog;
     28 import android.util.Log;
     29 
     30 import com.android.internal.telephony.uicc.IccConstants;
     31 import com.android.internal.telephony.uicc.IccFileHandler;
     32 import com.android.internal.util.HexDump;
     33 
     34 import java.util.ArrayList;
     35 import java.util.Arrays;
     36 import java.util.List;
     37 
     38 import static android.telephony.SmsManager.STATUS_ON_ICC_FREE;
     39 import static android.telephony.SmsManager.STATUS_ON_ICC_READ;
     40 import static android.telephony.SmsManager.STATUS_ON_ICC_UNREAD;
     41 
     42 /**
     43  * IccSmsInterfaceManager to provide an inter-process communication to
     44  * access Sms in Icc.
     45  */
     46 public abstract class IccSmsInterfaceManager extends ISms.Stub {
     47     static final String LOG_TAG = "IccSmsInterfaceManager";
     48     static final boolean DBG = true;
     49 
     50     protected final Object mLock = new Object();
     51     protected boolean mSuccess;
     52     private List<SmsRawData> mSms;
     53 
     54     private static final int EVENT_LOAD_DONE = 1;
     55     private static final int EVENT_UPDATE_DONE = 2;
     56     protected static final int EVENT_SET_BROADCAST_ACTIVATION_DONE = 3;
     57     protected static final int EVENT_SET_BROADCAST_CONFIG_DONE = 4;
     58 
     59     final protected PhoneBase mPhone;
     60     final protected Context mContext;
     61     final protected AppOpsManager mAppOps;
     62     protected SMSDispatcher mDispatcher;
     63 
     64     protected Handler mHandler = new Handler() {
     65         @Override
     66         public void handleMessage(Message msg) {
     67             AsyncResult ar;
     68 
     69             switch (msg.what) {
     70                 case EVENT_UPDATE_DONE:
     71                     ar = (AsyncResult) msg.obj;
     72                     synchronized (mLock) {
     73                         mSuccess = (ar.exception == null);
     74                         mLock.notifyAll();
     75                     }
     76                     break;
     77                 case EVENT_LOAD_DONE:
     78                     ar = (AsyncResult)msg.obj;
     79                     synchronized (mLock) {
     80                         if (ar.exception == null) {
     81                             mSms = buildValidRawData((ArrayList<byte[]>) ar.result);
     82                             //Mark SMS as read after importing it from card.
     83                             markMessagesAsRead((ArrayList<byte[]>) ar.result);
     84                         } else {
     85                             if(DBG) log("Cannot load Sms records");
     86                             if (mSms != null)
     87                                 mSms.clear();
     88                         }
     89                         mLock.notifyAll();
     90                     }
     91                     break;
     92                 case EVENT_SET_BROADCAST_ACTIVATION_DONE:
     93                 case EVENT_SET_BROADCAST_CONFIG_DONE:
     94                     ar = (AsyncResult) msg.obj;
     95                     synchronized (mLock) {
     96                         mSuccess = (ar.exception == null);
     97                         mLock.notifyAll();
     98                     }
     99                     break;
    100             }
    101         }
    102     };
    103 
    104     protected IccSmsInterfaceManager(PhoneBase phone){
    105         mPhone = phone;
    106         mContext = phone.getContext();
    107         mAppOps = (AppOpsManager)mContext.getSystemService(Context.APP_OPS_SERVICE);
    108     }
    109 
    110     protected void markMessagesAsRead(ArrayList<byte[]> messages) {
    111         if (messages == null) {
    112             return;
    113         }
    114 
    115         //IccFileHandler can be null, if icc card is absent.
    116         IccFileHandler fh = mPhone.getIccFileHandler();
    117         if (fh == null) {
    118             //shouldn't really happen, as messages are marked as read, only
    119             //after importing it from icc.
    120             if (Rlog.isLoggable("SMS", Log.DEBUG)) {
    121                 log("markMessagesAsRead - aborting, no icc card present.");
    122             }
    123             return;
    124         }
    125 
    126         int count = messages.size();
    127 
    128         for (int i = 0; i < count; i++) {
    129              byte[] ba = messages.get(i);
    130              if (ba[0] == STATUS_ON_ICC_UNREAD) {
    131                  int n = ba.length;
    132                  byte[] nba = new byte[n - 1];
    133                  System.arraycopy(ba, 1, nba, 0, n - 1);
    134                  byte[] record = makeSmsRecordData(STATUS_ON_ICC_READ, nba);
    135                  fh.updateEFLinearFixed(IccConstants.EF_SMS, i + 1, record, null, null);
    136                  if (Rlog.isLoggable("SMS", Log.DEBUG)) {
    137                      log("SMS " + (i + 1) + " marked as read");
    138                  }
    139              }
    140         }
    141     }
    142 
    143     protected void enforceReceiveAndSend(String message) {
    144         mContext.enforceCallingPermission(
    145                 Manifest.permission.RECEIVE_SMS, message);
    146         mContext.enforceCallingPermission(
    147                 Manifest.permission.SEND_SMS, message);
    148     }
    149 
    150     /**
    151      * Update the specified message on the Icc.
    152      *
    153      * @param index record index of message to update
    154      * @param status new message status (STATUS_ON_ICC_READ,
    155      *                  STATUS_ON_ICC_UNREAD, STATUS_ON_ICC_SENT,
    156      *                  STATUS_ON_ICC_UNSENT, STATUS_ON_ICC_FREE)
    157      * @param pdu the raw PDU to store
    158      * @return success or not
    159      *
    160      */
    161     @Override
    162     public boolean
    163     updateMessageOnIccEf(String callingPackage, int index, int status, byte[] pdu) {
    164         if (DBG) log("updateMessageOnIccEf: index=" + index +
    165                 " status=" + status + " ==> " +
    166                 "("+ Arrays.toString(pdu) + ")");
    167         enforceReceiveAndSend("Updating message on Icc");
    168         if (mAppOps.noteOp(AppOpsManager.OP_WRITE_ICC_SMS, Binder.getCallingUid(),
    169                 callingPackage) != AppOpsManager.MODE_ALLOWED) {
    170             return false;
    171         }
    172         synchronized(mLock) {
    173             mSuccess = false;
    174             Message response = mHandler.obtainMessage(EVENT_UPDATE_DONE);
    175 
    176             if (status == STATUS_ON_ICC_FREE) {
    177                 // RIL_REQUEST_DELETE_SMS_ON_SIM vs RIL_REQUEST_CDMA_DELETE_SMS_ON_RUIM
    178                 // Special case FREE: call deleteSmsOnSim/Ruim instead of
    179                 // manipulating the record
    180                 // Will eventually fail if icc card is not present.
    181                 deleteSms(index, response);
    182             } else {
    183                 //IccFilehandler can be null if ICC card is not present.
    184                 IccFileHandler fh = mPhone.getIccFileHandler();
    185                 if (fh == null) {
    186                     response.recycle();
    187                     return mSuccess; /* is false */
    188                 }
    189                 byte[] record = makeSmsRecordData(status, pdu);
    190                 fh.updateEFLinearFixed(
    191                         IccConstants.EF_SMS,
    192                         index, record, null, response);
    193             }
    194             try {
    195                 mLock.wait();
    196             } catch (InterruptedException e) {
    197                 log("interrupted while trying to update by index");
    198             }
    199         }
    200         return mSuccess;
    201     }
    202 
    203     /**
    204      * Copy a raw SMS PDU to the Icc.
    205      *
    206      * @param pdu the raw PDU to store
    207      * @param status message status (STATUS_ON_ICC_READ, STATUS_ON_ICC_UNREAD,
    208      *               STATUS_ON_ICC_SENT, STATUS_ON_ICC_UNSENT)
    209      * @return success or not
    210      *
    211      */
    212     @Override
    213     public boolean copyMessageToIccEf(String callingPackage, int status, byte[] pdu, byte[] smsc) {
    214         //NOTE smsc not used in RUIM
    215         if (DBG) log("copyMessageToIccEf: status=" + status + " ==> " +
    216                 "pdu=("+ Arrays.toString(pdu) +
    217                 "), smsc=(" + Arrays.toString(smsc) +")");
    218         enforceReceiveAndSend("Copying message to Icc");
    219         if (mAppOps.noteOp(AppOpsManager.OP_WRITE_ICC_SMS, Binder.getCallingUid(),
    220                 callingPackage) != AppOpsManager.MODE_ALLOWED) {
    221             return false;
    222         }
    223         synchronized(mLock) {
    224             mSuccess = false;
    225             Message response = mHandler.obtainMessage(EVENT_UPDATE_DONE);
    226 
    227             //RIL_REQUEST_WRITE_SMS_TO_SIM vs RIL_REQUEST_CDMA_WRITE_SMS_TO_RUIM
    228             writeSms(status, smsc, pdu, response);
    229 
    230             try {
    231                 mLock.wait();
    232             } catch (InterruptedException e) {
    233                 log("interrupted while trying to update by index");
    234             }
    235         }
    236         return mSuccess;
    237     }
    238 
    239     /**
    240      * Retrieves all messages currently stored on Icc.
    241      *
    242      * @return list of SmsRawData of all sms on Icc
    243      */
    244     @Override
    245     public List<SmsRawData> getAllMessagesFromIccEf(String callingPackage) {
    246         if (DBG) log("getAllMessagesFromEF");
    247 
    248         mContext.enforceCallingPermission(
    249                 Manifest.permission.RECEIVE_SMS,
    250                 "Reading messages from Icc");
    251         if (mAppOps.noteOp(AppOpsManager.OP_READ_ICC_SMS, Binder.getCallingUid(),
    252                 callingPackage) != AppOpsManager.MODE_ALLOWED) {
    253             return new ArrayList<SmsRawData>();
    254         }
    255         synchronized(mLock) {
    256 
    257             IccFileHandler fh = mPhone.getIccFileHandler();
    258             if (fh == null) {
    259                 Rlog.e(LOG_TAG, "Cannot load Sms records. No icc card?");
    260                 if (mSms != null) {
    261                     mSms.clear();
    262                     return mSms;
    263                 }
    264             }
    265 
    266             Message response = mHandler.obtainMessage(EVENT_LOAD_DONE);
    267             fh.loadEFLinearFixedAll(IccConstants.EF_SMS, response);
    268 
    269             try {
    270                 mLock.wait();
    271             } catch (InterruptedException e) {
    272                 log("interrupted while trying to load from the Icc");
    273             }
    274         }
    275         return mSms;
    276     }
    277 
    278     /**
    279      * Send a data based SMS to a specific application port.
    280      *
    281      * @param destAddr the address to send the message to
    282      * @param scAddr is the service center address or null to use
    283      *  the current default SMSC
    284      * @param destPort the port to deliver the message to
    285      * @param data the body of the message to send
    286      * @param sentIntent if not NULL this <code>PendingIntent</code> is
    287      *  broadcast when the message is successfully sent, or failed.
    288      *  The result code will be <code>Activity.RESULT_OK<code> for success,
    289      *  or one of these errors:<br>
    290      *  <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
    291      *  <code>RESULT_ERROR_RADIO_OFF</code><br>
    292      *  <code>RESULT_ERROR_NULL_PDU</code><br>
    293      *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
    294      *  the extra "errorCode" containing a radio technology specific value,
    295      *  generally only useful for troubleshooting.<br>
    296      *  The per-application based SMS control checks sentIntent. If sentIntent
    297      *  is NULL the caller will be checked against all unknown applications,
    298      *  which cause smaller number of SMS to be sent in checking period.
    299      * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
    300      *  broadcast when the message is delivered to the recipient.  The
    301      *  raw pdu of the status report is in the extended data ("pdu").
    302      */
    303     @Override
    304     public void sendData(String callingPackage, String destAddr, String scAddr, int destPort,
    305             byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
    306         mPhone.getContext().enforceCallingPermission(
    307                 Manifest.permission.SEND_SMS,
    308                 "Sending SMS message");
    309         if (Rlog.isLoggable("SMS", Log.VERBOSE)) {
    310             log("sendData: destAddr=" + destAddr + " scAddr=" + scAddr + " destPort=" +
    311                 destPort + " data='"+ HexDump.toHexString(data)  + "' sentIntent=" +
    312                 sentIntent + " deliveryIntent=" + deliveryIntent);
    313         }
    314         if (mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(),
    315                 callingPackage) != AppOpsManager.MODE_ALLOWED) {
    316             return;
    317         }
    318         mDispatcher.sendData(destAddr, scAddr, destPort, data, sentIntent, deliveryIntent);
    319     }
    320 
    321     /**
    322      * Send a text based SMS.
    323      *
    324      * @param destAddr the address to send the message to
    325      * @param scAddr is the service center address or null to use
    326      *  the current default SMSC
    327      * @param text the body of the message to send
    328      * @param sentIntent if not NULL this <code>PendingIntent</code> is
    329      *  broadcast when the message is successfully sent, or failed.
    330      *  The result code will be <code>Activity.RESULT_OK<code> for success,
    331      *  or one of these errors:<br>
    332      *  <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
    333      *  <code>RESULT_ERROR_RADIO_OFF</code><br>
    334      *  <code>RESULT_ERROR_NULL_PDU</code><br>
    335      *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
    336      *  the extra "errorCode" containing a radio technology specific value,
    337      *  generally only useful for troubleshooting.<br>
    338      *  The per-application based SMS control checks sentIntent. If sentIntent
    339      *  is NULL the caller will be checked against all unknown applications,
    340      *  which cause smaller number of SMS to be sent in checking period.
    341      * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
    342      *  broadcast when the message is delivered to the recipient.  The
    343      *  raw pdu of the status report is in the extended data ("pdu").
    344      */
    345     @Override
    346     public void sendText(String callingPackage, String destAddr, String scAddr,
    347             String text, PendingIntent sentIntent, PendingIntent deliveryIntent) {
    348         mPhone.getContext().enforceCallingPermission(
    349                 Manifest.permission.SEND_SMS,
    350                 "Sending SMS message");
    351         if (Rlog.isLoggable("SMS", Log.VERBOSE)) {
    352             log("sendText: destAddr=" + destAddr + " scAddr=" + scAddr +
    353                 " text='"+ text + "' sentIntent=" +
    354                 sentIntent + " deliveryIntent=" + deliveryIntent);
    355         }
    356         if (mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(),
    357                 callingPackage) != AppOpsManager.MODE_ALLOWED) {
    358             return;
    359         }
    360         mDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent);
    361     }
    362 
    363     /**
    364      * Send a multi-part text based SMS.
    365      *
    366      * @param destAddr the address to send the message to
    367      * @param scAddr is the service center address or null to use
    368      *   the current default SMSC
    369      * @param parts an <code>ArrayList</code> of strings that, in order,
    370      *   comprise the original message
    371      * @param sentIntents if not null, an <code>ArrayList</code> of
    372      *   <code>PendingIntent</code>s (one for each message part) that is
    373      *   broadcast when the corresponding message part has been sent.
    374      *   The result code will be <code>Activity.RESULT_OK<code> for success,
    375      *   or one of these errors:
    376      *   <code>RESULT_ERROR_GENERIC_FAILURE</code>
    377      *   <code>RESULT_ERROR_RADIO_OFF</code>
    378      *   <code>RESULT_ERROR_NULL_PDU</code>.
    379      *  The per-application based SMS control checks sentIntent. If sentIntent
    380      *  is NULL the caller will be checked against all unknown applications,
    381      *  which cause smaller number of SMS to be sent in checking period.
    382      * @param deliveryIntents if not null, an <code>ArrayList</code> of
    383      *   <code>PendingIntent</code>s (one for each message part) that is
    384      *   broadcast when the corresponding message part has been delivered
    385      *   to the recipient.  The raw pdu of the status report is in the
    386      *   extended data ("pdu").
    387      */
    388     @Override
    389     public void sendMultipartText(String callingPackage, String destAddr, String scAddr,
    390             List<String> parts, List<PendingIntent> sentIntents,
    391             List<PendingIntent> deliveryIntents) {
    392         mPhone.getContext().enforceCallingPermission(
    393                 Manifest.permission.SEND_SMS,
    394                 "Sending SMS message");
    395         if (Rlog.isLoggable("SMS", Log.VERBOSE)) {
    396             int i = 0;
    397             for (String part : parts) {
    398                 log("sendMultipartText: destAddr=" + destAddr + ", srAddr=" + scAddr +
    399                         ", part[" + (i++) + "]=" + part);
    400             }
    401         }
    402         if (mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(),
    403                 callingPackage) != AppOpsManager.MODE_ALLOWED) {
    404             return;
    405         }
    406         mDispatcher.sendMultipartText(destAddr, scAddr, (ArrayList<String>) parts,
    407                 (ArrayList<PendingIntent>) sentIntents, (ArrayList<PendingIntent>) deliveryIntents);
    408     }
    409 
    410     @Override
    411     public int getPremiumSmsPermission(String packageName) {
    412         return mDispatcher.getPremiumSmsPermission(packageName);
    413     }
    414 
    415     @Override
    416     public void setPremiumSmsPermission(String packageName, int permission) {
    417         mDispatcher.setPremiumSmsPermission(packageName, permission);
    418     }
    419 
    420     /**
    421      * create SmsRawData lists from all sms record byte[]
    422      * Use null to indicate "free" record
    423      *
    424      * @param messages List of message records from EF_SMS.
    425      * @return SmsRawData list of all in-used records
    426      */
    427     protected ArrayList<SmsRawData> buildValidRawData(ArrayList<byte[]> messages) {
    428         int count = messages.size();
    429         ArrayList<SmsRawData> ret;
    430 
    431         ret = new ArrayList<SmsRawData>(count);
    432 
    433         for (int i = 0; i < count; i++) {
    434             byte[] ba = messages.get(i);
    435             if (ba[0] == STATUS_ON_ICC_FREE) {
    436                 ret.add(null);
    437             } else {
    438                 ret.add(new SmsRawData(messages.get(i)));
    439             }
    440         }
    441 
    442         return ret;
    443     }
    444 
    445     /**
    446      * Generates an EF_SMS record from status and raw PDU.
    447      *
    448      * @param status Message status.  See TS 51.011 10.5.3.
    449      * @param pdu Raw message PDU.
    450      * @return byte array for the record.
    451      */
    452     protected byte[] makeSmsRecordData(int status, byte[] pdu) {
    453         byte[] data = new byte[IccConstants.SMS_RECORD_LENGTH];
    454 
    455         // Status bits for this record.  See TS 51.011 10.5.3
    456         data[0] = (byte)(status & 7);
    457 
    458         System.arraycopy(pdu, 0, data, 1, pdu.length);
    459 
    460         // Pad out with 0xFF's.
    461         for (int j = pdu.length+1; j < IccConstants.SMS_RECORD_LENGTH; j++) {
    462             data[j] = -1;
    463         }
    464 
    465         return data;
    466     }
    467 
    468     protected abstract void deleteSms(int index, Message response);
    469 
    470     protected abstract void writeSms(int status, byte[] pdu, byte[] smsc, Message response);
    471 
    472     protected abstract void log(String msg);
    473 
    474 }
    475