Home | History | Annotate | Download | only in gsm
      1 /*
      2 ** Copyright 2007, 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.gsm;
     18 
     19 import android.content.Context;
     20 import android.content.pm.PackageManager;
     21 import android.os.AsyncResult;
     22 import android.os.Binder;
     23 import android.os.Handler;
     24 import android.os.Message;
     25 import android.util.Log;
     26 
     27 import com.android.internal.telephony.IccConstants;
     28 import com.android.internal.telephony.IccSmsInterfaceManager;
     29 import com.android.internal.telephony.IccUtils;
     30 import com.android.internal.telephony.IntRangeManager;
     31 import com.android.internal.telephony.SMSDispatcher;
     32 import com.android.internal.telephony.SmsRawData;
     33 
     34 import java.util.ArrayList;
     35 import java.util.Arrays;
     36 import java.util.HashMap;
     37 import java.util.HashSet;
     38 import java.util.List;
     39 import java.util.Set;
     40 
     41 import static android.telephony.SmsManager.STATUS_ON_ICC_FREE;
     42 
     43 /**
     44  * SimSmsInterfaceManager to provide an inter-process communication to
     45  * access Sms in Sim.
     46  */
     47 public class SimSmsInterfaceManager extends IccSmsInterfaceManager {
     48     static final String LOG_TAG = "GSM";
     49     static final boolean DBG = true;
     50 
     51     private final Object mLock = new Object();
     52     private boolean mSuccess;
     53     private List<SmsRawData> mSms;
     54     private HashMap<Integer, HashSet<String>> mCellBroadcastSubscriptions =
     55             new HashMap<Integer, HashSet<String>>();
     56 
     57     private CellBroadcastRangeManager mCellBroadcastRangeManager =
     58             new CellBroadcastRangeManager();
     59 
     60     private static final int EVENT_LOAD_DONE = 1;
     61     private static final int EVENT_UPDATE_DONE = 2;
     62     private static final int EVENT_SET_BROADCAST_ACTIVATION_DONE = 3;
     63     private static final int EVENT_SET_BROADCAST_CONFIG_DONE = 4;
     64     private static final int SMS_CB_CODE_SCHEME_MIN = 0;
     65     private static final int SMS_CB_CODE_SCHEME_MAX = 255;
     66 
     67     Handler mHandler = new Handler() {
     68         @Override
     69         public void handleMessage(Message msg) {
     70             AsyncResult ar;
     71 
     72             switch (msg.what) {
     73                 case EVENT_UPDATE_DONE:
     74                     ar = (AsyncResult) msg.obj;
     75                     synchronized (mLock) {
     76                         mSuccess = (ar.exception == null);
     77                         mLock.notifyAll();
     78                     }
     79                     break;
     80                 case EVENT_LOAD_DONE:
     81                     ar = (AsyncResult)msg.obj;
     82                     synchronized (mLock) {
     83                         if (ar.exception == null) {
     84                             mSms  = buildValidRawData((ArrayList<byte[]>) ar.result);
     85                         } else {
     86                             if(DBG) log("Cannot load Sms records");
     87                             if (mSms != null)
     88                                 mSms.clear();
     89                         }
     90                         mLock.notifyAll();
     91                     }
     92                     break;
     93                 case EVENT_SET_BROADCAST_ACTIVATION_DONE:
     94                 case EVENT_SET_BROADCAST_CONFIG_DONE:
     95                     ar = (AsyncResult) msg.obj;
     96                     synchronized (mLock) {
     97                         mSuccess = (ar.exception == null);
     98                         mLock.notifyAll();
     99                     }
    100                     break;
    101             }
    102         }
    103     };
    104 
    105     public SimSmsInterfaceManager(GSMPhone phone, SMSDispatcher dispatcher) {
    106         super(phone);
    107         mDispatcher = dispatcher;
    108     }
    109 
    110     public void dispose() {
    111     }
    112 
    113     @Override
    114     protected void finalize() {
    115         try {
    116             super.finalize();
    117         } catch (Throwable throwable) {
    118             Log.e(LOG_TAG, "Error while finalizing:", throwable);
    119         }
    120         if(DBG) Log.d(LOG_TAG, "SimSmsInterfaceManager finalized");
    121     }
    122 
    123     /**
    124      * Update the specified message on the SIM.
    125      *
    126      * @param index record index of message to update
    127      * @param status new message status (STATUS_ON_ICC_READ,
    128      *                  STATUS_ON_ICC_UNREAD, STATUS_ON_ICC_SENT,
    129      *                  STATUS_ON_ICC_UNSENT, STATUS_ON_ICC_FREE)
    130      * @param pdu the raw PDU to store
    131      * @return success or not
    132      *
    133      */
    134     public boolean
    135     updateMessageOnIccEf(int index, int status, byte[] pdu) {
    136         if (DBG) log("updateMessageOnIccEf: index=" + index +
    137                 " status=" + status + " ==> " +
    138                 "("+ Arrays.toString(pdu) + ")");
    139         enforceReceiveAndSend("Updating message on SIM");
    140         synchronized(mLock) {
    141             mSuccess = false;
    142             Message response = mHandler.obtainMessage(EVENT_UPDATE_DONE);
    143 
    144             if (status == STATUS_ON_ICC_FREE) {
    145                 // Special case FREE: call deleteSmsOnSim instead of
    146                 // manipulating the SIM record
    147                 mPhone.mCM.deleteSmsOnSim(index, response);
    148             } else {
    149                 byte[] record = makeSmsRecordData(status, pdu);
    150                 mPhone.getIccFileHandler().updateEFLinearFixed(
    151                         IccConstants.EF_SMS,
    152                         index, record, null, response);
    153             }
    154             try {
    155                 mLock.wait();
    156             } catch (InterruptedException e) {
    157                 log("interrupted while trying to update by index");
    158             }
    159         }
    160         return mSuccess;
    161     }
    162 
    163     /**
    164      * Copy a raw SMS PDU to the SIM.
    165      *
    166      * @param pdu the raw PDU to store
    167      * @param status message status (STATUS_ON_ICC_READ, STATUS_ON_ICC_UNREAD,
    168      *               STATUS_ON_ICC_SENT, STATUS_ON_ICC_UNSENT)
    169      * @return success or not
    170      *
    171      */
    172     public boolean copyMessageToIccEf(int status, byte[] pdu, byte[] smsc) {
    173         if (DBG) log("copyMessageToIccEf: status=" + status + " ==> " +
    174                 "pdu=("+ Arrays.toString(pdu) +
    175                 "), smsm=(" + Arrays.toString(smsc) +")");
    176         enforceReceiveAndSend("Copying message to SIM");
    177         synchronized(mLock) {
    178             mSuccess = false;
    179             Message response = mHandler.obtainMessage(EVENT_UPDATE_DONE);
    180 
    181             mPhone.mCM.writeSmsToSim(status, IccUtils.bytesToHexString(smsc),
    182                     IccUtils.bytesToHexString(pdu), response);
    183 
    184             try {
    185                 mLock.wait();
    186             } catch (InterruptedException e) {
    187                 log("interrupted while trying to update by index");
    188             }
    189         }
    190         return mSuccess;
    191     }
    192 
    193     /**
    194      * Retrieves all messages currently stored on ICC.
    195      *
    196      * @return list of SmsRawData of all sms on ICC
    197      */
    198     public List<SmsRawData> getAllMessagesFromIccEf() {
    199         if (DBG) log("getAllMessagesFromEF");
    200 
    201         Context context = mPhone.getContext();
    202 
    203         context.enforceCallingPermission(
    204                 "android.permission.RECEIVE_SMS",
    205                 "Reading messages from SIM");
    206         synchronized(mLock) {
    207             Message response = mHandler.obtainMessage(EVENT_LOAD_DONE);
    208             mPhone.getIccFileHandler().loadEFLinearFixedAll(IccConstants.EF_SMS, response);
    209 
    210             try {
    211                 mLock.wait();
    212             } catch (InterruptedException e) {
    213                 log("interrupted while trying to load from the SIM");
    214             }
    215         }
    216         return mSms;
    217     }
    218 
    219     public boolean enableCellBroadcast(int messageIdentifier) {
    220         return enableCellBroadcastRange(messageIdentifier, messageIdentifier);
    221     }
    222 
    223     public boolean disableCellBroadcast(int messageIdentifier) {
    224         return disableCellBroadcastRange(messageIdentifier, messageIdentifier);
    225     }
    226 
    227     public boolean enableCellBroadcastRange(int startMessageId, int endMessageId) {
    228         if (DBG) log("enableCellBroadcastRange");
    229 
    230         Context context = mPhone.getContext();
    231 
    232         context.enforceCallingPermission(
    233                 "android.permission.RECEIVE_SMS",
    234                 "Enabling cell broadcast SMS");
    235 
    236         String client = context.getPackageManager().getNameForUid(
    237                 Binder.getCallingUid());
    238 
    239         if (!mCellBroadcastRangeManager.enableRange(startMessageId, endMessageId, client)) {
    240             log("Failed to add cell broadcast subscription for MID range " + startMessageId
    241                     + " to " + endMessageId + " from client " + client);
    242             return false;
    243         }
    244 
    245         if (DBG)
    246             log("Added cell broadcast subscription for MID range " + startMessageId
    247                     + " to " + endMessageId + " from client " + client);
    248 
    249         setCellBroadcastActivation(!mCellBroadcastRangeManager.isEmpty());
    250 
    251         return true;
    252     }
    253 
    254     public boolean disableCellBroadcastRange(int startMessageId, int endMessageId) {
    255         if (DBG) log("disableCellBroadcastRange");
    256 
    257         Context context = mPhone.getContext();
    258 
    259         context.enforceCallingPermission(
    260                 "android.permission.RECEIVE_SMS",
    261                 "Disabling cell broadcast SMS");
    262 
    263         String client = context.getPackageManager().getNameForUid(
    264                 Binder.getCallingUid());
    265 
    266         if (!mCellBroadcastRangeManager.disableRange(startMessageId, endMessageId, client)) {
    267             log("Failed to remove cell broadcast subscription for MID range " + startMessageId
    268                     + " to " + endMessageId + " from client " + client);
    269             return false;
    270         }
    271 
    272         if (DBG)
    273             log("Removed cell broadcast subscription for MID range " + startMessageId
    274                     + " to " + endMessageId + " from client " + client);
    275 
    276         setCellBroadcastActivation(!mCellBroadcastRangeManager.isEmpty());
    277 
    278         return true;
    279     }
    280 
    281     class CellBroadcastRangeManager extends IntRangeManager {
    282         private ArrayList<SmsBroadcastConfigInfo> mConfigList =
    283                 new ArrayList<SmsBroadcastConfigInfo>();
    284 
    285         /**
    286          * Called when the list of enabled ranges has changed. This will be
    287          * followed by zero or more calls to {@link #addRange} followed by
    288          * a call to {@link #finishUpdate}.
    289          */
    290         protected void startUpdate() {
    291             mConfigList.clear();
    292         }
    293 
    294         /**
    295          * Called after {@link #startUpdate} to indicate a range of enabled
    296          * values.
    297          * @param startId the first id included in the range
    298          * @param endId the last id included in the range
    299          */
    300         protected void addRange(int startId, int endId, boolean selected) {
    301             mConfigList.add(new SmsBroadcastConfigInfo(startId, endId,
    302                         SMS_CB_CODE_SCHEME_MIN, SMS_CB_CODE_SCHEME_MAX, selected));
    303         }
    304 
    305         /**
    306          * Called to indicate the end of a range update started by the
    307          * previous call to {@link #startUpdate}.
    308          * @return true if successful, false otherwise
    309          */
    310         protected boolean finishUpdate() {
    311             if (mConfigList.isEmpty()) {
    312                 return true;
    313             } else {
    314                 SmsBroadcastConfigInfo[] configs =
    315                         mConfigList.toArray(new SmsBroadcastConfigInfo[mConfigList.size()]);
    316                 return setCellBroadcastConfig(configs);
    317             }
    318         }
    319     }
    320 
    321     private boolean setCellBroadcastConfig(SmsBroadcastConfigInfo[] configs) {
    322         if (DBG)
    323             log("Calling setGsmBroadcastConfig with " + configs.length + " configurations");
    324 
    325         synchronized (mLock) {
    326             Message response = mHandler.obtainMessage(EVENT_SET_BROADCAST_CONFIG_DONE);
    327 
    328             mSuccess = false;
    329             mPhone.mCM.setGsmBroadcastConfig(configs, response);
    330 
    331             try {
    332                 mLock.wait();
    333             } catch (InterruptedException e) {
    334                 log("interrupted while trying to set cell broadcast config");
    335             }
    336         }
    337 
    338         return mSuccess;
    339     }
    340 
    341     private boolean setCellBroadcastActivation(boolean activate) {
    342         if (DBG)
    343             log("Calling setCellBroadcastActivation(" + activate + ')');
    344 
    345         synchronized (mLock) {
    346             Message response = mHandler.obtainMessage(EVENT_SET_BROADCAST_ACTIVATION_DONE);
    347 
    348             mSuccess = false;
    349             mPhone.mCM.setGsmBroadcastActivation(activate, response);
    350 
    351             try {
    352                 mLock.wait();
    353             } catch (InterruptedException e) {
    354                 log("interrupted while trying to set cell broadcast activation");
    355             }
    356         }
    357 
    358         return mSuccess;
    359     }
    360 
    361     @Override
    362     protected void log(String msg) {
    363         Log.d(LOG_TAG, "[SimSmsInterfaceManager] " + msg);
    364     }
    365 }
    366