FileDocCategorySizeDatePackage
WapPushOverSms.javaAPI DocAndroid 5.1 API21328Thu Mar 12 22:22:54 GMT 2015com.android.internal.telephony

WapPushOverSms

public class WapPushOverSms extends Object implements android.content.ServiceConnection
WAP push handler class.
hide

Fields Summary
private static final String
TAG
private static final boolean
DBG
private final android.content.Context
mContext
private volatile IWapPushManager
mWapPushManager
Assigned from ServiceConnection callback on main threaad.
private static final String
THREAD_ID_SELECTION
private static final String
LOCATION_SELECTION
Constructors Summary
public WapPushOverSms(android.content.Context context)

        mContext = context;
        Intent intent = new Intent(IWapPushManager.class.getName());
        ComponentName comp = intent.resolveSystemService(context.getPackageManager(), 0);
        intent.setComponent(comp);
        if (comp == null || !context.bindService(intent, this, Context.BIND_AUTO_CREATE)) {
            Rlog.e(TAG, "bindService() for wappush manager failed");
        } else {
            if (DBG) Rlog.v(TAG, "bindService() for wappush manager succeeded");
        }
    
Methods Summary
public intdispatchWapPdu(byte[] pdu, android.content.BroadcastReceiver receiver, InboundSmsHandler handler)
Dispatches inbound messages that are in the WAP PDU format. See wap-230-wsp-20010705-a section 8 for details on the WAP PDU format.

