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