Home | History | Annotate | Download | only in telephony
      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 android.app.Activity;
     20 import android.app.AppOpsManager;
     21 import android.content.BroadcastReceiver;
     22 import android.content.ComponentName;
     23 import android.content.Context;
     24 import android.content.Intent;
     25 import android.content.ServiceConnection;
     26 import android.os.IBinder;
     27 import android.os.RemoteException;
     28 import android.provider.Telephony.Sms.Intents;
     29 import android.telephony.Rlog;
     30 
     31 import com.android.internal.telephony.uicc.IccUtils;
     32 
     33 /**
     34  * WAP push handler class.
     35  *
     36  * @hide
     37  */
     38 public class WapPushOverSms implements ServiceConnection {
     39     private static final String TAG = "WAP PUSH";
     40     private static final boolean DBG = true;
     41 
     42     private final Context mContext;
     43 
     44     /** Assigned from ServiceConnection callback on main threaad. */
     45     private volatile IWapPushManager mWapPushManager;
     46 
     47     @Override
     48     public void onServiceConnected(ComponentName name, IBinder service) {
     49         mWapPushManager = IWapPushManager.Stub.asInterface(service);
     50         if (DBG) Rlog.v(TAG, "wappush manager connected to " + hashCode());
     51     }
     52 
     53     @Override
     54     public void onServiceDisconnected(ComponentName name) {
     55         mWapPushManager = null;
     56         if (DBG) Rlog.v(TAG, "wappush manager disconnected.");
     57     }
     58 
     59     public WapPushOverSms(Context context) {
     60         mContext = context;
     61         Intent intent = new Intent(IWapPushManager.class.getName());
     62         ComponentName comp = intent.resolveSystemService(context.getPackageManager(), 0);
     63         intent.setComponent(comp);
     64         if (comp == null || !context.bindService(intent, this, Context.BIND_AUTO_CREATE)) {
     65             Rlog.e(TAG, "bindService() for wappush manager failed");
     66         } else {
     67             if (DBG) Rlog.v(TAG, "bindService() for wappush manager succeeded");
     68         }
     69     }
     70 
     71     void dispose() {
     72         if (mWapPushManager != null) {
     73             if (DBG) Rlog.v(TAG, "dispose: unbind wappush manager");
     74             mContext.unbindService(this);
     75         } else {
     76             Rlog.e(TAG, "dispose: not bound to a wappush manager");
     77         }
     78     }
     79 
     80     /**
     81      * Dispatches inbound messages that are in the WAP PDU format. See
     82      * wap-230-wsp-20010705-a section 8 for details on the WAP PDU format.
     83      *
     84      * @param pdu The WAP PDU, made up of one or more SMS PDUs
     85      * @return a result code from {@link android.provider.Telephony.Sms.Intents}, or
     86      *         {@link Activity#RESULT_OK} if the message has been broadcast
     87      *         to applications
     88      */
     89     public int dispatchWapPdu(byte[] pdu, BroadcastReceiver receiver, InboundSmsHandler handler) {
     90 
     91         if (DBG) Rlog.d(TAG, "Rx: " + IccUtils.bytesToHexString(pdu));
     92 
     93         try {
     94             int index = 0;
     95             int transactionId = pdu[index++] & 0xFF;
     96             int pduType = pdu[index++] & 0xFF;
     97 
     98             if ((pduType != WspTypeDecoder.PDU_TYPE_PUSH) &&
     99                     (pduType != WspTypeDecoder.PDU_TYPE_CONFIRMED_PUSH)) {
    100                 index = mContext.getResources().getInteger(
    101                         com.android.internal.R.integer.config_valid_wappush_index);
    102                 if (index != -1) {
    103                     transactionId = pdu[index++] & 0xff;
    104                     pduType = pdu[index++] & 0xff;
    105                     if (DBG)
    106                         Rlog.d(TAG, "index = " + index + " PDU Type = " + pduType +
    107                                 " transactionID = " + transactionId);
    108 
    109                     // recheck wap push pduType
    110                     if ((pduType != WspTypeDecoder.PDU_TYPE_PUSH)
    111                             && (pduType != WspTypeDecoder.PDU_TYPE_CONFIRMED_PUSH)) {
    112                         if (DBG) Rlog.w(TAG, "Received non-PUSH WAP PDU. Type = " + pduType);
    113                         return Intents.RESULT_SMS_HANDLED;
    114                     }
    115                 } else {
    116                     if (DBG) Rlog.w(TAG, "Received non-PUSH WAP PDU. Type = " + pduType);
    117                     return Intents.RESULT_SMS_HANDLED;
    118                 }
    119             }
    120 
    121             WspTypeDecoder pduDecoder = new WspTypeDecoder(pdu);
    122 
    123             /**
    124              * Parse HeaderLen(unsigned integer).
    125              * From wap-230-wsp-20010705-a section 8.1.2
    126              * The maximum size of a uintvar is 32 bits.
    127              * So it will be encoded in no more than 5 octets.
    128              */
    129             if (pduDecoder.decodeUintvarInteger(index) == false) {
    130                 if (DBG) Rlog.w(TAG, "Received PDU. Header Length error.");
    131                 return Intents.RESULT_SMS_GENERIC_ERROR;
    132             }
    133             int headerLength = (int) pduDecoder.getValue32();
    134             index += pduDecoder.getDecodedDataLength();
    135 
    136             int headerStartIndex = index;
    137 
    138             /**
    139              * Parse Content-Type.
    140              * From wap-230-wsp-20010705-a section 8.4.2.24
    141              *
    142              * Content-type-value = Constrained-media | Content-general-form
    143              * Content-general-form = Value-length Media-type
    144              * Media-type = (Well-known-media | Extension-Media) *(Parameter)
    145              * Value-length = Short-length | (Length-quote Length)
    146              * Short-length = <Any octet 0-30>   (octet <= WAP_PDU_SHORT_LENGTH_MAX)
    147              * Length-quote = <Octet 31>         (WAP_PDU_LENGTH_QUOTE)
    148              * Length = Uintvar-integer
    149              */
    150             if (pduDecoder.decodeContentType(index) == false) {
    151                 if (DBG) Rlog.w(TAG, "Received PDU. Header Content-Type error.");
    152                 return Intents.RESULT_SMS_GENERIC_ERROR;
    153             }
    154 
    155             String mimeType = pduDecoder.getValueString();
    156             long binaryContentType = pduDecoder.getValue32();
    157             index += pduDecoder.getDecodedDataLength();
    158 
    159             byte[] header = new byte[headerLength];
    160             System.arraycopy(pdu, headerStartIndex, header, 0, header.length);
    161 
    162             byte[] intentData;
    163 
    164             if (mimeType != null && mimeType.equals(WspTypeDecoder.CONTENT_TYPE_B_PUSH_CO)) {
    165                 intentData = pdu;
    166             } else {
    167                 int dataIndex = headerStartIndex + headerLength;
    168                 intentData = new byte[pdu.length - dataIndex];
    169                 System.arraycopy(pdu, dataIndex, intentData, 0, intentData.length);
    170             }
    171 
    172             /**
    173              * Seek for application ID field in WSP header.
    174              * If application ID is found, WapPushManager substitute the message
    175              * processing. Since WapPushManager is optional module, if WapPushManager
    176              * is not found, legacy message processing will be continued.
    177              */
    178             if (pduDecoder.seekXWapApplicationId(index, index + headerLength - 1)) {
    179                 index = (int) pduDecoder.getValue32();
    180                 pduDecoder.decodeXWapApplicationId(index);
    181                 String wapAppId = pduDecoder.getValueString();
    182                 if (wapAppId == null) {
    183                     wapAppId = Integer.toString((int) pduDecoder.getValue32());
    184                 }
    185 
    186                 String contentType = ((mimeType == null) ?
    187                         Long.toString(binaryContentType) : mimeType);
    188                 if (DBG) Rlog.v(TAG, "appid found: " + wapAppId + ":" + contentType);
    189 
    190                 try {
    191                     boolean processFurther = true;
    192                     IWapPushManager wapPushMan = mWapPushManager;
    193 
    194                     if (wapPushMan == null) {
    195                         if (DBG) Rlog.w(TAG, "wap push manager not found!");
    196                     } else {
    197                         Intent intent = new Intent();
    198                         intent.putExtra("transactionId", transactionId);
    199                         intent.putExtra("pduType", pduType);
    200                         intent.putExtra("header", header);
    201                         intent.putExtra("data", intentData);
    202                         intent.putExtra("contentTypeParameters",
    203                                 pduDecoder.getContentParameters());
    204 
    205                         int procRet = wapPushMan.processMessage(wapAppId, contentType, intent);
    206                         if (DBG) Rlog.v(TAG, "procRet:" + procRet);
    207                         if ((procRet & WapPushManagerParams.MESSAGE_HANDLED) > 0
    208                                 && (procRet & WapPushManagerParams.FURTHER_PROCESSING) == 0) {
    209                             processFurther = false;
    210                         }
    211                     }
    212                     if (!processFurther) {
    213                         return Intents.RESULT_SMS_HANDLED;
    214                     }
    215                 } catch (RemoteException e) {
    216                     if (DBG) Rlog.w(TAG, "remote func failed...");
    217                 }
    218             }
    219             if (DBG) Rlog.v(TAG, "fall back to existing handler");
    220 
    221             if (mimeType == null) {
    222                 if (DBG) Rlog.w(TAG, "Header Content-Type error.");
    223                 return Intents.RESULT_SMS_GENERIC_ERROR;
    224             }
    225 
    226             String permission;
    227             int appOp;
    228 
    229             if (mimeType.equals(WspTypeDecoder.CONTENT_TYPE_B_MMS)) {
    230                 permission = android.Manifest.permission.RECEIVE_MMS;
    231                 appOp = AppOpsManager.OP_RECEIVE_MMS;
    232             } else {
    233                 permission = android.Manifest.permission.RECEIVE_WAP_PUSH;
    234                 appOp = AppOpsManager.OP_RECEIVE_WAP_PUSH;
    235             }
    236 
    237             Intent intent = new Intent(Intents.WAP_PUSH_DELIVER_ACTION);
    238             intent.setType(mimeType);
    239             intent.putExtra("transactionId", transactionId);
    240             intent.putExtra("pduType", pduType);
    241             intent.putExtra("header", header);
    242             intent.putExtra("data", intentData);
    243             intent.putExtra("contentTypeParameters", pduDecoder.getContentParameters());
    244 
    245             // Direct the intent to only the default MMS app. If we can't find a default MMS app
    246             // then sent it to all broadcast receivers.
    247             ComponentName componentName = SmsApplication.getDefaultMmsApplication(mContext, true);
    248             if (componentName != null) {
    249                 // Deliver MMS message only to this receiver
    250                 intent.setComponent(componentName);
    251                 if (DBG) Rlog.v(TAG, "Delivering MMS to: " + componentName.getPackageName() +
    252                         " " + componentName.getClassName());
    253             }
    254 
    255             handler.dispatchIntent(intent, permission, appOp, receiver);
    256             return Activity.RESULT_OK;
    257         } catch (ArrayIndexOutOfBoundsException aie) {
    258             // 0-byte WAP PDU or other unexpected WAP PDU contents can easily throw this;
    259             // log exception string without stack trace and return false.
    260             Rlog.e(TAG, "ignoring dispatchWapPdu() array index exception: " + aie);
    261             return Intents.RESULT_SMS_GENERIC_ERROR;
    262         }
    263     }
    264 }
    265