WapPushOverSms.java revision 0d4bcdf379842af4b6304809156971e926f374f0
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
17package com.android.internal.telephony;
18
19import android.app.Activity;
20import android.app.AppOpsManager;
21import android.content.BroadcastReceiver;
22import android.content.ComponentName;
23import android.content.Context;
24import android.content.Intent;
25import android.content.ServiceConnection;
26import android.os.IBinder;
27import android.os.RemoteException;
28import android.provider.Telephony.Sms.Intents;
29import android.telephony.Rlog;
30
31import com.android.internal.telephony.uicc.IccUtils;
32
33/**
34 * WAP push handler class.
35 *
36 * @hide
37 */
38public 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        if (!context.bindService(new Intent(IWapPushManager.class.getName()),
62                this, Context.BIND_AUTO_CREATE)) {
63            Rlog.e(TAG, "bindService() for IWapPushManager failed");
64        }
65    }
66
67    void dispose() {
68        mContext.unbindService(this);
69    }
70
71    /**
72     * Dispatches inbound messages that are in the WAP PDU format. See
73     * wap-230-wsp-20010705-a section 8 for details on the WAP PDU format.
74     *
75     * @param pdu The WAP PDU, made up of one or more SMS PDUs
76     * @return a result code from {@link android.provider.Telephony.Sms.Intents}, or
77     *         {@link Activity#RESULT_OK} if the message has been broadcast
78     *         to applications
79     */
80    public int dispatchWapPdu(byte[] pdu, BroadcastReceiver receiver, InboundSmsHandler handler) {
81
82        if (DBG) Rlog.d(TAG, "Rx: " + IccUtils.bytesToHexString(pdu));
83
84        int index = 0;
85        int transactionId = pdu[index++] & 0xFF;
86        int pduType = pdu[index++] & 0xFF;
87
88        if ((pduType != WspTypeDecoder.PDU_TYPE_PUSH) &&
89                (pduType != WspTypeDecoder.PDU_TYPE_CONFIRMED_PUSH)) {
90            if (DBG) Rlog.w(TAG, "Received non-PUSH WAP PDU. Type = " + pduType);
91            return Intents.RESULT_SMS_HANDLED;
92        }
93
94        WspTypeDecoder pduDecoder = new WspTypeDecoder(pdu);
95
96        /**
97         * Parse HeaderLen(unsigned integer).
98         * From wap-230-wsp-20010705-a section 8.1.2
99         * The maximum size of a uintvar is 32 bits.
100         * So it will be encoded in no more than 5 octets.
101         */
102        if (pduDecoder.decodeUintvarInteger(index) == false) {
103            if (DBG) Rlog.w(TAG, "Received PDU. Header Length error.");
104            return Intents.RESULT_SMS_GENERIC_ERROR;
105        }
106        int headerLength = (int) pduDecoder.getValue32();
107        index += pduDecoder.getDecodedDataLength();
108
109        int headerStartIndex = index;
110
111        /**
112         * Parse Content-Type.
113         * From wap-230-wsp-20010705-a section 8.4.2.24
114         *
115         * Content-type-value = Constrained-media | Content-general-form
116         * Content-general-form = Value-length Media-type
117         * Media-type = (Well-known-media | Extension-Media) *(Parameter)
118         * Value-length = Short-length | (Length-quote Length)
119         * Short-length = <Any octet 0-30>   (octet <= WAP_PDU_SHORT_LENGTH_MAX)
120         * Length-quote = <Octet 31>         (WAP_PDU_LENGTH_QUOTE)
121         * Length = Uintvar-integer
122         */
123        if (pduDecoder.decodeContentType(index) == false) {
124            if (DBG) Rlog.w(TAG, "Received PDU. Header Content-Type error.");
125            return Intents.RESULT_SMS_GENERIC_ERROR;
126        }
127
128        String mimeType = pduDecoder.getValueString();
129        long binaryContentType = pduDecoder.getValue32();
130        index += pduDecoder.getDecodedDataLength();
131
132        byte[] header = new byte[headerLength];
133        System.arraycopy(pdu, headerStartIndex, header, 0, header.length);
134
135        byte[] intentData;
136
137        if (mimeType != null && mimeType.equals(WspTypeDecoder.CONTENT_TYPE_B_PUSH_CO)) {
138            intentData = pdu;
139        } else {
140            int dataIndex = headerStartIndex + headerLength;
141            intentData = new byte[pdu.length - dataIndex];
142            System.arraycopy(pdu, dataIndex, intentData, 0, intentData.length);
143        }
144
145        /**
146         * Seek for application ID field in WSP header.
147         * If application ID is found, WapPushManager substitute the message
148         * processing. Since WapPushManager is optional module, if WapPushManager
149         * is not found, legacy message processing will be continued.
150         */
151        if (pduDecoder.seekXWapApplicationId(index, index + headerLength - 1)) {
152            index = (int) pduDecoder.getValue32();
153            pduDecoder.decodeXWapApplicationId(index);
154            String wapAppId = pduDecoder.getValueString();
155            if (wapAppId == null) {
156                wapAppId = Integer.toString((int) pduDecoder.getValue32());
157            }
158
159            String contentType = ((mimeType == null) ?
160                                  Long.toString(binaryContentType) : mimeType);
161            if (DBG) Rlog.v(TAG, "appid found: " + wapAppId + ":" + contentType);
162
163            try {
164                boolean processFurther = true;
165                IWapPushManager wapPushMan = mWapPushManager;
166
167                if (wapPushMan == null) {
168                    if (DBG) Rlog.w(TAG, "wap push manager not found!");
169                } else {
170                    Intent intent = new Intent();
171                    intent.putExtra("transactionId", transactionId);
172                    intent.putExtra("pduType", pduType);
173                    intent.putExtra("header", header);
174                    intent.putExtra("data", intentData);
175                    intent.putExtra("contentTypeParameters",
176                            pduDecoder.getContentParameters());
177
178                    int procRet = wapPushMan.processMessage(wapAppId, contentType, intent);
179                    if (DBG) Rlog.v(TAG, "procRet:" + procRet);
180                    if ((procRet & WapPushManagerParams.MESSAGE_HANDLED) > 0
181                        && (procRet & WapPushManagerParams.FURTHER_PROCESSING) == 0) {
182                        processFurther = false;
183                    }
184                }
185                if (!processFurther) {
186                    return Intents.RESULT_SMS_HANDLED;
187                }
188            } catch (RemoteException e) {
189                if (DBG) Rlog.w(TAG, "remote func failed...");
190            }
191        }
192        if (DBG) Rlog.v(TAG, "fall back to existing handler");
193
194        if (mimeType == null) {
195            if (DBG) Rlog.w(TAG, "Header Content-Type error.");
196            return Intents.RESULT_SMS_GENERIC_ERROR;
197        }
198
199        String permission;
200        int appOp;
201
202        if (mimeType.equals(WspTypeDecoder.CONTENT_TYPE_B_MMS)) {
203            permission = android.Manifest.permission.RECEIVE_MMS;
204            appOp = AppOpsManager.OP_RECEIVE_MMS;
205        } else {
206            permission = android.Manifest.permission.RECEIVE_WAP_PUSH;
207            appOp = AppOpsManager.OP_RECEIVE_WAP_PUSH;
208        }
209
210        Intent intent = new Intent(Intents.WAP_PUSH_RECEIVED_ACTION);
211        intent.setType(mimeType);
212        intent.putExtra("transactionId", transactionId);
213        intent.putExtra("pduType", pduType);
214        intent.putExtra("header", header);
215        intent.putExtra("data", intentData);
216        intent.putExtra("contentTypeParameters", pduDecoder.getContentParameters());
217
218        handler.dispatchIntent(intent, permission, appOp, receiver);
219        return Activity.RESULT_OK;
220    }
221}
222