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 17package com.android.internal.telephony.gsm; 18 19import android.content.Context; 20import android.content.pm.PackageManager; 21import android.os.AsyncResult; 22import android.os.Binder; 23import android.os.Handler; 24import android.os.Message; 25import android.util.Log; 26 27import com.android.internal.telephony.IccConstants; 28import com.android.internal.telephony.IccSmsInterfaceManager; 29import com.android.internal.telephony.IccUtils; 30import com.android.internal.telephony.IntRangeManager; 31import com.android.internal.telephony.SMSDispatcher; 32import com.android.internal.telephony.SmsRawData; 33 34import java.util.ArrayList; 35import java.util.Arrays; 36import java.util.HashMap; 37import java.util.HashSet; 38import java.util.List; 39import java.util.Set; 40 41import 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 */ 47public 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