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