IccSmsInterfaceManager.java revision a8467dd0c524787104b1ccdddc5e8af10ba729ed
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.Context;
23import android.os.AsyncResult;
24import android.os.Binder;
25import android.os.Handler;
26import android.os.Message;
27import android.os.ServiceManager;
28import android.telephony.Rlog;
29import android.util.Log;
30
31import com.android.internal.telephony.ISms;
32import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo;
33import com.android.internal.telephony.cdma.CdmaSmsBroadcastConfigInfo;
34import com.android.internal.telephony.uicc.IccConstants;
35import com.android.internal.telephony.uicc.IccFileHandler;
36import com.android.internal.util.HexDump;
37
38import java.util.ArrayList;
39import java.util.Arrays;
40import java.util.List;
41
42import static android.telephony.SmsManager.STATUS_ON_ICC_FREE;
43import static android.telephony.SmsManager.STATUS_ON_ICC_READ;
44import static android.telephony.SmsManager.STATUS_ON_ICC_UNREAD;
45
46/**
47 * IccSmsInterfaceManager to provide an inter-process communication to
48 * access Sms in Icc.
49 */
50public class IccSmsInterfaceManager {
51    static final String LOG_TAG = "IccSmsInterfaceManager";
52    static final boolean DBG = true;
53
54    protected final Object mLock = new Object();
55    protected boolean mSuccess;
56    private List<SmsRawData> mSms;
57
58    private CellBroadcastRangeManager mCellBroadcastRangeManager =
59            new CellBroadcastRangeManager();
60    private CdmaBroadcastRangeManager mCdmaBroadcastRangeManager =
61            new CdmaBroadcastRangeManager();
62
63    private static final int EVENT_LOAD_DONE = 1;
64    private static final int EVENT_UPDATE_DONE = 2;
65    protected static final int EVENT_SET_BROADCAST_ACTIVATION_DONE = 3;
66    protected static final int EVENT_SET_BROADCAST_CONFIG_DONE = 4;
67    private static final int SMS_CB_CODE_SCHEME_MIN = 0;
68    private static final int SMS_CB_CODE_SCHEME_MAX = 255;
69
70    protected PhoneBase mPhone;
71    final protected Context mContext;
72    final protected AppOpsManager mAppOps;
73    protected SMSDispatcher mDispatcher;
74
75    protected Handler mHandler = new Handler() {
76        @Override
77        public void handleMessage(Message msg) {
78            AsyncResult ar;
79
80            switch (msg.what) {
81                case EVENT_UPDATE_DONE:
82                    ar = (AsyncResult) msg.obj;
83                    synchronized (mLock) {
84                        mSuccess = (ar.exception == null);
85                        mLock.notifyAll();
86                    }
87                    break;
88                case EVENT_LOAD_DONE:
89                    ar = (AsyncResult)msg.obj;
90                    synchronized (mLock) {
91                        if (ar.exception == null) {
92                            mSms = buildValidRawData((ArrayList<byte[]>) ar.result);
93                            //Mark SMS as read after importing it from card.
94                            markMessagesAsRead((ArrayList<byte[]>) ar.result);
95                        } else {
96                            if (Rlog.isLoggable("SMS", Log.DEBUG)) {
97                                log("Cannot load Sms records");
98                            }
99                            if (mSms != null)
100                                mSms.clear();
101                        }
102                        mLock.notifyAll();
103                    }
104                    break;
105                case EVENT_SET_BROADCAST_ACTIVATION_DONE:
106                case EVENT_SET_BROADCAST_CONFIG_DONE:
107                    ar = (AsyncResult) msg.obj;
108                    synchronized (mLock) {
109                        mSuccess = (ar.exception == null);
110                        mLock.notifyAll();
111                    }
112                    break;
113            }
114        }
115    };
116
117    protected IccSmsInterfaceManager(PhoneBase phone) {
118        mPhone = phone;
119        mContext = phone.getContext();
120        mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
121        mDispatcher = new ImsSMSDispatcher(phone,
122                phone.mSmsStorageMonitor, phone.mSmsUsageMonitor);
123    }
124
125    protected void markMessagesAsRead(ArrayList<byte[]> messages) {
126        if (messages == null) {
127            return;
128        }
129
130        //IccFileHandler can be null, if icc card is absent.
131        IccFileHandler fh = mPhone.getIccFileHandler();
132        if (fh == null) {
133            //shouldn't really happen, as messages are marked as read, only
134            //after importing it from icc.
135            if (Rlog.isLoggable("SMS", Log.DEBUG)) {
136                log("markMessagesAsRead - aborting, no icc card present.");
137            }
138            return;
139        }
140
141        int count = messages.size();
142
143        for (int i = 0; i < count; i++) {
144             byte[] ba = messages.get(i);
145             if (ba[0] == STATUS_ON_ICC_UNREAD) {
146                 int n = ba.length;
147                 byte[] nba = new byte[n - 1];
148                 System.arraycopy(ba, 1, nba, 0, n - 1);
149                 byte[] record = makeSmsRecordData(STATUS_ON_ICC_READ, nba);
150                 fh.updateEFLinearFixed(IccConstants.EF_SMS, i + 1, record, null, null);
151                 if (Rlog.isLoggable("SMS", Log.DEBUG)) {
152                     log("SMS " + (i + 1) + " marked as read");
153                 }
154             }
155        }
156    }
157
158    protected void updatePhoneObject(PhoneBase phone) {
159        mPhone = phone;
160        mDispatcher.updatePhoneObject(phone);
161    }
162
163    protected void enforceReceiveAndSend(String message) {
164        mContext.enforceCallingPermission(
165                Manifest.permission.RECEIVE_SMS, message);
166        mContext.enforceCallingPermission(
167                Manifest.permission.SEND_SMS, message);
168    }
169
170    /**
171     * Update the specified message on the Icc.
172     *
173     * @param index record index of message to update
174     * @param status new message status (STATUS_ON_ICC_READ,
175     *                  STATUS_ON_ICC_UNREAD, STATUS_ON_ICC_SENT,
176     *                  STATUS_ON_ICC_UNSENT, STATUS_ON_ICC_FREE)
177     * @param pdu the raw PDU to store
178     * @return success or not
179     *
180     */
181
182    public boolean
183    updateMessageOnIccEf(String callingPackage, int index, int status, byte[] pdu) {
184        if (DBG) log("updateMessageOnIccEf: index=" + index +
185                " status=" + status + " ==> " +
186                "("+ Arrays.toString(pdu) + ")");
187        enforceReceiveAndSend("Updating message on Icc");
188        if (mAppOps.noteOp(AppOpsManager.OP_WRITE_ICC_SMS, Binder.getCallingUid(),
189                callingPackage) != AppOpsManager.MODE_ALLOWED) {
190            return false;
191        }
192        synchronized(mLock) {
193            mSuccess = false;
194            Message response = mHandler.obtainMessage(EVENT_UPDATE_DONE);
195
196            if (status == STATUS_ON_ICC_FREE) {
197                // RIL_REQUEST_DELETE_SMS_ON_SIM vs RIL_REQUEST_CDMA_DELETE_SMS_ON_RUIM
198                // Special case FREE: call deleteSmsOnSim/Ruim instead of
199                // manipulating the record
200                // Will eventually fail if icc card is not present.
201                if (PhoneConstants.PHONE_TYPE_GSM == mPhone.getPhoneType()) {
202                    mPhone.mCi.deleteSmsOnSim(index, response);
203                } else {
204                    mPhone.mCi.deleteSmsOnRuim(index, response);
205                }
206            } else {
207                //IccFilehandler can be null if ICC card is not present.
208                IccFileHandler fh = mPhone.getIccFileHandler();
209                if (fh == null) {
210                    response.recycle();
211                    return mSuccess; /* is false */
212                }
213                byte[] record = makeSmsRecordData(status, pdu);
214                fh.updateEFLinearFixed(
215                        IccConstants.EF_SMS,
216                        index, record, null, response);
217            }
218            try {
219                mLock.wait();
220            } catch (InterruptedException e) {
221                log("interrupted while trying to update by index");
222            }
223        }
224        return mSuccess;
225    }
226
227    /**
228     * Copy a raw SMS PDU to the Icc.
229     *
230     * @param pdu the raw PDU to store
231     * @param status message status (STATUS_ON_ICC_READ, STATUS_ON_ICC_UNREAD,
232     *               STATUS_ON_ICC_SENT, STATUS_ON_ICC_UNSENT)
233     * @return success or not
234     *
235     */
236    public boolean copyMessageToIccEf(String callingPackage, int status, byte[] pdu, byte[] smsc) {
237        //NOTE smsc not used in RUIM
238        if (DBG) log("copyMessageToIccEf: status=" + status + " ==> " +
239                "pdu=("+ Arrays.toString(pdu) +
240                "), smsc=(" + Arrays.toString(smsc) +")");
241        enforceReceiveAndSend("Copying message to Icc");
242        if (mAppOps.noteOp(AppOpsManager.OP_WRITE_ICC_SMS, Binder.getCallingUid(),
243                callingPackage) != AppOpsManager.MODE_ALLOWED) {
244            return false;
245        }
246        synchronized(mLock) {
247            mSuccess = false;
248            Message response = mHandler.obtainMessage(EVENT_UPDATE_DONE);
249
250            //RIL_REQUEST_WRITE_SMS_TO_SIM vs RIL_REQUEST_CDMA_WRITE_SMS_TO_RUIM
251            if (PhoneConstants.PHONE_TYPE_GSM == mPhone.getPhoneType()) {
252                mPhone.mCi.writeSmsToSim(status, IccUtils.bytesToHexString(smsc),
253                        IccUtils.bytesToHexString(pdu), response);
254            } else {
255                mPhone.mCi.writeSmsToRuim(status, IccUtils.bytesToHexString(pdu),
256                        response);
257            }
258
259            try {
260                mLock.wait();
261            } catch (InterruptedException e) {
262                log("interrupted while trying to update by index");
263            }
264        }
265        return mSuccess;
266    }
267
268    /**
269     * Retrieves all messages currently stored on Icc.
270     *
271     * @return list of SmsRawData of all sms on Icc
272     */
273
274    public List<SmsRawData> getAllMessagesFromIccEf(String callingPackage) {
275        if (DBG) log("getAllMessagesFromEF");
276
277        mContext.enforceCallingOrSelfPermission(
278                Manifest.permission.RECEIVE_SMS,
279                "Reading messages from Icc");
280        if (mAppOps.noteOp(AppOpsManager.OP_READ_ICC_SMS, Binder.getCallingUid(),
281                callingPackage) != AppOpsManager.MODE_ALLOWED) {
282            return new ArrayList<SmsRawData>();
283        }
284        synchronized(mLock) {
285
286            IccFileHandler fh = mPhone.getIccFileHandler();
287            if (fh == null) {
288                Rlog.e(LOG_TAG, "Cannot load Sms records. No icc card?");
289                if (mSms != null) {
290                    mSms.clear();
291                    return mSms;
292                }
293            }
294
295            Message response = mHandler.obtainMessage(EVENT_LOAD_DONE);
296            fh.loadEFLinearFixedAll(IccConstants.EF_SMS, response);
297
298            try {
299                mLock.wait();
300            } catch (InterruptedException e) {
301                log("interrupted while trying to load from the Icc");
302            }
303        }
304        return mSms;
305    }
306
307    /**
308     * Send a data based SMS to a specific application port.
309     *
310     * @param destAddr the address to send the message to
311     * @param scAddr is the service center address or null to use
312     *  the current default SMSC
313     * @param destPort the port to deliver the message to
314     * @param data the body of the message to send
315     * @param sentIntent if not NULL this <code>PendingIntent</code> is
316     *  broadcast when the message is successfully sent, or failed.
317     *  The result code will be <code>Activity.RESULT_OK<code> for success,
318     *  or one of these errors:<br>
319     *  <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
320     *  <code>RESULT_ERROR_RADIO_OFF</code><br>
321     *  <code>RESULT_ERROR_NULL_PDU</code><br>
322     *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
323     *  the extra "errorCode" containing a radio technology specific value,
324     *  generally only useful for troubleshooting.<br>
325     *  The per-application based SMS control checks sentIntent. If sentIntent
326     *  is NULL the caller will be checked against all unknown applications,
327     *  which cause smaller number of SMS to be sent in checking period.
328     * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
329     *  broadcast when the message is delivered to the recipient.  The
330     *  raw pdu of the status report is in the extended data ("pdu").
331     */
332
333    public void sendData(String callingPackage, String destAddr, String scAddr, int destPort,
334            byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
335        mPhone.getContext().enforceCallingPermission(
336                Manifest.permission.SEND_SMS,
337                "Sending SMS message");
338        if (Rlog.isLoggable("SMS", Log.VERBOSE)) {
339            log("sendData: destAddr=" + destAddr + " scAddr=" + scAddr + " destPort=" +
340                destPort + " data='"+ HexDump.toHexString(data)  + "' sentIntent=" +
341                sentIntent + " deliveryIntent=" + deliveryIntent);
342        }
343        if (mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(),
344                callingPackage) != AppOpsManager.MODE_ALLOWED) {
345            return;
346        }
347        mDispatcher.sendData(destAddr, scAddr, destPort, data, sentIntent, deliveryIntent);
348    }
349
350    /**
351     * Send a text based SMS.
352     *
353     * @param destAddr the address to send the message to
354     * @param scAddr is the service center address or null to use
355     *  the current default SMSC
356     * @param text the body of the message to send
357     * @param sentIntent if not NULL this <code>PendingIntent</code> is
358     *  broadcast when the message is successfully sent, or failed.
359     *  The result code will be <code>Activity.RESULT_OK<code> for success,
360     *  or one of these errors:<br>
361     *  <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
362     *  <code>RESULT_ERROR_RADIO_OFF</code><br>
363     *  <code>RESULT_ERROR_NULL_PDU</code><br>
364     *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
365     *  the extra "errorCode" containing a radio technology specific value,
366     *  generally only useful for troubleshooting.<br>
367     *  The per-application based SMS control checks sentIntent. If sentIntent
368     *  is NULL the caller will be checked against all unknown applications,
369     *  which cause smaller number of SMS to be sent in checking period.
370     * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
371     *  broadcast when the message is delivered to the recipient.  The
372     *  raw pdu of the status report is in the extended data ("pdu").
373     */
374
375    public void sendText(String callingPackage, String destAddr, String scAddr,
376            String text, PendingIntent sentIntent, PendingIntent deliveryIntent) {
377        mPhone.getContext().enforceCallingPermission(
378                Manifest.permission.SEND_SMS,
379                "Sending SMS message");
380        if (Rlog.isLoggable("SMS", Log.VERBOSE)) {
381            log("sendText: destAddr=" + destAddr + " scAddr=" + scAddr +
382                " text='"+ text + "' sentIntent=" +
383                sentIntent + " deliveryIntent=" + deliveryIntent);
384        }
385        if (mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(),
386                callingPackage) != AppOpsManager.MODE_ALLOWED) {
387            return;
388        }
389        mDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent);
390    }
391
392    /**
393     * Send a multi-part text based SMS.
394     *
395     * @param destAddr the address to send the message to
396     * @param scAddr is the service center address or null to use
397     *   the current default SMSC
398     * @param parts an <code>ArrayList</code> of strings that, in order,
399     *   comprise the original message
400     * @param sentIntents if not null, an <code>ArrayList</code> of
401     *   <code>PendingIntent</code>s (one for each message part) that is
402     *   broadcast when the corresponding message part has been sent.
403     *   The result code will be <code>Activity.RESULT_OK<code> for success,
404     *   or one of these errors:
405     *   <code>RESULT_ERROR_GENERIC_FAILURE</code>
406     *   <code>RESULT_ERROR_RADIO_OFF</code>
407     *   <code>RESULT_ERROR_NULL_PDU</code>.
408     *  The per-application based SMS control checks sentIntent. If sentIntent
409     *  is NULL the caller will be checked against all unknown applications,
410     *  which cause smaller number of SMS to be sent in checking period.
411     * @param deliveryIntents if not null, an <code>ArrayList</code> of
412     *   <code>PendingIntent</code>s (one for each message part) that is
413     *   broadcast when the corresponding message part has been delivered
414     *   to the recipient.  The raw pdu of the status report is in the
415     *   extended data ("pdu").
416     */
417
418    public void sendMultipartText(String callingPackage, String destAddr, String scAddr,
419            List<String> parts, List<PendingIntent> sentIntents,
420            List<PendingIntent> deliveryIntents) {
421        mPhone.getContext().enforceCallingPermission(
422                Manifest.permission.SEND_SMS,
423                "Sending SMS message");
424        if (Rlog.isLoggable("SMS", Log.VERBOSE)) {
425            int i = 0;
426            for (String part : parts) {
427                log("sendMultipartText: destAddr=" + destAddr + ", srAddr=" + scAddr +
428                        ", part[" + (i++) + "]=" + part);
429            }
430        }
431        if (mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(),
432                callingPackage) != AppOpsManager.MODE_ALLOWED) {
433            return;
434        }
435        mDispatcher.sendMultipartText(destAddr, scAddr, (ArrayList<String>) parts,
436                (ArrayList<PendingIntent>) sentIntents, (ArrayList<PendingIntent>) deliveryIntents);
437    }
438
439
440    public int getPremiumSmsPermission(String packageName) {
441        return mDispatcher.getPremiumSmsPermission(packageName);
442    }
443
444
445    public void setPremiumSmsPermission(String packageName, int permission) {
446        mDispatcher.setPremiumSmsPermission(packageName, permission);
447    }
448
449    /**
450     * create SmsRawData lists from all sms record byte[]
451     * Use null to indicate "free" record
452     *
453     * @param messages List of message records from EF_SMS.
454     * @return SmsRawData list of all in-used records
455     */
456    protected ArrayList<SmsRawData> buildValidRawData(ArrayList<byte[]> messages) {
457        int count = messages.size();
458        ArrayList<SmsRawData> ret;
459
460        ret = new ArrayList<SmsRawData>(count);
461
462        for (int i = 0; i < count; i++) {
463            byte[] ba = messages.get(i);
464            if (ba[0] == STATUS_ON_ICC_FREE) {
465                ret.add(null);
466            } else {
467                ret.add(new SmsRawData(messages.get(i)));
468            }
469        }
470
471        return ret;
472    }
473
474    /**
475     * Generates an EF_SMS record from status and raw PDU.
476     *
477     * @param status Message status.  See TS 51.011 10.5.3.
478     * @param pdu Raw message PDU.
479     * @return byte array for the record.
480     */
481    protected byte[] makeSmsRecordData(int status, byte[] pdu) {
482        byte[] data;
483        if (PhoneConstants.PHONE_TYPE_GSM == mPhone.getPhoneType()) {
484            data = new byte[IccConstants.SMS_RECORD_LENGTH];
485        } else {
486            data = new byte[IccConstants.CDMA_SMS_RECORD_LENGTH];
487        }
488
489        // Status bits for this record.  See TS 51.011 10.5.3
490        data[0] = (byte)(status & 7);
491
492        System.arraycopy(pdu, 0, data, 1, pdu.length);
493
494        // Pad out with 0xFF's.
495        for (int j = pdu.length+1; j < data.length; j++) {
496            data[j] = -1;
497        }
498
499        return data;
500    }
501
502    public boolean enableCellBroadcast(int messageIdentifier) {
503        return enableCellBroadcastRange(messageIdentifier, messageIdentifier);
504    }
505
506    public boolean disableCellBroadcast(int messageIdentifier) {
507        return disableCellBroadcastRange(messageIdentifier, messageIdentifier);
508    }
509
510    public boolean enableCellBroadcastRange(int startMessageId, int endMessageId) {
511        if (PhoneConstants.PHONE_TYPE_GSM == mPhone.getPhoneType()) {
512            return enableGsmBroadcastRange(startMessageId, endMessageId);
513        } else {
514            return enableCdmaBroadcastRange(startMessageId, endMessageId);
515        }
516    }
517
518    public boolean disableCellBroadcastRange(int startMessageId, int endMessageId) {
519        if (PhoneConstants.PHONE_TYPE_GSM == mPhone.getPhoneType()) {
520            return disableGsmBroadcastRange(startMessageId, endMessageId);
521        } else {
522            return disableCdmaBroadcastRange(startMessageId, endMessageId);
523        }
524    }
525
526    synchronized public boolean enableGsmBroadcastRange(int startMessageId, int endMessageId) {
527        if (DBG) log("enableGsmBroadcastRange");
528
529        Context context = mPhone.getContext();
530
531        context.enforceCallingPermission(
532                "android.permission.RECEIVE_SMS",
533                "Enabling cell broadcast SMS");
534
535        String client = context.getPackageManager().getNameForUid(
536                Binder.getCallingUid());
537
538        if (!mCellBroadcastRangeManager.enableRange(startMessageId, endMessageId, client)) {
539            log("Failed to add cell broadcast subscription for MID range " + startMessageId
540                    + " to " + endMessageId + " from client " + client);
541            return false;
542        }
543
544        if (DBG)
545            log("Added cell broadcast subscription for MID range " + startMessageId
546                    + " to " + endMessageId + " from client " + client);
547
548        setCellBroadcastActivation(!mCellBroadcastRangeManager.isEmpty());
549
550        return true;
551    }
552
553    synchronized public boolean disableGsmBroadcastRange(int startMessageId, int endMessageId) {
554        if (DBG) log("disableGsmBroadcastRange");
555
556        Context context = mPhone.getContext();
557
558        context.enforceCallingPermission(
559                "android.permission.RECEIVE_SMS",
560                "Disabling cell broadcast SMS");
561
562        String client = context.getPackageManager().getNameForUid(
563                Binder.getCallingUid());
564
565        if (!mCellBroadcastRangeManager.disableRange(startMessageId, endMessageId, client)) {
566            log("Failed to remove cell broadcast subscription for MID range " + startMessageId
567                    + " to " + endMessageId + " from client " + client);
568            return false;
569        }
570
571        if (DBG)
572            log("Removed cell broadcast subscription for MID range " + startMessageId
573                    + " to " + endMessageId + " from client " + client);
574
575        setCellBroadcastActivation(!mCellBroadcastRangeManager.isEmpty());
576
577        return true;
578    }
579
580    synchronized public boolean enableCdmaBroadcastRange(int startMessageId, int endMessageId) {
581        if (DBG) log("enableCdmaBroadcastRange");
582
583        Context context = mPhone.getContext();
584
585        context.enforceCallingPermission(
586                "android.permission.RECEIVE_SMS",
587                "Enabling cdma broadcast SMS");
588
589        String client = context.getPackageManager().getNameForUid(
590                Binder.getCallingUid());
591
592        if (!mCdmaBroadcastRangeManager.enableRange(startMessageId, endMessageId, client)) {
593            log("Failed to add cdma broadcast subscription for MID range " + startMessageId
594                    + " to " + endMessageId + " from client " + client);
595            return false;
596        }
597
598        if (DBG)
599            log("Added cdma broadcast subscription for MID range " + startMessageId
600                    + " to " + endMessageId + " from client " + client);
601
602        setCdmaBroadcastActivation(!mCdmaBroadcastRangeManager.isEmpty());
603
604        return true;
605    }
606
607    synchronized public boolean disableCdmaBroadcastRange(int startMessageId, int endMessageId) {
608        if (DBG) log("disableCdmaBroadcastRange");
609
610        Context context = mPhone.getContext();
611
612        context.enforceCallingPermission(
613                "android.permission.RECEIVE_SMS",
614                "Disabling cell broadcast SMS");
615
616        String client = context.getPackageManager().getNameForUid(
617                Binder.getCallingUid());
618
619        if (!mCdmaBroadcastRangeManager.disableRange(startMessageId, endMessageId, client)) {
620            log("Failed to remove cdma broadcast subscription for MID range " + startMessageId
621                    + " to " + endMessageId + " from client " + client);
622            return false;
623        }
624
625        if (DBG)
626            log("Removed cdma broadcast subscription for MID range " + startMessageId
627                    + " to " + endMessageId + " from client " + client);
628
629        setCdmaBroadcastActivation(!mCdmaBroadcastRangeManager.isEmpty());
630
631        return true;
632    }
633
634    class CellBroadcastRangeManager extends IntRangeManager {
635        private ArrayList<SmsBroadcastConfigInfo> mConfigList =
636                new ArrayList<SmsBroadcastConfigInfo>();
637
638        /**
639         * Called when the list of enabled ranges has changed. This will be
640         * followed by zero or more calls to {@link #addRange} followed by
641         * a call to {@link #finishUpdate}.
642         */
643        protected void startUpdate() {
644            mConfigList.clear();
645        }
646
647        /**
648         * Called after {@link #startUpdate} to indicate a range of enabled
649         * values.
650         * @param startId the first id included in the range
651         * @param endId the last id included in the range
652         */
653        protected void addRange(int startId, int endId, boolean selected) {
654            mConfigList.add(new SmsBroadcastConfigInfo(startId, endId,
655                        SMS_CB_CODE_SCHEME_MIN, SMS_CB_CODE_SCHEME_MAX, selected));
656        }
657
658        /**
659         * Called to indicate the end of a range update started by the
660         * previous call to {@link #startUpdate}.
661         * @return true if successful, false otherwise
662         */
663        protected boolean finishUpdate() {
664            if (mConfigList.isEmpty()) {
665                return true;
666            } else {
667                SmsBroadcastConfigInfo[] configs =
668                        mConfigList.toArray(new SmsBroadcastConfigInfo[mConfigList.size()]);
669                return setCellBroadcastConfig(configs);
670            }
671        }
672    }
673
674    class CdmaBroadcastRangeManager extends IntRangeManager {
675        private ArrayList<CdmaSmsBroadcastConfigInfo> mConfigList =
676                new ArrayList<CdmaSmsBroadcastConfigInfo>();
677
678        /**
679         * Called when the list of enabled ranges has changed. This will be
680         * followed by zero or more calls to {@link #addRange} followed by a
681         * call to {@link #finishUpdate}.
682         */
683        protected void startUpdate() {
684            mConfigList.clear();
685        }
686
687        /**
688         * Called after {@link #startUpdate} to indicate a range of enabled
689         * values.
690         * @param startId the first id included in the range
691         * @param endId the last id included in the range
692         */
693        protected void addRange(int startId, int endId, boolean selected) {
694            mConfigList.add(new CdmaSmsBroadcastConfigInfo(startId, endId,
695                    1, selected));
696        }
697
698        /**
699         * Called to indicate the end of a range update started by the previous
700         * call to {@link #startUpdate}.
701         * @return true if successful, false otherwise
702         */
703        protected boolean finishUpdate() {
704            if (mConfigList.isEmpty()) {
705                return true;
706            } else {
707                CdmaSmsBroadcastConfigInfo[] configs =
708                        mConfigList.toArray(new CdmaSmsBroadcastConfigInfo[mConfigList.size()]);
709                return setCdmaBroadcastConfig(configs);
710            }
711        }
712    }
713
714    private boolean setCellBroadcastConfig(SmsBroadcastConfigInfo[] configs) {
715        if (DBG)
716            log("Calling setGsmBroadcastConfig with " + configs.length + " configurations");
717
718        synchronized (mLock) {
719            Message response = mHandler.obtainMessage(EVENT_SET_BROADCAST_CONFIG_DONE);
720
721            mSuccess = false;
722            mPhone.mCi.setGsmBroadcastConfig(configs, response);
723
724            try {
725                mLock.wait();
726            } catch (InterruptedException e) {
727                log("interrupted while trying to set cell broadcast config");
728            }
729        }
730
731        return mSuccess;
732    }
733
734    private boolean setCellBroadcastActivation(boolean activate) {
735        if (DBG)
736            log("Calling setCellBroadcastActivation(" + activate + ')');
737
738        synchronized (mLock) {
739            Message response = mHandler.obtainMessage(EVENT_SET_BROADCAST_ACTIVATION_DONE);
740
741            mSuccess = false;
742            mPhone.mCi.setGsmBroadcastActivation(activate, response);
743
744            try {
745                mLock.wait();
746            } catch (InterruptedException e) {
747                log("interrupted while trying to set cell broadcast activation");
748            }
749        }
750
751        return mSuccess;
752    }
753
754    private boolean setCdmaBroadcastConfig(CdmaSmsBroadcastConfigInfo[] configs) {
755        if (DBG)
756            log("Calling setCdmaBroadcastConfig with " + configs.length + " configurations");
757
758        synchronized (mLock) {
759            Message response = mHandler.obtainMessage(EVENT_SET_BROADCAST_CONFIG_DONE);
760
761            mSuccess = false;
762            mPhone.mCi.setCdmaBroadcastConfig(configs, response);
763
764            try {
765                mLock.wait();
766            } catch (InterruptedException e) {
767                log("interrupted while trying to set cdma broadcast config");
768            }
769        }
770
771        return mSuccess;
772    }
773
774    private boolean setCdmaBroadcastActivation(boolean activate) {
775        if (DBG)
776            log("Calling setCdmaBroadcastActivation(" + activate + ")");
777
778        synchronized (mLock) {
779            Message response = mHandler.obtainMessage(EVENT_SET_BROADCAST_ACTIVATION_DONE);
780
781            mSuccess = false;
782            mPhone.mCi.setCdmaBroadcastActivation(activate, response);
783
784            try {
785                mLock.wait();
786            } catch (InterruptedException e) {
787                log("interrupted while trying to set cdma broadcast activation");
788            }
789        }
790
791        return mSuccess;
792    }
793
794    protected void log(String msg) {
795        Log.d(LOG_TAG, "[IccSmsInterfaceManager] " + msg);
796    }
797
798    public boolean isImsSmsSupported() {
799        return mDispatcher.isIms();
800    }
801
802    public String getImsSmsFormat() {
803        return mDispatcher.getImsSmsFormat();
804    }
805}
806