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 com.google.android.mms.pdu.PduHeaders.MESSAGE_TYPE_DELIVERY_IND; 20 import static com.google.android.mms.pdu.PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND; 21 import static com.google.android.mms.pdu.PduHeaders.MESSAGE_TYPE_READ_ORIG_IND; 22 import android.app.Activity; 23 import android.app.AppOpsManager; 24 import android.app.BroadcastOptions; 25 import android.content.BroadcastReceiver; 26 import android.content.ComponentName; 27 import android.content.ContentValues; 28 import android.content.Context; 29 import android.content.Intent; 30 import android.content.IntentFilter; 31 import android.content.ServiceConnection; 32 import android.database.Cursor; 33 import android.database.DatabaseUtils; 34 import android.database.sqlite.SQLiteException; 35 import android.database.sqlite.SqliteWrapper; 36 import android.net.Uri; 37 import android.os.Bundle; 38 import android.os.IBinder; 39 import android.os.IDeviceIdleController; 40 import android.os.RemoteException; 41 import android.os.ServiceManager; 42 import android.os.UserHandle; 43 import android.os.UserManager; 44 import android.provider.Telephony; 45 import android.provider.Telephony.Sms.Intents; 46 import android.telephony.Rlog; 47 import android.telephony.SmsManager; 48 import android.telephony.SubscriptionManager; 49 import android.util.Log; 50 51 import com.android.internal.telephony.uicc.IccUtils; 52 53 import java.util.HashMap; 54 55 import com.google.android.mms.MmsException; 56 import com.google.android.mms.pdu.DeliveryInd; 57 import com.google.android.mms.pdu.GenericPdu; 58 import com.google.android.mms.pdu.NotificationInd; 59 import com.google.android.mms.pdu.PduHeaders; 60 import com.google.android.mms.pdu.PduParser; 61 import com.google.android.mms.pdu.PduPersister; 62 import com.google.android.mms.pdu.ReadOrigInd; 63 64 /** 65 * WAP push handler class. 66 * 67 * @hide 68 */ 69 public class WapPushOverSms implements ServiceConnection { 70 private static final String TAG = "WAP PUSH"; 71 private static final boolean DBG = false; 72 73 private final Context mContext; 74 private IDeviceIdleController mDeviceIdleController; 75 76 private String mWapPushManagerPackage; 77 78 /** Assigned from ServiceConnection callback on main threaad. */ 79 private volatile IWapPushManager mWapPushManager; 80 81 /** Broadcast receiver that binds to WapPushManager when the user unlocks the phone for the 82 * first time after reboot and the credential-encrypted storage is available. 83 */ 84 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 85 @Override 86 public void onReceive(final Context context, Intent intent) { 87 Rlog.d(TAG, "Received broadcast " + intent.getAction()); 88 if (Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) { 89 new BindServiceThread(mContext).start(); 90 } 91 } 92 }; 93 94 private class BindServiceThread extends Thread { 95 private final Context context; 96 BindServiceThread(Context context)97 private BindServiceThread(Context context) { 98 this.context = context; 99 } 100 101 @Override run()102 public void run() { 103 bindWapPushManagerService(context); 104 } 105 } 106 bindWapPushManagerService(Context context)107 private void bindWapPushManagerService(Context context) { 108 Intent intent = new Intent(IWapPushManager.class.getName()); 109 ComponentName comp = intent.resolveSystemService(context.getPackageManager(), 0); 110 intent.setComponent(comp); 111 if (comp == null || !context.bindService(intent, this, Context.BIND_AUTO_CREATE)) { 112 Rlog.e(TAG, "bindService() for wappush manager failed"); 113 } else { 114 synchronized (this) { 115 mWapPushManagerPackage = comp.getPackageName(); 116 } 117 if (DBG) Rlog.v(TAG, "bindService() for wappush manager succeeded"); 118 } 119 } 120 121 @Override onServiceConnected(ComponentName name, IBinder service)122 public void onServiceConnected(ComponentName name, IBinder service) { 123 mWapPushManager = IWapPushManager.Stub.asInterface(service); 124 if (DBG) Rlog.v(TAG, "wappush manager connected to " + hashCode()); 125 } 126 127 @Override onServiceDisconnected(ComponentName name)128 public void onServiceDisconnected(ComponentName name) { 129 mWapPushManager = null; 130 if (DBG) Rlog.v(TAG, "wappush manager disconnected."); 131 } 132 WapPushOverSms(Context context)133 public WapPushOverSms(Context context) { 134 mContext = context; 135 mDeviceIdleController = TelephonyComponentFactory.getInstance().getIDeviceIdleController(); 136 137 UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); 138 139 if (userManager.isUserUnlocked()) { 140 bindWapPushManagerService(mContext); 141 } else { 142 IntentFilter userFilter = new IntentFilter(); 143 userFilter.addAction(Intent.ACTION_USER_UNLOCKED); 144 context.registerReceiver(mBroadcastReceiver, userFilter); 145 } 146 } 147 dispose()148 public void dispose() { 149 if (mWapPushManager != null) { 150 if (DBG) Rlog.v(TAG, "dispose: unbind wappush manager"); 151 mContext.unbindService(this); 152 } else { 153 Rlog.e(TAG, "dispose: not bound to a wappush manager"); 154 } 155 } 156 157 /** 158 * Decodes the wap push pdu. The decoded result is wrapped inside the {@link DecodedResult} 159 * object. The caller of this method should check {@link DecodedResult#statusCode} for the 160 * decoding status. It can have the following values. 161 * 162 * Activity.RESULT_OK - the wap push pdu is successfully decoded and should be further processed 163 * Intents.RESULT_SMS_HANDLED - the wap push pdu should be ignored. 164 * Intents.RESULT_SMS_GENERIC_ERROR - the pdu is invalid. 165 */ decodeWapPdu(byte[] pdu, InboundSmsHandler handler)166 private DecodedResult decodeWapPdu(byte[] pdu, InboundSmsHandler handler) { 167 DecodedResult result = new DecodedResult(); 168 if (DBG) Rlog.d(TAG, "Rx: " + IccUtils.bytesToHexString(pdu)); 169 170 try { 171 int index = 0; 172 int transactionId = pdu[index++] & 0xFF; 173 int pduType = pdu[index++] & 0xFF; 174 175 // Should we "abort" if no subId for now just no supplying extra param below 176 int phoneId = handler.getPhone().getPhoneId(); 177 178 if ((pduType != WspTypeDecoder.PDU_TYPE_PUSH) && 179 (pduType != WspTypeDecoder.PDU_TYPE_CONFIRMED_PUSH)) { 180 index = mContext.getResources().getInteger( 181 com.android.internal.R.integer.config_valid_wappush_index); 182 if (index != -1) { 183 transactionId = pdu[index++] & 0xff; 184 pduType = pdu[index++] & 0xff; 185 if (DBG) 186 Rlog.d(TAG, "index = " + index + " PDU Type = " + pduType + 187 " transactionID = " + transactionId); 188 189 // recheck wap push pduType 190 if ((pduType != WspTypeDecoder.PDU_TYPE_PUSH) 191 && (pduType != WspTypeDecoder.PDU_TYPE_CONFIRMED_PUSH)) { 192 if (DBG) Rlog.w(TAG, "Received non-PUSH WAP PDU. Type = " + pduType); 193 result.statusCode = Intents.RESULT_SMS_HANDLED; 194 return result; 195 } 196 } else { 197 if (DBG) Rlog.w(TAG, "Received non-PUSH WAP PDU. Type = " + pduType); 198 result.statusCode = Intents.RESULT_SMS_HANDLED; 199 return result; 200 } 201 } 202 203 WspTypeDecoder pduDecoder = 204 TelephonyComponentFactory.getInstance().makeWspTypeDecoder(pdu); 205 206 /** 207 * Parse HeaderLen(unsigned integer). 208 * From wap-230-wsp-20010705-a section 8.1.2 209 * The maximum size of a uintvar is 32 bits. 210 * So it will be encoded in no more than 5 octets. 211 */ 212 if (pduDecoder.decodeUintvarInteger(index) == false) { 213 if (DBG) Rlog.w(TAG, "Received PDU. Header Length error."); 214 result.statusCode = Intents.RESULT_SMS_GENERIC_ERROR; 215 return result; 216 } 217 int headerLength = (int) pduDecoder.getValue32(); 218 index += pduDecoder.getDecodedDataLength(); 219 220 int headerStartIndex = index; 221 222 /** 223 * Parse Content-Type. 224 * From wap-230-wsp-20010705-a section 8.4.2.24 225 * 226 * Content-type-value = Constrained-media | Content-general-form 227 * Content-general-form = Value-length Media-type 228 * Media-type = (Well-known-media | Extension-Media) *(Parameter) 229 * Value-length = Short-length | (Length-quote Length) 230 * Short-length = <Any octet 0-30> (octet <= WAP_PDU_SHORT_LENGTH_MAX) 231 * Length-quote = <Octet 31> (WAP_PDU_LENGTH_QUOTE) 232 * Length = Uintvar-integer 233 */ 234 if (pduDecoder.decodeContentType(index) == false) { 235 if (DBG) Rlog.w(TAG, "Received PDU. Header Content-Type error."); 236 result.statusCode = Intents.RESULT_SMS_GENERIC_ERROR; 237 return result; 238 } 239 240 String mimeType = pduDecoder.getValueString(); 241 long binaryContentType = pduDecoder.getValue32(); 242 index += pduDecoder.getDecodedDataLength(); 243 244 byte[] header = new byte[headerLength]; 245 System.arraycopy(pdu, headerStartIndex, header, 0, header.length); 246 247 byte[] intentData; 248 249 if (mimeType != null && mimeType.equals(WspTypeDecoder.CONTENT_TYPE_B_PUSH_CO)) { 250 intentData = pdu; 251 } else { 252 int dataIndex = headerStartIndex + headerLength; 253 intentData = new byte[pdu.length - dataIndex]; 254 System.arraycopy(pdu, dataIndex, intentData, 0, intentData.length); 255 } 256 257 int[] subIds = SubscriptionManager.getSubId(phoneId); 258 int subId = (subIds != null) && (subIds.length > 0) ? subIds[0] 259 : SmsManager.getDefaultSmsSubscriptionId(); 260 261 // Continue if PDU parsing fails: the default messaging app may successfully parse the 262 // same PDU. 263 GenericPdu parsedPdu = null; 264 try { 265 parsedPdu = new PduParser(intentData, shouldParseContentDisposition(subId)).parse(); 266 } catch (Exception e) { 267 Rlog.e(TAG, "Unable to parse PDU: " + e.toString()); 268 } 269 270 if (parsedPdu != null && parsedPdu.getMessageType() == MESSAGE_TYPE_NOTIFICATION_IND) { 271 final NotificationInd nInd = (NotificationInd) parsedPdu; 272 if (nInd.getFrom() != null 273 && BlockChecker.isBlocked(mContext, nInd.getFrom().getString())) { 274 result.statusCode = Intents.RESULT_SMS_HANDLED; 275 return result; 276 } 277 } 278 279 /** 280 * Seek for application ID field in WSP header. 281 * If application ID is found, WapPushManager substitute the message 282 * processing. Since WapPushManager is optional module, if WapPushManager 283 * is not found, legacy message processing will be continued. 284 */ 285 if (pduDecoder.seekXWapApplicationId(index, index + headerLength - 1)) { 286 index = (int) pduDecoder.getValue32(); 287 pduDecoder.decodeXWapApplicationId(index); 288 String wapAppId = pduDecoder.getValueString(); 289 if (wapAppId == null) { 290 wapAppId = Integer.toString((int) pduDecoder.getValue32()); 291 } 292 result.wapAppId = wapAppId; 293 String contentType = ((mimeType == null) ? 294 Long.toString(binaryContentType) : mimeType); 295 result.contentType = contentType; 296 if (DBG) Rlog.v(TAG, "appid found: " + wapAppId + ":" + contentType); 297 } 298 299 result.subId = subId; 300 result.phoneId = phoneId; 301 result.parsedPdu = parsedPdu; 302 result.mimeType = mimeType; 303 result.transactionId = transactionId; 304 result.pduType = pduType; 305 result.header = header; 306 result.intentData = intentData; 307 result.contentTypeParameters = pduDecoder.getContentParameters(); 308 result.statusCode = Activity.RESULT_OK; 309 } catch (ArrayIndexOutOfBoundsException aie) { 310 // 0-byte WAP PDU or other unexpected WAP PDU contents can easily throw this; 311 // log exception string without stack trace and return false. 312 Rlog.e(TAG, "ignoring dispatchWapPdu() array index exception: " + aie); 313 result.statusCode = Intents.RESULT_SMS_GENERIC_ERROR; 314 } 315 return result; 316 } 317 318 /** 319 * Dispatches inbound messages that are in the WAP PDU format. See 320 * wap-230-wsp-20010705-a section 8 for details on the WAP PDU format. 321 * 322 * @param pdu The WAP PDU, made up of one or more SMS PDUs 323 * @return a result code from {@link android.provider.Telephony.Sms.Intents}, or 324 * {@link Activity#RESULT_OK} if the message has been broadcast 325 * to applications 326 */ dispatchWapPdu(byte[] pdu, BroadcastReceiver receiver, InboundSmsHandler handler)327 public int dispatchWapPdu(byte[] pdu, BroadcastReceiver receiver, InboundSmsHandler handler) { 328 DecodedResult result = decodeWapPdu(pdu, handler); 329 if (result.statusCode != Activity.RESULT_OK) { 330 return result.statusCode; 331 } 332 333 if (SmsManager.getDefault().getAutoPersisting()) { 334 // Store the wap push data in telephony 335 writeInboxMessage(result.subId, result.parsedPdu); 336 } 337 338 /** 339 * If the pdu has application ID, WapPushManager substitute the message 340 * processing. Since WapPushManager is optional module, if WapPushManager 341 * is not found, legacy message processing will be continued. 342 */ 343 if (result.wapAppId != null) { 344 try { 345 boolean processFurther = true; 346 IWapPushManager wapPushMan = mWapPushManager; 347 348 if (wapPushMan == null) { 349 if (DBG) Rlog.w(TAG, "wap push manager not found!"); 350 } else { 351 synchronized (this) { 352 mDeviceIdleController.addPowerSaveTempWhitelistAppForMms( 353 mWapPushManagerPackage, 0, "mms-mgr"); 354 } 355 356 Intent intent = new Intent(); 357 intent.putExtra("transactionId", result.transactionId); 358 intent.putExtra("pduType", result.pduType); 359 intent.putExtra("header", result.header); 360 intent.putExtra("data", result.intentData); 361 intent.putExtra("contentTypeParameters", result.contentTypeParameters); 362 SubscriptionManager.putPhoneIdAndSubIdExtra(intent, result.phoneId); 363 364 int procRet = wapPushMan.processMessage( 365 result.wapAppId, result.contentType, intent); 366 if (DBG) Rlog.v(TAG, "procRet:" + procRet); 367 if ((procRet & WapPushManagerParams.MESSAGE_HANDLED) > 0 368 && (procRet & WapPushManagerParams.FURTHER_PROCESSING) == 0) { 369 processFurther = false; 370 } 371 } 372 if (!processFurther) { 373 return Intents.RESULT_SMS_HANDLED; 374 } 375 } catch (RemoteException e) { 376 if (DBG) Rlog.w(TAG, "remote func failed..."); 377 } 378 } 379 if (DBG) Rlog.v(TAG, "fall back to existing handler"); 380 381 if (result.mimeType == null) { 382 if (DBG) Rlog.w(TAG, "Header Content-Type error."); 383 return Intents.RESULT_SMS_GENERIC_ERROR; 384 } 385 386 Intent intent = new Intent(Intents.WAP_PUSH_DELIVER_ACTION); 387 intent.setType(result.mimeType); 388 intent.putExtra("transactionId", result.transactionId); 389 intent.putExtra("pduType", result.pduType); 390 intent.putExtra("header", result.header); 391 intent.putExtra("data", result.intentData); 392 intent.putExtra("contentTypeParameters", result.contentTypeParameters); 393 SubscriptionManager.putPhoneIdAndSubIdExtra(intent, result.phoneId); 394 395 // Direct the intent to only the default MMS app. If we can't find a default MMS app 396 // then sent it to all broadcast receivers. 397 ComponentName componentName = SmsApplication.getDefaultMmsApplication(mContext, true); 398 Bundle options = null; 399 if (componentName != null) { 400 // Deliver MMS message only to this receiver 401 intent.setComponent(componentName); 402 if (DBG) Rlog.v(TAG, "Delivering MMS to: " + componentName.getPackageName() + 403 " " + componentName.getClassName()); 404 try { 405 long duration = mDeviceIdleController.addPowerSaveTempWhitelistAppForMms( 406 componentName.getPackageName(), 0, "mms-app"); 407 BroadcastOptions bopts = BroadcastOptions.makeBasic(); 408 bopts.setTemporaryAppWhitelistDuration(duration); 409 options = bopts.toBundle(); 410 } catch (RemoteException e) { 411 } 412 } 413 414 handler.dispatchIntent(intent, getPermissionForType(result.mimeType), 415 getAppOpsPermissionForIntent(result.mimeType), options, receiver, 416 UserHandle.SYSTEM); 417 return Activity.RESULT_OK; 418 } 419 420 /** 421 * Check whether the pdu is a MMS WAP push pdu that should be dispatched to the SMS app. 422 */ isWapPushForMms(byte[] pdu, InboundSmsHandler handler)423 public boolean isWapPushForMms(byte[] pdu, InboundSmsHandler handler) { 424 DecodedResult result = decodeWapPdu(pdu, handler); 425 return result.statusCode == Activity.RESULT_OK 426 && WspTypeDecoder.CONTENT_TYPE_B_MMS.equals(result.mimeType); 427 } 428 shouldParseContentDisposition(int subId)429 private static boolean shouldParseContentDisposition(int subId) { 430 return SmsManager 431 .getSmsManagerForSubscriptionId(subId) 432 .getCarrierConfigValues() 433 .getBoolean(SmsManager.MMS_CONFIG_SUPPORT_MMS_CONTENT_DISPOSITION, true); 434 } 435 writeInboxMessage(int subId, GenericPdu pdu)436 private void writeInboxMessage(int subId, GenericPdu pdu) { 437 if (pdu == null) { 438 Rlog.e(TAG, "Invalid PUSH PDU"); 439 } 440 final PduPersister persister = PduPersister.getPduPersister(mContext); 441 final int type = pdu.getMessageType(); 442 try { 443 switch (type) { 444 case MESSAGE_TYPE_DELIVERY_IND: 445 case MESSAGE_TYPE_READ_ORIG_IND: { 446 final long threadId = getDeliveryOrReadReportThreadId(mContext, pdu); 447 if (threadId == -1) { 448 // The associated SendReq isn't found, therefore skip 449 // processing this PDU. 450 Rlog.e(TAG, "Failed to find delivery or read report's thread id"); 451 break; 452 } 453 final Uri uri = persister.persist( 454 pdu, 455 Telephony.Mms.Inbox.CONTENT_URI, 456 true/*createThreadId*/, 457 true/*groupMmsEnabled*/, 458 null/*preOpenedFiles*/); 459 if (uri == null) { 460 Rlog.e(TAG, "Failed to persist delivery or read report"); 461 break; 462 } 463 // Update thread ID for ReadOrigInd & DeliveryInd. 464 final ContentValues values = new ContentValues(1); 465 values.put(Telephony.Mms.THREAD_ID, threadId); 466 if (SqliteWrapper.update( 467 mContext, 468 mContext.getContentResolver(), 469 uri, 470 values, 471 null/*where*/, 472 null/*selectionArgs*/) != 1) { 473 Rlog.e(TAG, "Failed to update delivery or read report thread id"); 474 } 475 break; 476 } 477 case MESSAGE_TYPE_NOTIFICATION_IND: { 478 final NotificationInd nInd = (NotificationInd) pdu; 479 480 Bundle configs = SmsManager.getSmsManagerForSubscriptionId(subId) 481 .getCarrierConfigValues(); 482 if (configs != null && configs.getBoolean( 483 SmsManager.MMS_CONFIG_APPEND_TRANSACTION_ID, false)) { 484 final byte [] contentLocation = nInd.getContentLocation(); 485 if ('=' == contentLocation[contentLocation.length - 1]) { 486 byte [] transactionId = nInd.getTransactionId(); 487 byte [] contentLocationWithId = new byte [contentLocation.length 488 + transactionId.length]; 489 System.arraycopy(contentLocation, 0, contentLocationWithId, 490 0, contentLocation.length); 491 System.arraycopy(transactionId, 0, contentLocationWithId, 492 contentLocation.length, transactionId.length); 493 nInd.setContentLocation(contentLocationWithId); 494 } 495 } 496 if (!isDuplicateNotification(mContext, nInd)) { 497 final Uri uri = persister.persist( 498 pdu, 499 Telephony.Mms.Inbox.CONTENT_URI, 500 true/*createThreadId*/, 501 true/*groupMmsEnabled*/, 502 null/*preOpenedFiles*/); 503 if (uri == null) { 504 Rlog.e(TAG, "Failed to save MMS WAP push notification ind"); 505 } 506 } else { 507 Rlog.d(TAG, "Skip storing duplicate MMS WAP push notification ind: " 508 + new String(nInd.getContentLocation())); 509 } 510 break; 511 } 512 default: 513 Log.e(TAG, "Received unrecognized WAP Push PDU."); 514 } 515 } catch (MmsException e) { 516 Log.e(TAG, "Failed to save MMS WAP push data: type=" + type, e); 517 } catch (RuntimeException e) { 518 Log.e(TAG, "Unexpected RuntimeException in persisting MMS WAP push data", e); 519 } 520 521 } 522 523 private static final String THREAD_ID_SELECTION = 524 Telephony.Mms.MESSAGE_ID + "=? AND " + Telephony.Mms.MESSAGE_TYPE + "=?"; 525 getDeliveryOrReadReportThreadId(Context context, GenericPdu pdu)526 private static long getDeliveryOrReadReportThreadId(Context context, GenericPdu pdu) { 527 String messageId; 528 if (pdu instanceof DeliveryInd) { 529 messageId = new String(((DeliveryInd) pdu).getMessageId()); 530 } else if (pdu instanceof ReadOrigInd) { 531 messageId = new String(((ReadOrigInd) pdu).getMessageId()); 532 } else { 533 Rlog.e(TAG, "WAP Push data is neither delivery or read report type: " 534 + pdu.getClass().getCanonicalName()); 535 return -1L; 536 } 537 Cursor cursor = null; 538 try { 539 cursor = SqliteWrapper.query( 540 context, 541 context.getContentResolver(), 542 Telephony.Mms.CONTENT_URI, 543 new String[]{ Telephony.Mms.THREAD_ID }, 544 THREAD_ID_SELECTION, 545 new String[]{ 546 DatabaseUtils.sqlEscapeString(messageId), 547 Integer.toString(PduHeaders.MESSAGE_TYPE_SEND_REQ) 548 }, 549 null/*sortOrder*/); 550 if (cursor != null && cursor.moveToFirst()) { 551 return cursor.getLong(0); 552 } 553 } catch (SQLiteException e) { 554 Rlog.e(TAG, "Failed to query delivery or read report thread id", e); 555 } finally { 556 if (cursor != null) { 557 cursor.close(); 558 } 559 } 560 return -1L; 561 } 562 563 private static final String LOCATION_SELECTION = 564 Telephony.Mms.MESSAGE_TYPE + "=? AND " + Telephony.Mms.CONTENT_LOCATION + " =?"; 565 isDuplicateNotification(Context context, NotificationInd nInd)566 private static boolean isDuplicateNotification(Context context, NotificationInd nInd) { 567 final byte[] rawLocation = nInd.getContentLocation(); 568 if (rawLocation != null) { 569 String location = new String(rawLocation); 570 String[] selectionArgs = new String[] { location }; 571 Cursor cursor = null; 572 try { 573 cursor = SqliteWrapper.query( 574 context, 575 context.getContentResolver(), 576 Telephony.Mms.CONTENT_URI, 577 new String[]{Telephony.Mms._ID}, 578 LOCATION_SELECTION, 579 new String[]{ 580 Integer.toString(PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND), 581 new String(rawLocation) 582 }, 583 null/*sortOrder*/); 584 if (cursor != null && cursor.getCount() > 0) { 585 // We already received the same notification before. 586 return true; 587 } 588 } catch (SQLiteException e) { 589 Rlog.e(TAG, "failed to query existing notification ind", e); 590 } finally { 591 if (cursor != null) { 592 cursor.close(); 593 } 594 } 595 } 596 return false; 597 } 598 getPermissionForType(String mimeType)599 public static String getPermissionForType(String mimeType) { 600 String permission; 601 if (WspTypeDecoder.CONTENT_TYPE_B_MMS.equals(mimeType)) { 602 permission = android.Manifest.permission.RECEIVE_MMS; 603 } else { 604 permission = android.Manifest.permission.RECEIVE_WAP_PUSH; 605 } 606 return permission; 607 } 608 getAppOpsPermissionForIntent(String mimeType)609 public static int getAppOpsPermissionForIntent(String mimeType) { 610 int appOp; 611 if (WspTypeDecoder.CONTENT_TYPE_B_MMS.equals(mimeType)) { 612 appOp = AppOpsManager.OP_RECEIVE_MMS; 613 } else { 614 appOp = AppOpsManager.OP_RECEIVE_WAP_PUSH; 615 } 616 return appOp; 617 } 618 619 /** 620 * Place holder for decoded Wap pdu data. 621 */ 622 private final class DecodedResult { 623 String mimeType; 624 String contentType; 625 int transactionId; 626 int pduType; 627 int phoneId; 628 int subId; 629 byte[] header; 630 String wapAppId; 631 byte[] intentData; 632 HashMap<String, String> contentTypeParameters; 633 GenericPdu parsedPdu; 634 int statusCode; 635 } 636 } 637