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
17package com.android.internal.telephony;
18
19import android.Manifest;
20import android.app.AppOpsManager;
21import android.app.PendingIntent;
22import android.content.ContentResolver;
23import android.content.Context;
24import android.database.Cursor;
25import android.database.sqlite.SQLiteException;
26import android.net.Uri;
27import android.os.AsyncResult;
28import android.os.Binder;
29import android.os.Handler;
30import android.os.Message;
31import android.os.UserManager;
32import android.provider.Telephony;
33import android.telephony.Rlog;
34import android.telephony.SmsManager;
35import android.telephony.SmsMessage;
36import android.util.Log;
37
38import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo;
39import com.android.internal.telephony.cdma.CdmaSmsBroadcastConfigInfo;
40import com.android.internal.telephony.uicc.IccConstants;
41import com.android.internal.telephony.uicc.IccFileHandler;
42import com.android.internal.telephony.uicc.IccUtils;
43import com.android.internal.telephony.uicc.UiccController;
44import com.android.internal.util.HexDump;
45
46import java.util.ArrayList;
47import java.util.Arrays;
48import java.util.List;
49
50import static android.telephony.SmsManager.STATUS_ON_ICC_FREE;
51import static android.telephony.SmsManager.STATUS_ON_ICC_READ;
52import static android.telephony.SmsManager.STATUS_ON_ICC_UNREAD;
53
54import android.telephony.TelephonyManager;
55
56/**
57 * IccSmsInterfaceManager to provide an inter-process communication to
58 * access Sms in Icc.
59 */
60public class IccSmsInterfaceManager {
61    static final String LOG_TAG = "IccSmsInterfaceManager";
62    static final boolean DBG = true;
63
64    protected final Object mLock = new Object();
65    protected boolean mSuccess;
66    private List<SmsRawData> mSms;
67
68    private CellBroadcastRangeManager mCellBroadcastRangeManager =
69            new CellBroadcastRangeManager();
70    private CdmaBroadcastRangeManager mCdmaBroadcastRangeManager =
71            new CdmaBroadcastRangeManager();
72
73    private static final int EVENT_LOAD_DONE = 1;
74    private static final int EVENT_UPDATE_DONE = 2;
75    protected static final int EVENT_SET_BROADCAST_ACTIVATION_DONE = 3;
76    protected static final int EVENT_SET_BROADCAST_CONFIG_DONE = 4;
77    private static final int SMS_CB_CODE_SCHEME_MIN = 0;
78    private static final int SMS_CB_CODE_SCHEME_MAX = 255;
79
80    protected Phone mPhone;
81    final protected Context mContext;
82    final protected AppOpsManager mAppOps;
83    final private UserManager mUserManager;
84    protected SMSDispatcher mDispatcher;
85
86    protected Handler mHandler = new Handler() {
87        @Override
88        public void handleMessage(Message msg) {
89            AsyncResult ar;
90
91            switch (msg.what) {
92                case EVENT_UPDATE_DONE:
93                    ar = (AsyncResult) msg.obj;
94                    synchronized (mLock) {
95                        mSuccess = (ar.exception == null);
96                        mLock.notifyAll();
97                    }
98                    break;
99                case EVENT_LOAD_DONE:
100                    ar = (AsyncResult)msg.obj;
101                    synchronized (mLock) {
102                        if (ar.exception == null) {
103                            mSms = buildValidRawData((ArrayList<byte[]>) ar.result);
104                            //Mark SMS as read after importing it from card.
105                            markMessagesAsRead((ArrayList<byte[]>) ar.result);
106                        } else {
107                            if (Rlog.isLoggable("SMS", Log.DEBUG)) {
108                                log("Cannot load Sms records");
109                            }
110                            mSms = null;
111                        }
112                        mLock.notifyAll();
113                    }
114                    break;
115                case EVENT_SET_BROADCAST_ACTIVATION_DONE:
116                case EVENT_SET_BROADCAST_CONFIG_DONE:
117                    ar = (AsyncResult) msg.obj;
118                    synchronized (mLock) {
119                        mSuccess = (ar.exception == null);
120                        mLock.notifyAll();
121                    }
122                    break;
123            }
124        }
125    };
126
127    protected IccSmsInterfaceManager(Phone phone) {
128        mPhone = phone;
129        mContext = phone.getContext();
130        mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
131        mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
132        mDispatcher = new ImsSMSDispatcher(phone,
133                phone.mSmsStorageMonitor, phone.mSmsUsageMonitor);
134    }
135
136    protected void markMessagesAsRead(ArrayList<byte[]> messages) {
137        if (messages == null) {
138            return;
139        }
140
141        //IccFileHandler can be null, if icc card is absent.
142        IccFileHandler fh = mPhone.getIccFileHandler();
143        if (fh == null) {
144            //shouldn't really happen, as messages are marked as read, only
145            //after importing it from icc.
146            if (Rlog.isLoggable("SMS", Log.DEBUG)) {
147                log("markMessagesAsRead - aborting, no icc card present.");
148            }
149            return;
150        }
151
152        int count = messages.size();
153
154        for (int i = 0; i < count; i++) {
155             byte[] ba = messages.get(i);
156             if (ba[0] == STATUS_ON_ICC_UNREAD) {
157                 int n = ba.length;
158                 byte[] nba = new byte[n - 1];
159                 System.arraycopy(ba, 1, nba, 0, n - 1);
160                 byte[] record = makeSmsRecordData(STATUS_ON_ICC_READ, nba);
161                 fh.updateEFLinearFixed(IccConstants.EF_SMS, i + 1, record, null, null);
162                 if (Rlog.isLoggable("SMS", Log.DEBUG)) {
163                     log("SMS " + (i + 1) + " marked as read");
164                 }
165             }
166        }
167    }
168
169    protected void updatePhoneObject(Phone phone) {
170        mPhone = phone;
171        mDispatcher.updatePhoneObject(phone);
172    }
173
174    protected void enforceReceiveAndSend(String message) {
175        mContext.enforceCallingOrSelfPermission(
176                Manifest.permission.RECEIVE_SMS, message);
177        mContext.enforceCallingOrSelfPermission(
178                Manifest.permission.SEND_SMS, message);
179    }
180
181    /**
182     * Update the specified message on the Icc.
183     *
184     * @param index record index of message to update
185     * @param status new message status (STATUS_ON_ICC_READ,
186     *                  STATUS_ON_ICC_UNREAD, STATUS_ON_ICC_SENT,
187     *                  STATUS_ON_ICC_UNSENT, STATUS_ON_ICC_FREE)
188     * @param pdu the raw PDU to store
189     * @return success or not
190     *
191     */
192
193    public boolean
194    updateMessageOnIccEf(String callingPackage, int index, int status, byte[] pdu) {
195        if (DBG) log("updateMessageOnIccEf: index=" + index +
196                " status=" + status + " ==> " +
197                "("+ Arrays.toString(pdu) + ")");
198        enforceReceiveAndSend("Updating message on Icc");
199        if (mAppOps.noteOp(AppOpsManager.OP_WRITE_ICC_SMS, Binder.getCallingUid(),
200                callingPackage) != AppOpsManager.MODE_ALLOWED) {
201            return false;
202        }
203        synchronized(mLock) {
204            mSuccess = false;
205            Message response = mHandler.obtainMessage(EVENT_UPDATE_DONE);
206
207            if (status == STATUS_ON_ICC_FREE) {
208                // RIL_REQUEST_DELETE_SMS_ON_SIM vs RIL_REQUEST_CDMA_DELETE_SMS_ON_RUIM
209                // Special case FREE: call deleteSmsOnSim/Ruim instead of
210                // manipulating the record
211                // Will eventually fail if icc card is not present.
212                if (PhoneConstants.PHONE_TYPE_GSM == mPhone.getPhoneType()) {
213                    mPhone.mCi.deleteSmsOnSim(index, response);
214                } else {
215                    mPhone.mCi.deleteSmsOnRuim(index, response);
216                }
217            } else {
218                //IccFilehandler can be null if ICC card is not present.
219                IccFileHandler fh = mPhone.getIccFileHandler();
220                if (fh == null) {
221                    response.recycle();
222                    return mSuccess; /* is false */
223                }
224                byte[] record = makeSmsRecordData(status, pdu);
225                fh.updateEFLinearFixed(
226                        IccConstants.EF_SMS,
227                        index, record, null, response);
228            }
229            try {
230                mLock.wait();
231            } catch (InterruptedException e) {
232                log("interrupted while trying to update by index");
233            }
234        }
235        return mSuccess;
236    }
237
238    /**
239     * Copy a raw SMS PDU to the Icc.
240     *
241     * @param pdu the raw PDU to store
242     * @param status message status (STATUS_ON_ICC_READ, STATUS_ON_ICC_UNREAD,
243     *               STATUS_ON_ICC_SENT, STATUS_ON_ICC_UNSENT)
244     * @return success or not
245     *
246     */
247    public boolean copyMessageToIccEf(String callingPackage, int status, byte[] pdu, byte[] smsc) {
248        //NOTE smsc not used in RUIM
249        if (DBG) log("copyMessageToIccEf: status=" + status + " ==> " +
250                "pdu=("+ Arrays.toString(pdu) +
251                "), smsc=(" + Arrays.toString(smsc) +")");
252        enforceReceiveAndSend("Copying message to Icc");
253        if (mAppOps.noteOp(AppOpsManager.OP_WRITE_ICC_SMS, Binder.getCallingUid(),
254                callingPackage) != AppOpsManager.MODE_ALLOWED) {
255            return false;
256        }
257        synchronized(mLock) {
258            mSuccess = false;
259            Message response = mHandler.obtainMessage(EVENT_UPDATE_DONE);
260
261            //RIL_REQUEST_WRITE_SMS_TO_SIM vs RIL_REQUEST_CDMA_WRITE_SMS_TO_RUIM
262            if (PhoneConstants.PHONE_TYPE_GSM == mPhone.getPhoneType()) {
263                mPhone.mCi.writeSmsToSim(status, IccUtils.bytesToHexString(smsc),
264                        IccUtils.bytesToHexString(pdu), response);
265            } else {
266                mPhone.mCi.writeSmsToRuim(status, IccUtils.bytesToHexString(pdu),
267                        response);
268            }
269
270            try {
271                mLock.wait();
272            } catch (InterruptedException e) {
273                log("interrupted while trying to update by index");
274            }
275        }
276        return mSuccess;
277    }
278
279    /**
280     * Retrieves all messages currently stored on Icc.
281     *
282     * @return list of SmsRawData of all sms on Icc
283     */
284
285    public List<SmsRawData> getAllMessagesFromIccEf(String callingPackage) {
286        if (DBG) log("getAllMessagesFromEF");
287
288        mContext.enforceCallingOrSelfPermission(
289                Manifest.permission.RECEIVE_SMS,
290                "Reading messages from Icc");
291        if (mAppOps.noteOp(AppOpsManager.OP_READ_ICC_SMS, Binder.getCallingUid(),
292                callingPackage) != AppOpsManager.MODE_ALLOWED) {
293            return new ArrayList<SmsRawData>();
294        }
295        synchronized(mLock) {
296
297            IccFileHandler fh = mPhone.getIccFileHandler();
298            if (fh == null) {
299                Rlog.e(LOG_TAG, "Cannot load Sms records. No icc card?");
300                mSms = null;
301                return mSms;
302            }
303
304            Message response = mHandler.obtainMessage(EVENT_LOAD_DONE);
305            fh.loadEFLinearFixedAll(IccConstants.EF_SMS, response);
306
307            try {
308                mLock.wait();
309            } catch (InterruptedException e) {
310                log("interrupted while trying to load from the Icc");
311            }
312        }
313        return mSms;
314    }
315
316    /**
317     * A permissions check before passing to {@link IccSmsInterfaceManager#sendDataInternal}.
318     * This method checks if the calling package or itself has the permission to send the data sms.
319     */
320    public void sendDataWithSelfPermissions(String callingPackage, String destAddr, String scAddr,
321            int destPort, byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
322        mPhone.getContext().enforceCallingOrSelfPermission(
323                Manifest.permission.SEND_SMS,
324                "Sending SMS message");
325        sendDataInternal(callingPackage, destAddr, scAddr, destPort, data, sentIntent,
326                deliveryIntent);
327    }
328
329    /**
330     * A permissions check before passing to {@link IccSmsInterfaceManager#sendDataInternal}.
331     * This method checks only if the calling package has the permission to send the data sms.
332     */
333    public void sendData(String callingPackage, String destAddr, String scAddr, int destPort,
334            byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
335        mPhone.getContext().enforceCallingPermission(
336                Manifest.permission.SEND_SMS,
337                "Sending SMS message");
338        sendDataInternal(callingPackage, destAddr, scAddr, destPort, data, sentIntent,
339                deliveryIntent);
340    }
341
342    /**
343     * Send a data based SMS to a specific application port.
344     *
345     * @param destAddr the address to send the message to
346     * @param scAddr is the service center address or null to use
347     *  the current default SMSC
348     * @param destPort the port to deliver the message to
349     * @param data the body of the message to send
350     * @param sentIntent if not NULL this <code>PendingIntent</code> is
351     *  broadcast when the message is successfully sent, or failed.
352     *  The result code will be <code>Activity.RESULT_OK<code> for success,
353     *  or one of these errors:<br>
354     *  <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
355     *  <code>RESULT_ERROR_RADIO_OFF</code><br>
356     *  <code>RESULT_ERROR_NULL_PDU</code><br>
357     *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
358     *  the extra "errorCode" containing a radio technology specific value,
359     *  generally only useful for troubleshooting.<br>
360     *  The per-application based SMS control checks sentIntent. If sentIntent
361     *  is NULL the caller will be checked against all unknown applications,
362     *  which cause smaller number of SMS to be sent in checking period.
363     * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
364     *  broadcast when the message is delivered to the recipient.  The
365     *  raw pdu of the status report is in the extended data ("pdu").
366     */
367
368    private void sendDataInternal(String callingPackage, String destAddr, String scAddr,
369            int destPort, byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
370        if (Rlog.isLoggable("SMS", Log.VERBOSE)) {
371            log("sendData: destAddr=" + destAddr + " scAddr=" + scAddr + " destPort=" +
372                destPort + " data='"+ HexDump.toHexString(data)  + "' sentIntent=" +
373                sentIntent + " deliveryIntent=" + deliveryIntent);
374        }
375        if (mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(),
376                callingPackage) != AppOpsManager.MODE_ALLOWED) {
377            return;
378        }
379        destAddr = filterDestAddress(destAddr);
380        mDispatcher.sendData(destAddr, scAddr, destPort, data, sentIntent, deliveryIntent);
381    }
382
383    /**
384     * A permissions check before passing to {@link IccSmsInterfaceManager#sendTextInternal}.
385     * This method checks only if the calling package has the permission to send the sms.
386     */
387    public void sendText(String callingPackage, String destAddr, String scAddr,
388            String text, PendingIntent sentIntent, PendingIntent deliveryIntent,
389            boolean persistMessageForNonDefaultSmsApp) {
390        mPhone.getContext().enforceCallingPermission(
391                Manifest.permission.SEND_SMS,
392                "Sending SMS message");
393        sendTextInternal(callingPackage, destAddr, scAddr, text, sentIntent, deliveryIntent,
394            persistMessageForNonDefaultSmsApp);
395    }
396
397    /**
398     * A permissions check before passing to {@link IccSmsInterfaceManager#sendTextInternal}.
399     * This method checks if the calling package or itself has the permission to send the sms.
400     */
401    public void sendTextWithSelfPermissions(String callingPackage, String destAddr, String scAddr,
402            String text, PendingIntent sentIntent, PendingIntent deliveryIntent) {
403        mPhone.getContext().enforceCallingOrSelfPermission(
404                Manifest.permission.SEND_SMS,
405                "Sending SMS message");
406        sendTextInternal(callingPackage, destAddr, scAddr, text, sentIntent, deliveryIntent,
407            true /* persistMessageForNonDefaultSmsApp */);
408    }
409
410    /**
411     * Send a text based SMS.
412     *
413     * @param destAddr the address to send the message to
414     * @param scAddr is the service center address or null to use
415     *  the current default SMSC
416     * @param text the body of the message to send
417     * @param sentIntent if not NULL this <code>PendingIntent</code> is
418     *  broadcast when the message is successfully sent, or failed.
419     *  The result code will be <code>Activity.RESULT_OK<code> for success,
420     *  or one of these errors:<br>
421     *  <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
422     *  <code>RESULT_ERROR_RADIO_OFF</code><br>
423     *  <code>RESULT_ERROR_NULL_PDU</code><br>
424     *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
425     *  the extra "errorCode" containing a radio technology specific value,
426     *  generally only useful for troubleshooting.<br>
427     *  The per-application based SMS control checks sentIntent. If sentIntent
428     *  is NULL the caller will be checked against all unknown applications,
429     *  which cause smaller number of SMS to be sent in checking period.
430     * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
431     *  broadcast when the message is delivered to the recipient.  The
432     *  raw pdu of the status report is in the extended data ("pdu").
433     */
434
435    private void sendTextInternal(String callingPackage, String destAddr, String scAddr,
436            String text, PendingIntent sentIntent, PendingIntent deliveryIntent,
437            boolean persistMessageForNonDefaultSmsApp) {
438        if (Rlog.isLoggable("SMS", Log.VERBOSE)) {
439            log("sendText: destAddr=" + destAddr + " scAddr=" + scAddr +
440                " text='"+ text + "' sentIntent=" +
441                sentIntent + " deliveryIntent=" + deliveryIntent);
442        }
443        if (mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(),
444                callingPackage) != AppOpsManager.MODE_ALLOWED) {
445            return;
446        }
447        if (!persistMessageForNonDefaultSmsApp) {
448            // Only allow carrier app to skip auto message persistence.
449            enforceCarrierPrivilege();
450        }
451        destAddr = filterDestAddress(destAddr);
452        mDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent,
453                null/*messageUri*/, callingPackage, persistMessageForNonDefaultSmsApp);
454    }
455
456    /**
457     * Inject an SMS PDU into the android application framework.
458     *
459     * @param pdu is the byte array of pdu to be injected into android application framework
460     * @param format is the format of SMS pdu (3gpp or 3gpp2)
461     * @param receivedIntent if not NULL this <code>PendingIntent</code> is
462     *  broadcast when the message is successfully received by the
463     *  android application framework. This intent is broadcasted at
464     *  the same time an SMS received from radio is acknowledged back.
465     */
466    public void injectSmsPdu(byte[] pdu, String format, PendingIntent receivedIntent) {
467        enforceCarrierPrivilege();
468        if (Rlog.isLoggable("SMS", Log.VERBOSE)) {
469            log("pdu: " + pdu +
470                "\n format=" + format +
471                "\n receivedIntent=" + receivedIntent);
472        }
473        mDispatcher.injectSmsPdu(pdu, format, receivedIntent);
474    }
475
476    /**
477     * Send a multi-part text based SMS.
478     *
479     * @param destAddr the address to send the message to
480     * @param scAddr is the service center address or null to use
481     *   the current default SMSC
482     * @param parts an <code>ArrayList</code> of strings that, in order,
483     *   comprise the original message
484     * @param sentIntents if not null, an <code>ArrayList</code> of
485     *   <code>PendingIntent</code>s (one for each message part) that is
486     *   broadcast when the corresponding message part has been sent.
487     *   The result code will be <code>Activity.RESULT_OK<code> for success,
488     *   or one of these errors:
489     *   <code>RESULT_ERROR_GENERIC_FAILURE</code>
490     *   <code>RESULT_ERROR_RADIO_OFF</code>
491     *   <code>RESULT_ERROR_NULL_PDU</code>.
492     *  The per-application based SMS control checks sentIntent. If sentIntent
493     *  is NULL the caller will be checked against all unknown applications,
494     *  which cause smaller number of SMS to be sent in checking period.
495     * @param deliveryIntents if not null, an <code>ArrayList</code> of
496     *   <code>PendingIntent</code>s (one for each message part) that is
497     *   broadcast when the corresponding message part has been delivered
498     *   to the recipient.  The raw pdu of the status report is in the
499     *   extended data ("pdu").
500     */
501
502    public void sendMultipartText(String callingPackage, String destAddr, String scAddr,
503            List<String> parts, List<PendingIntent> sentIntents,
504            List<PendingIntent> deliveryIntents, boolean persistMessageForNonDefaultSmsApp) {
505        mPhone.getContext().enforceCallingPermission(
506                Manifest.permission.SEND_SMS,
507                "Sending SMS message");
508        if (!persistMessageForNonDefaultSmsApp) {
509            // Only allow carrier app to skip auto message persistence.
510            enforceCarrierPrivilege();
511        }
512        if (Rlog.isLoggable("SMS", Log.VERBOSE)) {
513            int i = 0;
514            for (String part : parts) {
515                log("sendMultipartText: destAddr=" + destAddr + ", srAddr=" + scAddr +
516                        ", part[" + (i++) + "]=" + part);
517            }
518        }
519        if (mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(),
520                callingPackage) != AppOpsManager.MODE_ALLOWED) {
521            return;
522        }
523
524        destAddr = filterDestAddress(destAddr);
525
526        if (parts.size() > 1 && parts.size() < 10 && !SmsMessage.hasEmsSupport()) {
527            for (int i = 0; i < parts.size(); i++) {
528                // If EMS is not supported, we have to break down EMS into single segment SMS
529                // and add page info " x/y".
530                String singlePart = parts.get(i);
531                if (SmsMessage.shouldAppendPageNumberAsPrefix()) {
532                    singlePart = String.valueOf(i + 1) + '/' + parts.size() + ' ' + singlePart;
533                } else {
534                    singlePart = singlePart.concat(' ' + String.valueOf(i + 1) + '/' + parts.size());
535                }
536
537                PendingIntent singleSentIntent = null;
538                if (sentIntents != null && sentIntents.size() > i) {
539                    singleSentIntent = sentIntents.get(i);
540                }
541
542                PendingIntent singleDeliveryIntent = null;
543                if (deliveryIntents != null && deliveryIntents.size() > i) {
544                    singleDeliveryIntent = deliveryIntents.get(i);
545                }
546
547                mDispatcher.sendText(destAddr, scAddr, singlePart,
548                        singleSentIntent, singleDeliveryIntent,
549                        null/*messageUri*/, callingPackage,
550                        persistMessageForNonDefaultSmsApp);
551            }
552            return;
553        }
554
555        mDispatcher.sendMultipartText(destAddr, scAddr, (ArrayList<String>) parts,
556                (ArrayList<PendingIntent>) sentIntents, (ArrayList<PendingIntent>) deliveryIntents,
557                null/*messageUri*/, callingPackage, persistMessageForNonDefaultSmsApp);
558    }
559
560
561    public int getPremiumSmsPermission(String packageName) {
562        return mDispatcher.getPremiumSmsPermission(packageName);
563    }
564
565
566    public void setPremiumSmsPermission(String packageName, int permission) {
567        mDispatcher.setPremiumSmsPermission(packageName, permission);
568    }
569
570    /**
571     * create SmsRawData lists from all sms record byte[]
572     * Use null to indicate "free" record
573     *
574     * @param messages List of message records from EF_SMS.
575     * @return SmsRawData list of all in-used records
576     */
577    protected ArrayList<SmsRawData> buildValidRawData(ArrayList<byte[]> messages) {
578        int count = messages.size();
579        ArrayList<SmsRawData> ret;
580
581        ret = new ArrayList<SmsRawData>(count);
582
583        for (int i = 0; i < count; i++) {
584            byte[] ba = messages.get(i);
585            if (ba[0] == STATUS_ON_ICC_FREE) {
586                ret.add(null);
587            } else {
588                ret.add(new SmsRawData(messages.get(i)));
589            }
590        }
591
592        return ret;
593    }
594
595    /**
596     * Generates an EF_SMS record from status and raw PDU.
597     *
598     * @param status Message status.  See TS 51.011 10.5.3.
599     * @param pdu Raw message PDU.
600     * @return byte array for the record.
601     */
602    protected byte[] makeSmsRecordData(int status, byte[] pdu) {
603        byte[] data;
604        if (PhoneConstants.PHONE_TYPE_GSM == mPhone.getPhoneType()) {
605            data = new byte[IccConstants.SMS_RECORD_LENGTH];
606        } else {
607            data = new byte[IccConstants.CDMA_SMS_RECORD_LENGTH];
608        }
609
610        // Status bits for this record.  See TS 51.011 10.5.3
611        data[0] = (byte)(status & 7);
612
613        System.arraycopy(pdu, 0, data, 1, pdu.length);
614
615        // Pad out with 0xFF's.
616        for (int j = pdu.length+1; j < data.length; j++) {
617            data[j] = -1;
618        }
619
620        return data;
621    }
622
623    public boolean enableCellBroadcast(int messageIdentifier, int ranType) {
624        return enableCellBroadcastRange(messageIdentifier, messageIdentifier, ranType);
625    }
626
627    public boolean disableCellBroadcast(int messageIdentifier, int ranType) {
628        return disableCellBroadcastRange(messageIdentifier, messageIdentifier, ranType);
629    }
630
631    public boolean enableCellBroadcastRange(int startMessageId, int endMessageId, int ranType) {
632        if (ranType == SmsManager.CELL_BROADCAST_RAN_TYPE_GSM) {
633            return enableGsmBroadcastRange(startMessageId, endMessageId);
634        } else if (ranType == SmsManager.CELL_BROADCAST_RAN_TYPE_CDMA) {
635            return enableCdmaBroadcastRange(startMessageId, endMessageId);
636        } else {
637            throw new IllegalArgumentException("Not a supportted RAN Type");
638        }
639    }
640
641    public boolean disableCellBroadcastRange(int startMessageId, int endMessageId, int ranType) {
642        if (ranType == SmsManager.CELL_BROADCAST_RAN_TYPE_GSM ) {
643            return disableGsmBroadcastRange(startMessageId, endMessageId);
644        } else if (ranType == SmsManager.CELL_BROADCAST_RAN_TYPE_CDMA)  {
645            return disableCdmaBroadcastRange(startMessageId, endMessageId);
646        } else {
647            throw new IllegalArgumentException("Not a supportted RAN Type");
648        }
649    }
650
651    synchronized public boolean enableGsmBroadcastRange(int startMessageId, int endMessageId) {
652
653        Context context = mPhone.getContext();
654
655        context.enforceCallingPermission(
656                "android.permission.RECEIVE_SMS",
657                "Enabling cell broadcast SMS");
658
659        String client = context.getPackageManager().getNameForUid(
660                Binder.getCallingUid());
661
662        if (!mCellBroadcastRangeManager.enableRange(startMessageId, endMessageId, client)) {
663            log("Failed to add GSM cell broadcast subscription for MID range " + startMessageId
664                    + " to " + endMessageId + " from client " + client);
665            return false;
666        }
667
668        if (DBG)
669            log("Added GSM cell broadcast subscription for MID range " + startMessageId
670                    + " to " + endMessageId + " from client " + client);
671
672        setCellBroadcastActivation(!mCellBroadcastRangeManager.isEmpty());
673
674        return true;
675    }
676
677    synchronized public boolean disableGsmBroadcastRange(int startMessageId, int endMessageId) {
678
679        Context context = mPhone.getContext();
680
681        context.enforceCallingPermission(
682                "android.permission.RECEIVE_SMS",
683                "Disabling cell broadcast SMS");
684
685        String client = context.getPackageManager().getNameForUid(
686                Binder.getCallingUid());
687
688        if (!mCellBroadcastRangeManager.disableRange(startMessageId, endMessageId, client)) {
689            log("Failed to remove GSM cell broadcast subscription for MID range " + startMessageId
690                    + " to " + endMessageId + " from client " + client);
691            return false;
692        }
693
694        if (DBG)
695            log("Removed GSM cell broadcast subscription for MID range " + startMessageId
696                    + " to " + endMessageId + " from client " + client);
697
698        setCellBroadcastActivation(!mCellBroadcastRangeManager.isEmpty());
699
700        return true;
701    }
702
703    synchronized public boolean enableCdmaBroadcastRange(int startMessageId, int endMessageId) {
704
705        Context context = mPhone.getContext();
706
707        context.enforceCallingPermission(
708                "android.permission.RECEIVE_SMS",
709                "Enabling cdma broadcast SMS");
710
711        String client = context.getPackageManager().getNameForUid(
712                Binder.getCallingUid());
713
714        if (!mCdmaBroadcastRangeManager.enableRange(startMessageId, endMessageId, client)) {
715            log("Failed to add cdma broadcast subscription for MID range " + startMessageId
716                    + " to " + endMessageId + " from client " + client);
717            return false;
718        }
719
720        if (DBG)
721            log("Added cdma broadcast subscription for MID range " + startMessageId
722                    + " to " + endMessageId + " from client " + client);
723
724        setCdmaBroadcastActivation(!mCdmaBroadcastRangeManager.isEmpty());
725
726        return true;
727    }
728
729    synchronized public boolean disableCdmaBroadcastRange(int startMessageId, int endMessageId) {
730
731        Context context = mPhone.getContext();
732
733        context.enforceCallingPermission(
734                "android.permission.RECEIVE_SMS",
735                "Disabling cell broadcast SMS");
736
737        String client = context.getPackageManager().getNameForUid(
738                Binder.getCallingUid());
739
740        if (!mCdmaBroadcastRangeManager.disableRange(startMessageId, endMessageId, client)) {
741            log("Failed to remove cdma broadcast subscription for MID range " + startMessageId
742                    + " to " + endMessageId + " from client " + client);
743            return false;
744        }
745
746        if (DBG)
747            log("Removed cdma broadcast subscription for MID range " + startMessageId
748                    + " to " + endMessageId + " from client " + client);
749
750        setCdmaBroadcastActivation(!mCdmaBroadcastRangeManager.isEmpty());
751
752        return true;
753    }
754
755    class CellBroadcastRangeManager extends IntRangeManager {
756        private ArrayList<SmsBroadcastConfigInfo> mConfigList =
757                new ArrayList<SmsBroadcastConfigInfo>();
758
759        /**
760         * Called when the list of enabled ranges has changed. This will be
761         * followed by zero or more calls to {@link #addRange} followed by
762         * a call to {@link #finishUpdate}.
763         */
764        protected void startUpdate() {
765            mConfigList.clear();
766        }
767
768        /**
769         * Called after {@link #startUpdate} to indicate a range of enabled
770         * values.
771         * @param startId the first id included in the range
772         * @param endId the last id included in the range
773         */
774        protected void addRange(int startId, int endId, boolean selected) {
775            mConfigList.add(new SmsBroadcastConfigInfo(startId, endId,
776                        SMS_CB_CODE_SCHEME_MIN, SMS_CB_CODE_SCHEME_MAX, selected));
777        }
778
779        /**
780         * Called to indicate the end of a range update started by the
781         * previous call to {@link #startUpdate}.
782         * @return true if successful, false otherwise
783         */
784        protected boolean finishUpdate() {
785            if (mConfigList.isEmpty()) {
786                return true;
787            } else {
788                SmsBroadcastConfigInfo[] configs =
789                        mConfigList.toArray(new SmsBroadcastConfigInfo[mConfigList.size()]);
790                return setCellBroadcastConfig(configs);
791            }
792        }
793    }
794
795    class CdmaBroadcastRangeManager extends IntRangeManager {
796        private ArrayList<CdmaSmsBroadcastConfigInfo> mConfigList =
797                new ArrayList<CdmaSmsBroadcastConfigInfo>();
798
799        /**
800         * Called when the list of enabled ranges has changed. This will be
801         * followed by zero or more calls to {@link #addRange} followed by a
802         * call to {@link #finishUpdate}.
803         */
804        protected void startUpdate() {
805            mConfigList.clear();
806        }
807
808        /**
809         * Called after {@link #startUpdate} to indicate a range of enabled
810         * values.
811         * @param startId the first id included in the range
812         * @param endId the last id included in the range
813         */
814        protected void addRange(int startId, int endId, boolean selected) {
815            mConfigList.add(new CdmaSmsBroadcastConfigInfo(startId, endId,
816                    1, selected));
817        }
818
819        /**
820         * Called to indicate the end of a range update started by the previous
821         * call to {@link #startUpdate}.
822         * @return true if successful, false otherwise
823         */
824        protected boolean finishUpdate() {
825            if (mConfigList.isEmpty()) {
826                return true;
827            } else {
828                CdmaSmsBroadcastConfigInfo[] configs =
829                        mConfigList.toArray(new CdmaSmsBroadcastConfigInfo[mConfigList.size()]);
830                return setCdmaBroadcastConfig(configs);
831            }
832        }
833    }
834
835    private boolean setCellBroadcastConfig(SmsBroadcastConfigInfo[] configs) {
836        if (DBG)
837            log("Calling setGsmBroadcastConfig with " + configs.length + " configurations");
838
839        synchronized (mLock) {
840            Message response = mHandler.obtainMessage(EVENT_SET_BROADCAST_CONFIG_DONE);
841
842            mSuccess = false;
843            mPhone.mCi.setGsmBroadcastConfig(configs, response);
844
845            try {
846                mLock.wait();
847            } catch (InterruptedException e) {
848                log("interrupted while trying to set cell broadcast config");
849            }
850        }
851
852        return mSuccess;
853    }
854
855    private boolean setCellBroadcastActivation(boolean activate) {
856        if (DBG)
857            log("Calling setCellBroadcastActivation(" + activate + ')');
858
859        synchronized (mLock) {
860            Message response = mHandler.obtainMessage(EVENT_SET_BROADCAST_ACTIVATION_DONE);
861
862            mSuccess = false;
863            mPhone.mCi.setGsmBroadcastActivation(activate, response);
864
865            try {
866                mLock.wait();
867            } catch (InterruptedException e) {
868                log("interrupted while trying to set cell broadcast activation");
869            }
870        }
871
872        return mSuccess;
873    }
874
875    private boolean setCdmaBroadcastConfig(CdmaSmsBroadcastConfigInfo[] configs) {
876        if (DBG)
877            log("Calling setCdmaBroadcastConfig with " + configs.length + " configurations");
878
879        synchronized (mLock) {
880            Message response = mHandler.obtainMessage(EVENT_SET_BROADCAST_CONFIG_DONE);
881
882            mSuccess = false;
883            mPhone.mCi.setCdmaBroadcastConfig(configs, response);
884
885            try {
886                mLock.wait();
887            } catch (InterruptedException e) {
888                log("interrupted while trying to set cdma broadcast config");
889            }
890        }
891
892        return mSuccess;
893    }
894
895    private boolean setCdmaBroadcastActivation(boolean activate) {
896        if (DBG)
897            log("Calling setCdmaBroadcastActivation(" + activate + ")");
898
899        synchronized (mLock) {
900            Message response = mHandler.obtainMessage(EVENT_SET_BROADCAST_ACTIVATION_DONE);
901
902            mSuccess = false;
903            mPhone.mCi.setCdmaBroadcastActivation(activate, response);
904
905            try {
906                mLock.wait();
907            } catch (InterruptedException e) {
908                log("interrupted while trying to set cdma broadcast activation");
909            }
910        }
911
912        return mSuccess;
913    }
914
915    protected void log(String msg) {
916        Log.d(LOG_TAG, "[IccSmsInterfaceManager] " + msg);
917    }
918
919    public boolean isImsSmsSupported() {
920        return mDispatcher.isIms();
921    }
922
923    public String getImsSmsFormat() {
924        return mDispatcher.getImsSmsFormat();
925    }
926
927    public void sendStoredText(String callingPkg, Uri messageUri, String scAddress,
928            PendingIntent sentIntent, PendingIntent deliveryIntent) {
929        mPhone.getContext().enforceCallingPermission(Manifest.permission.SEND_SMS,
930                "Sending SMS message");
931        if (Rlog.isLoggable("SMS", Log.VERBOSE)) {
932            log("sendStoredText: scAddr=" + scAddress + " messageUri=" + messageUri
933                    + " sentIntent=" + sentIntent + " deliveryIntent=" + deliveryIntent);
934        }
935        if (mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), callingPkg)
936                != AppOpsManager.MODE_ALLOWED) {
937            return;
938        }
939        final ContentResolver resolver = mPhone.getContext().getContentResolver();
940        if (!isFailedOrDraft(resolver, messageUri)) {
941            Log.e(LOG_TAG, "[IccSmsInterfaceManager]sendStoredText: not FAILED or DRAFT message");
942            returnUnspecifiedFailure(sentIntent);
943            return;
944        }
945        final String[] textAndAddress = loadTextAndAddress(resolver, messageUri);
946        if (textAndAddress == null) {
947            Log.e(LOG_TAG, "[IccSmsInterfaceManager]sendStoredText: can not load text");
948            returnUnspecifiedFailure(sentIntent);
949            return;
950        }
951        textAndAddress[1] = filterDestAddress(textAndAddress[1]);
952        mDispatcher.sendText(textAndAddress[1], scAddress, textAndAddress[0],
953                sentIntent, deliveryIntent, messageUri, callingPkg,
954                true /* persistMessageForNonDefaultSmsApp */);
955    }
956
957    public void sendStoredMultipartText(String callingPkg, Uri messageUri, String scAddress,
958            List<PendingIntent> sentIntents, List<PendingIntent> deliveryIntents) {
959        mPhone.getContext().enforceCallingPermission(Manifest.permission.SEND_SMS,
960                "Sending SMS message");
961        if (mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), callingPkg)
962                != AppOpsManager.MODE_ALLOWED) {
963            return;
964        }
965        final ContentResolver resolver = mPhone.getContext().getContentResolver();
966        if (!isFailedOrDraft(resolver, messageUri)) {
967            Log.e(LOG_TAG, "[IccSmsInterfaceManager]sendStoredMultipartText: "
968                    + "not FAILED or DRAFT message");
969            returnUnspecifiedFailure(sentIntents);
970            return;
971        }
972        final String[] textAndAddress = loadTextAndAddress(resolver, messageUri);
973        if (textAndAddress == null) {
974            Log.e(LOG_TAG, "[IccSmsInterfaceManager]sendStoredMultipartText: can not load text");
975            returnUnspecifiedFailure(sentIntents);
976            return;
977        }
978        final ArrayList<String> parts = SmsManager.getDefault().divideMessage(textAndAddress[0]);
979        if (parts == null || parts.size() < 1) {
980            Log.e(LOG_TAG, "[IccSmsInterfaceManager]sendStoredMultipartText: can not divide text");
981            returnUnspecifiedFailure(sentIntents);
982            return;
983        }
984
985        textAndAddress[1] = filterDestAddress(textAndAddress[1]);
986
987        if (parts.size() > 1 && parts.size() < 10 && !SmsMessage.hasEmsSupport()) {
988            for (int i = 0; i < parts.size(); i++) {
989                // If EMS is not supported, we have to break down EMS into single segment SMS
990                // and add page info " x/y".
991                String singlePart = parts.get(i);
992                if (SmsMessage.shouldAppendPageNumberAsPrefix()) {
993                    singlePart = String.valueOf(i + 1) + '/' + parts.size() + ' ' + singlePart;
994                } else {
995                    singlePart = singlePart.concat(' ' + String.valueOf(i + 1) + '/' + parts.size());
996                }
997
998                PendingIntent singleSentIntent = null;
999                if (sentIntents != null && sentIntents.size() > i) {
1000                    singleSentIntent = sentIntents.get(i);
1001                }
1002
1003                PendingIntent singleDeliveryIntent = null;
1004                if (deliveryIntents != null && deliveryIntents.size() > i) {
1005                    singleDeliveryIntent = deliveryIntents.get(i);
1006                }
1007
1008                mDispatcher.sendText(textAndAddress[1], scAddress, singlePart,
1009                        singleSentIntent, singleDeliveryIntent, messageUri, callingPkg,
1010                        true  /* persistMessageForNonDefaultSmsApp */);
1011            }
1012            return;
1013        }
1014
1015        mDispatcher.sendMultipartText(
1016                textAndAddress[1], // destAddress
1017                scAddress,
1018                parts,
1019                (ArrayList<PendingIntent>) sentIntents,
1020                (ArrayList<PendingIntent>) deliveryIntents,
1021                messageUri,
1022                callingPkg,
1023                true  /* persistMessageForNonDefaultSmsApp */);
1024    }
1025
1026    private boolean isFailedOrDraft(ContentResolver resolver, Uri messageUri) {
1027        // Clear the calling identity and query the database using the phone user id
1028        // Otherwise the AppOps check in TelephonyProvider would complain about mismatch
1029        // between the calling uid and the package uid
1030        final long identity = Binder.clearCallingIdentity();
1031        Cursor cursor = null;
1032        try {
1033            cursor = resolver.query(
1034                    messageUri,
1035                    new String[]{ Telephony.Sms.TYPE },
1036                    null/*selection*/,
1037                    null/*selectionArgs*/,
1038                    null/*sortOrder*/);
1039            if (cursor != null && cursor.moveToFirst()) {
1040                final int type = cursor.getInt(0);
1041                return type == Telephony.Sms.MESSAGE_TYPE_DRAFT
1042                        || type == Telephony.Sms.MESSAGE_TYPE_FAILED;
1043            }
1044        } catch (SQLiteException e) {
1045            Log.e(LOG_TAG, "[IccSmsInterfaceManager]isFailedOrDraft: query message type failed", e);
1046        } finally {
1047            if (cursor != null) {
1048                cursor.close();
1049            }
1050            Binder.restoreCallingIdentity(identity);
1051        }
1052        return false;
1053    }
1054
1055    // Return an array including both the SMS text (0) and address (1)
1056    private String[] loadTextAndAddress(ContentResolver resolver, Uri messageUri) {
1057        // Clear the calling identity and query the database using the phone user id
1058        // Otherwise the AppOps check in TelephonyProvider would complain about mismatch
1059        // between the calling uid and the package uid
1060        final long identity = Binder.clearCallingIdentity();
1061        Cursor cursor = null;
1062        try {
1063            cursor = resolver.query(
1064                    messageUri,
1065                    new String[]{
1066                            Telephony.Sms.BODY,
1067                            Telephony.Sms.ADDRESS
1068                    },
1069                    null/*selection*/,
1070                    null/*selectionArgs*/,
1071                    null/*sortOrder*/);
1072            if (cursor != null && cursor.moveToFirst()) {
1073                return new String[]{ cursor.getString(0), cursor.getString(1) };
1074            }
1075        } catch (SQLiteException e) {
1076            Log.e(LOG_TAG, "[IccSmsInterfaceManager]loadText: query message text failed", e);
1077        } finally {
1078            if (cursor != null) {
1079                cursor.close();
1080            }
1081            Binder.restoreCallingIdentity(identity);
1082        }
1083        return null;
1084    }
1085
1086    private void returnUnspecifiedFailure(PendingIntent pi) {
1087        if (pi != null) {
1088            try {
1089                pi.send(SmsManager.RESULT_ERROR_GENERIC_FAILURE);
1090            } catch (PendingIntent.CanceledException e) {
1091                // ignore
1092            }
1093        }
1094    }
1095
1096    private void returnUnspecifiedFailure(List<PendingIntent> pis) {
1097        if (pis == null) {
1098            return;
1099        }
1100        for (PendingIntent pi : pis) {
1101            returnUnspecifiedFailure(pi);
1102        }
1103    }
1104
1105    private void enforceCarrierPrivilege() {
1106        UiccController controller = UiccController.getInstance();
1107        if (controller == null || controller.getUiccCard(mPhone.getPhoneId()) == null) {
1108            throw new SecurityException("No Carrier Privilege: No UICC");
1109        }
1110        if (controller.getUiccCard(mPhone.getPhoneId()).getCarrierPrivilegeStatusForCurrentTransaction(
1111                mContext.getPackageManager()) !=
1112                    TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
1113            throw new SecurityException("No Carrier Privilege.");
1114        }
1115    }
1116
1117    private String filterDestAddress(String destAddr) {
1118        String result  = null;
1119        result = SmsNumberUtils.filterDestAddr(mPhone, destAddr);
1120        return result != null ? result : destAddr;
1121    }
1122
1123}
1124