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