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