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