03ed2443b701618a40ddcc1fd1dc00af5a8e1539
[android/platform/frameworks/opt/telephony.git] / src / java / com / android / internal / telephony / IccSmsInterfaceManager.java
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 }