param
pdu The WAP PDU, made up of one or more SMS PDUs
return
a result code from {@link android.provider.Telephony.Sms.Intents}, or {@link Activity#RESULT_OK} if the message has been broadcast to applications


        if (DBG) Rlog.d(TAG, "Rx: " + IccUtils.bytesToHexString(pdu));

        try {
            int index = 0;
            int transactionId = pdu[index++] & 0xFF;
            int pduType = pdu[index++] & 0xFF;

            // Should we "abort" if no subId for now just no supplying extra param below
            int phoneId = handler.getPhone().getPhoneId();

            if ((pduType != WspTypeDecoder.PDU_TYPE_PUSH) &&
                    (pduType != WspTypeDecoder.PDU_TYPE_CONFIRMED_PUSH)) {
                index = mContext.getResources().getInteger(
                        com.android.internal.R.integer.config_valid_wappush_index);
                if (index != -1) {
                    transactionId = pdu[index++] & 0xff;
                    pduType = pdu[index++] & 0xff;
                    if (DBG)
                        Rlog.d(TAG, "index = " + index + " PDU Type = " + pduType +
                                " transactionID = " + transactionId);

                    // recheck wap push pduType
                    if ((pduType != WspTypeDecoder.PDU_TYPE_PUSH)
                            && (pduType != WspTypeDecoder.PDU_TYPE_CONFIRMED_PUSH)) {
                        if (DBG) Rlog.w(TAG, "Received non-PUSH WAP PDU. Type = " + pduType);
                        return Intents.RESULT_SMS_HANDLED;
                    }
                } else {
                    if (DBG) Rlog.w(TAG, "Received non-PUSH WAP PDU. Type = " + pduType);
                    return Intents.RESULT_SMS_HANDLED;
                }
            }

            WspTypeDecoder pduDecoder = new WspTypeDecoder(pdu);

            /**
             * Parse HeaderLen(unsigned integer).
             * From wap-230-wsp-20010705-a section 8.1.2
             * The maximum size of a uintvar is 32 bits.
             * So it will be encoded in no more than 5 octets.
             */
            if (pduDecoder.decodeUintvarInteger(index) == false) {
                if (DBG) Rlog.w(TAG, "Received PDU. Header Length error.");
                return Intents.RESULT_SMS_GENERIC_ERROR;
            }
            int headerLength = (int) pduDecoder.getValue32();
            index += pduDecoder.getDecodedDataLength();

            int headerStartIndex = index;

            /**
             * Parse Content-Type.
             * From wap-230-wsp-20010705-a section 8.4.2.24
             *
             * Content-type-value = Constrained-media | Content-general-form
             * Content-general-form = Value-length Media-type
             * Media-type = (Well-known-media | Extension-Media) *(Parameter)
             * Value-length = Short-length | (Length-quote Length)
             * Short-length = <Any octet 0-30>   (octet <= WAP_PDU_SHORT_LENGTH_MAX)
             * Length-quote = <Octet 31>         (WAP_PDU_LENGTH_QUOTE)
             * Length = Uintvar-integer
             */
            if (pduDecoder.decodeContentType(index) == false) {
                if (DBG) Rlog.w(TAG, "Received PDU. Header Content-Type error.");
                return Intents.RESULT_SMS_GENERIC_ERROR;
            }

            String mimeType = pduDecoder.getValueString();
            long binaryContentType = pduDecoder.getValue32();
            index += pduDecoder.getDecodedDataLength();

            byte[] header = new byte[headerLength];
            System.arraycopy(pdu, headerStartIndex, header, 0, header.length);

            byte[] intentData;

            if (mimeType != null && mimeType.equals(WspTypeDecoder.CONTENT_TYPE_B_PUSH_CO)) {
                intentData = pdu;
            } else {
                int dataIndex = headerStartIndex + headerLength;
                intentData = new byte[pdu.length - dataIndex];
                System.arraycopy(pdu, dataIndex, intentData, 0, intentData.length);
            }

            if (SmsManager.getDefault().getAutoPersisting()) {
                // Store the wap push data in telephony
                int [] subIds = SubscriptionManager.getSubId(phoneId);
                // FIXME (tomtaylor) - when we've updated SubscriptionManager, change
                // SubscriptionManager.DEFAULT_SUB_ID to SubscriptionManager.getDefaultSmsSubId()
                int subId = (subIds != null) && (subIds.length > 0) ? subIds[0] :
                    SmsManager.getDefaultSmsSubscriptionId();
                writeInboxMessage(subId, intentData);
            }

            /**
             * Seek for application ID field in WSP header.
             * If application ID is found, WapPushManager substitute the message
             * processing. Since WapPushManager is optional module, if WapPushManager
             * is not found, legacy message processing will be continued.
             */
            if (pduDecoder.seekXWapApplicationId(index, index + headerLength - 1)) {
                index = (int) pduDecoder.getValue32();
                pduDecoder.decodeXWapApplicationId(index);
                String wapAppId = pduDecoder.getValueString();
                if (wapAppId == null) {
                    wapAppId = Integer.toString((int) pduDecoder.getValue32());
                }

                String contentType = ((mimeType == null) ?
                        Long.toString(binaryContentType) : mimeType);
                if (DBG) Rlog.v(TAG, "appid found: " + wapAppId + ":" + contentType);

                try {
                    boolean processFurther = true;
                    IWapPushManager wapPushMan = mWapPushManager;

                    if (wapPushMan == null) {
                        if (DBG) Rlog.w(TAG, "wap push manager not found!");
                    } else {
                        Intent intent = new Intent();
                        intent.putExtra("transactionId", transactionId);
                        intent.putExtra("pduType", pduType);
                        intent.putExtra("header", header);
                        intent.putExtra("data", intentData);
                        intent.putExtra("contentTypeParameters",
                                pduDecoder.getContentParameters());
                        SubscriptionManager.putPhoneIdAndSubIdExtra(intent, phoneId);

                        int procRet = wapPushMan.processMessage(wapAppId, contentType, intent);
                        if (DBG) Rlog.v(TAG, "procRet:" + procRet);
                        if ((procRet & WapPushManagerParams.MESSAGE_HANDLED) > 0
                                && (procRet & WapPushManagerParams.FURTHER_PROCESSING) == 0) {
                            processFurther = false;
                        }
                    }
                    if (!processFurther) {
                        return Intents.RESULT_SMS_HANDLED;
                    }
                } catch (RemoteException e) {
                    if (DBG) Rlog.w(TAG, "remote func failed...");
                }
            }
            if (DBG) Rlog.v(TAG, "fall back to existing handler");

            if (mimeType == null) {
                if (DBG) Rlog.w(TAG, "Header Content-Type error.");
                return Intents.RESULT_SMS_GENERIC_ERROR;
            }

            String permission;
            int appOp;

            if (mimeType.equals(WspTypeDecoder.CONTENT_TYPE_B_MMS)) {
                permission = android.Manifest.permission.RECEIVE_MMS;
                appOp = AppOpsManager.OP_RECEIVE_MMS;
            } else {
                permission = android.Manifest.permission.RECEIVE_WAP_PUSH;
                appOp = AppOpsManager.OP_RECEIVE_WAP_PUSH;
            }

            Intent intent = new Intent(Intents.WAP_PUSH_DELIVER_ACTION);
            intent.setType(mimeType);
            intent.putExtra("transactionId", transactionId);
            intent.putExtra("pduType", pduType);
            intent.putExtra("header", header);
            intent.putExtra("data", intentData);
            intent.putExtra("contentTypeParameters", pduDecoder.getContentParameters());
            SubscriptionManager.putPhoneIdAndSubIdExtra(intent, phoneId);

            // Direct the intent to only the default MMS app. If we can't find a default MMS app
            // then sent it to all broadcast receivers.
            ComponentName componentName = SmsApplication.getDefaultMmsApplication(mContext, true);
            if (componentName != null) {
                // Deliver MMS message only to this receiver
                intent.setComponent(componentName);
                if (DBG) Rlog.v(TAG, "Delivering MMS to: " + componentName.getPackageName() +
                        " " + componentName.getClassName());
            }

            handler.dispatchIntent(intent, permission, appOp, receiver, UserHandle.OWNER);
            return Activity.RESULT_OK;
        } catch (ArrayIndexOutOfBoundsException aie) {
            // 0-byte WAP PDU or other unexpected WAP PDU contents can easily throw this;
            // log exception string without stack trace and return false.
            Rlog.e(TAG, "ignoring dispatchWapPdu() array index exception: " + aie);
            return Intents.RESULT_SMS_GENERIC_ERROR;
        }
    
voiddispose()

        if (mWapPushManager != null) {
            if (DBG) Rlog.v(TAG, "dispose: unbind wappush manager");
            mContext.unbindService(this);
        } else {
            Rlog.e(TAG, "dispose: not bound to a wappush manager");
        }
    
private static longgetDeliveryOrReadReportThreadId(android.content.Context context, com.google.android.mms.pdu.GenericPdu pdu)


           
        String messageId;
        if (pdu instanceof DeliveryInd) {
            messageId = new String(((DeliveryInd) pdu).getMessageId());
        } else if (pdu instanceof ReadOrigInd) {
            messageId = new String(((ReadOrigInd) pdu).getMessageId());
        } else {
            Rlog.e(TAG, "WAP Push data is neither delivery or read report type: "
                    + pdu.getClass().getCanonicalName());
            return -1L;
        }
        Cursor cursor = null;
        try {
            cursor = SqliteWrapper.query(
                    context,
                    context.getContentResolver(),
                    Telephony.Mms.CONTENT_URI,
                    new String[]{ Telephony.Mms.THREAD_ID },
                    THREAD_ID_SELECTION,
                    new String[]{
                            DatabaseUtils.sqlEscapeString(messageId),
                            Integer.toString(PduHeaders.MESSAGE_TYPE_SEND_REQ)
                    },
                    null/*sortOrder*/);
            if (cursor != null && cursor.moveToFirst()) {
                return cursor.getLong(0);
            }
        } catch (SQLiteException e) {
            Rlog.e(TAG, "Failed to query delivery or read report thread id", e);
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }
        return -1L;
    
private static booleanisDuplicateNotification(android.content.Context context, com.google.android.mms.pdu.NotificationInd nInd)


           
        final byte[] rawLocation = nInd.getContentLocation();
        if (rawLocation != null) {
            String location = new String(rawLocation);
            String[] selectionArgs = new String[] { location };
            Cursor cursor = null;
            try {
                cursor = SqliteWrapper.query(
                        context,
                        context.getContentResolver(),
                        Telephony.Mms.CONTENT_URI,
                        new String[]{Telephony.Mms._ID},
                        LOCATION_SELECTION,
                        new String[]{
                                Integer.toString(PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND),
                                new String(rawLocation)
                        },
                        null/*sortOrder*/);
                if (cursor != null && cursor.getCount() > 0) {
                    // We already received the same notification before.
                    return true;
                }
            } catch (SQLiteException e) {
                Rlog.e(TAG, "failed to query existing notification ind", e);
            } finally {
                if (cursor != null) {
                    cursor.close();
                }
            }
        }
        return false;
    
public voidonServiceConnected(android.content.ComponentName name, android.os.IBinder service)


    
          
        mWapPushManager = IWapPushManager.Stub.asInterface(service);
        if (DBG) Rlog.v(TAG, "wappush manager connected to " + hashCode());
    
public voidonServiceDisconnected(android.content.ComponentName name)

        mWapPushManager = null;
        if (DBG) Rlog.v(TAG, "wappush manager disconnected.");
    
private static booleanshouldParseContentDisposition(int subId)

        return SmsManager
                .getSmsManagerForSubscriptionId(subId)
                .getCarrierConfigValues()
                .getBoolean(SmsManager.MMS_CONFIG_SUPPORT_MMS_CONTENT_DISPOSITION, true);
    
private voidwriteInboxMessage(int subId, byte[] pushData)

        final GenericPdu pdu =
                new PduParser(pushData, shouldParseContentDisposition(subId)).parse();
        if (pdu == null) {
            Rlog.e(TAG, "Invalid PUSH PDU");
        }
        final PduPersister persister = PduPersister.getPduPersister(mContext);
        final int type = pdu.getMessageType();
        try {
            switch (type) {
                case MESSAGE_TYPE_DELIVERY_IND:
                case MESSAGE_TYPE_READ_ORIG_IND: {
                    final long threadId = getDeliveryOrReadReportThreadId(mContext, pdu);
                    if (threadId == -1) {
                        // The associated SendReq isn't found, therefore skip
                        // processing this PDU.
                        Rlog.e(TAG, "Failed to find delivery or read report's thread id");
                        break;
                    }
                    final Uri uri = persister.persist(
                            pdu,
                            Telephony.Mms.Inbox.CONTENT_URI,
                            true/*createThreadId*/,
                            true/*groupMmsEnabled*/,
                            null/*preOpenedFiles*/);
                    if (uri == null) {
                        Rlog.e(TAG, "Failed to persist delivery or read report");
                        break;
                    }
                    // Update thread ID for ReadOrigInd & DeliveryInd.
                    final ContentValues values = new ContentValues(1);
                    values.put(Telephony.Mms.THREAD_ID, threadId);
                    if (SqliteWrapper.update(
                            mContext,
                            mContext.getContentResolver(),
                            uri,
                            values,
                            null/*where*/,
                            null/*selectionArgs*/) != 1) {
                        Rlog.e(TAG, "Failed to update delivery or read report thread id");
                    }
                    break;
                }
                case MESSAGE_TYPE_NOTIFICATION_IND: {
                    final NotificationInd nInd = (NotificationInd) pdu;

                    Bundle configs = SmsManager.getSmsManagerForSubscriptionId(subId)
                            .getCarrierConfigValues();
                    if (configs != null && configs.getBoolean(
                        SmsManager.MMS_CONFIG_APPEND_TRANSACTION_ID, false)) {
                        final byte [] contentLocation = nInd.getContentLocation();
                        if ('=" == contentLocation[contentLocation.length - 1]) {
                            byte [] transactionId = nInd.getTransactionId();
                            byte [] contentLocationWithId = new byte [contentLocation.length
                                    + transactionId.length];
                            System.arraycopy(contentLocation, 0, contentLocationWithId,
                                    0, contentLocation.length);
                            System.arraycopy(transactionId, 0, contentLocationWithId,
                                    contentLocation.length, transactionId.length);
                            nInd.setContentLocation(contentLocationWithId);
                        }
                    }
                    if (!isDuplicateNotification(mContext, nInd)) {
                        final Uri uri = persister.persist(
                                pdu,
                                Telephony.Mms.Inbox.CONTENT_URI,
                                true/*createThreadId*/,
                                true/*groupMmsEnabled*/,
                                null/*preOpenedFiles*/);
                        if (uri == null) {
                            Rlog.e(TAG, "Failed to save MMS WAP push notification ind");
                        }
                    } else {
                        Rlog.d(TAG, "Skip storing duplicate MMS WAP push notification ind: "
                                + new String(nInd.getContentLocation()));
                    }
                    break;
                }
                default:
                    Log.e(TAG, "Received unrecognized WAP Push PDU.");
            }
        } catch (MmsException e) {
            Log.e(TAG, "Failed to save MMS WAP push data: type=" + type, e);
        } catch (RuntimeException e) {
            Log.e(TAG, "Unexpected RuntimeException in persisting MMS WAP push data", e);
        }