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 
     18 package com.android.internal.telephony;
     19 
     20 import android.app.Activity;
     21 import android.app.AppOpsManager;
     22 import android.content.Context;
     23 import android.content.ComponentName;
     24 import android.content.Intent;
     25 import android.content.ServiceConnection;
     26 import android.provider.Telephony.Sms.Intents;
     27 import android.telephony.Rlog;
     28 import android.os.IBinder;
     29 import android.os.RemoteException;
     30 
     31 import com.android.internal.telephony.uicc.IccUtils;
     32 
     33 /**
     34  * WAP push handler class.
     35  *
     36  * @hide
     37  */
     38 public class WapPushOverSms {
     39     private static final String LOG_TAG = "WAP PUSH";
     40     private static final boolean DBG = false;
     41 
     42     private final Context mContext;
     43     private WspTypeDecoder pduDecoder;
     44     private SMSDispatcher mSmsDispatcher;
     45 
     46     private final int BIND_RETRY_INTERVAL = 1000;
     47     /**
     48      * A handle to WapPushManager interface
     49      */
     50     private WapPushConnection mWapConn = null;
     51     private class WapPushConnection implements ServiceConnection {
     52         private IWapPushManager mWapPushMan;
     53         private Context mOwner;
     54 
     55         public WapPushConnection(Context ownerContext) {
     56             mOwner = ownerContext;
     57         }
     58 
     59         @Override
     60         public void onServiceConnected(ComponentName name, IBinder service) {
     61             mWapPushMan = IWapPushManager.Stub.asInterface(service);
     62             if (DBG) Rlog.v(LOG_TAG, "wappush manager connected to " +
     63                     mOwner.hashCode());
     64         }
     65 
     66         @Override
     67         public void onServiceDisconnected(ComponentName name) {
     68             mWapPushMan = null;
     69             if (DBG) Rlog.v(LOG_TAG, "wappush manager disconnected.");
     70             // Detach the previous binder
     71             mOwner.unbindService(mWapConn);
     72             // WapPushManager must be always attached.
     73             rebindWapPushManager();
     74         }
     75 
     76         /**
     77          * bind WapPushManager
     78          */
     79         public void bindWapPushManager() {
     80             if (mWapPushMan != null) return;
     81 
     82             final ServiceConnection wapPushConnection = this;
     83 
     84             mOwner.bindService(new Intent(IWapPushManager.class.getName()),
     85                     wapPushConnection, Context.BIND_AUTO_CREATE);
     86         }
     87 
     88         /**
     89          * rebind WapPushManager
     90          * This method is called when WapPushManager is disconnected unexpectedly.
     91          */
     92         private void rebindWapPushManager() {
     93             if (mWapPushMan != null) return;
     94 
     95             final ServiceConnection wapPushConnection = this;
     96             new Thread() {
     97                 @Override
     98                 public void run() {
     99                     while (mWapPushMan == null) {
    100                         mOwner.bindService(new Intent(IWapPushManager.class.getName()),
    101                                 wapPushConnection, Context.BIND_AUTO_CREATE);
    102                         try {
    103                             Thread.sleep(BIND_RETRY_INTERVAL);
    104                         } catch (InterruptedException e) {
    105                             if (DBG) Rlog.v(LOG_TAG, "sleep interrupted.");
    106                         }
    107                     }
    108                 }
    109             }.start();
    110         }
    111 
    112         /**
    113          * Returns interface to WapPushManager
    114          */
    115         public IWapPushManager getWapPushManager() {
    116             return mWapPushMan;
    117         }
    118     }
    119 
    120     public WapPushOverSms(Phone phone, SMSDispatcher smsDispatcher) {
    121         mSmsDispatcher = smsDispatcher;
    122         mContext = phone.getContext();
    123         mWapConn = new WapPushConnection(mContext);
    124         mWapConn.bindWapPushManager();
    125     }
    126 
    127 
    128     /**
    129      * Dispatches inbound messages that are in the WAP PDU format. See
    130      * wap-230-wsp-20010705-a section 8 for details on the WAP PDU format.
    131      *
    132      * @param pdu The WAP PDU, made up of one or more SMS PDUs
    133      * @return a result code from {@link android.provider.Telephony.Sms.Intents}, or
    134      *         {@link Activity#RESULT_OK} if the message has been broadcast
    135      *         to applications
    136      */
    137     public int dispatchWapPdu(byte[] pdu) {
    138 
    139         if (DBG) Rlog.d(LOG_TAG, "Rx: " + IccUtils.bytesToHexString(pdu));
    140 
    141         int index = 0;
    142         int transactionId = pdu[index++] & 0xFF;
    143         int pduType = pdu[index++] & 0xFF;
    144         int headerLength = 0;
    145 
    146         if ((pduType != WspTypeDecoder.PDU_TYPE_PUSH) &&
    147                 (pduType != WspTypeDecoder.PDU_TYPE_CONFIRMED_PUSH)) {
    148             if (DBG) Rlog.w(LOG_TAG, "Received non-PUSH WAP PDU. Type = " + pduType);
    149             return Intents.RESULT_SMS_HANDLED;
    150         }
    151 
    152         pduDecoder = new WspTypeDecoder(pdu);
    153 
    154         /**
    155          * Parse HeaderLen(unsigned integer).
    156          * From wap-230-wsp-20010705-a section 8.1.2
    157          * The maximum size of a uintvar is 32 bits.
    158          * So it will be encoded in no more than 5 octets.
    159          */
    160         if (pduDecoder.decodeUintvarInteger(index) == false) {
    161             if (DBG) Rlog.w(LOG_TAG, "Received PDU. Header Length error.");
    162             return Intents.RESULT_SMS_GENERIC_ERROR;
    163         }
    164         headerLength = (int)pduDecoder.getValue32();
    165         index += pduDecoder.getDecodedDataLength();
    166 
    167         int headerStartIndex = index;
    168 
    169         /**
    170          * Parse Content-Type.
    171          * From wap-230-wsp-20010705-a section 8.4.2.24
    172          *
    173          * Content-type-value = Constrained-media | Content-general-form
    174          * Content-general-form = Value-length Media-type
    175          * Media-type = (Well-known-media | Extension-Media) *(Parameter)
    176          * Value-length = Short-length | (Length-quote Length)
    177          * Short-length = <Any octet 0-30>   (octet <= WAP_PDU_SHORT_LENGTH_MAX)
    178          * Length-quote = <Octet 31>         (WAP_PDU_LENGTH_QUOTE)
    179          * Length = Uintvar-integer
    180          */
    181         if (pduDecoder.decodeContentType(index) == false) {
    182             if (DBG) Rlog.w(LOG_TAG, "Received PDU. Header Content-Type error.");
    183             return Intents.RESULT_SMS_GENERIC_ERROR;
    184         }
    185 
    186         String mimeType = pduDecoder.getValueString();
    187         long binaryContentType = pduDecoder.getValue32();
    188         index += pduDecoder.getDecodedDataLength();
    189 
    190         byte[] header = new byte[headerLength];
    191         System.arraycopy(pdu, headerStartIndex, header, 0, header.length);
    192 
    193         byte[] intentData;
    194 
    195         if (mimeType != null && mimeType.equals(WspTypeDecoder.CONTENT_TYPE_B_PUSH_CO)) {
    196             intentData = pdu;
    197         } else {
    198             int dataIndex = headerStartIndex + headerLength;
    199             intentData = new byte[pdu.length - dataIndex];
    200             System.arraycopy(pdu, dataIndex, intentData, 0, intentData.length);
    201         }
    202 
    203         /**
    204          * Seek for application ID field in WSP header.
    205          * If application ID is found, WapPushManager substitute the message
    206          * processing. Since WapPushManager is optional module, if WapPushManager
    207          * is not found, legacy message processing will be continued.
    208          */
    209         if (pduDecoder.seekXWapApplicationId(index, index + headerLength - 1)) {
    210             index = (int) pduDecoder.getValue32();
    211             pduDecoder.decodeXWapApplicationId(index);
    212             String wapAppId = pduDecoder.getValueString();
    213             if (wapAppId == null) {
    214                 wapAppId = Integer.toString((int) pduDecoder.getValue32());
    215             }
    216 
    217             String contentType = ((mimeType == null) ?
    218                                   Long.toString(binaryContentType) : mimeType);
    219             if (DBG) Rlog.v(LOG_TAG, "appid found: " + wapAppId + ":" + contentType);
    220 
    221             try {
    222                 boolean processFurther = true;
    223                 IWapPushManager wapPushMan = mWapConn.getWapPushManager();
    224 
    225                 if (wapPushMan == null) {
    226                     if (DBG) Rlog.w(LOG_TAG, "wap push manager not found!");
    227                 } else {
    228                     Intent intent = new Intent();
    229                     intent.putExtra("transactionId", transactionId);
    230                     intent.putExtra("pduType", pduType);
    231                     intent.putExtra("header", header);
    232                     intent.putExtra("data", intentData);
    233                     intent.putExtra("contentTypeParameters",
    234                             pduDecoder.getContentParameters());
    235 
    236                     int procRet = wapPushMan.processMessage(wapAppId, contentType, intent);
    237                     if (DBG) Rlog.v(LOG_TAG, "procRet:" + procRet);
    238                     if ((procRet & WapPushManagerParams.MESSAGE_HANDLED) > 0
    239                         && (procRet & WapPushManagerParams.FURTHER_PROCESSING) == 0) {
    240                         processFurther = false;
    241                     }
    242                 }
    243                 if (!processFurther) {
    244                     return Intents.RESULT_SMS_HANDLED;
    245                 }
    246             } catch (RemoteException e) {
    247                 if (DBG) Rlog.w(LOG_TAG, "remote func failed...");
    248             }
    249         }
    250         if (DBG) Rlog.v(LOG_TAG, "fall back to existing handler");
    251 
    252         if (mimeType == null) {
    253             if (DBG) Rlog.w(LOG_TAG, "Header Content-Type error.");
    254             return Intents.RESULT_SMS_GENERIC_ERROR;
    255         }
    256 
    257         String permission;
    258         int appOp;
    259 
    260         if (mimeType.equals(WspTypeDecoder.CONTENT_TYPE_B_MMS)) {
    261             permission = android.Manifest.permission.RECEIVE_MMS;
    262             appOp = AppOpsManager.OP_RECEIVE_MMS;
    263         } else {
    264             permission = android.Manifest.permission.RECEIVE_WAP_PUSH;
    265             appOp = AppOpsManager.OP_RECEIVE_WAP_PUSH;
    266         }
    267 
    268         Intent intent = new Intent(Intents.WAP_PUSH_RECEIVED_ACTION);
    269         intent.setType(mimeType);
    270         intent.putExtra("transactionId", transactionId);
    271         intent.putExtra("pduType", pduType);
    272         intent.putExtra("header", header);
    273         intent.putExtra("data", intentData);
    274         intent.putExtra("contentTypeParameters", pduDecoder.getContentParameters());
    275 
    276         mSmsDispatcher.dispatch(intent, permission, appOp);
    277 
    278         return Activity.RESULT_OK;
    279     }
    280 }
    281