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