b60060611ace98d56b9e4a45cfdd38742536e1ce
[android/platform/frameworks/opt/telephony.git] / src / java / com / android / internal / telephony / WapPushOverSms.java
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         int index = 0;
94         int transactionId = pdu[index++] & 0xFF;
95         int pduType = pdu[index++] & 0xFF;
96
97         if ((pduType != WspTypeDecoder.PDU_TYPE_PUSH) &&
98                 (pduType != WspTypeDecoder.PDU_TYPE_CONFIRMED_PUSH)) {
99             if (DBG) Rlog.w(TAG, "Received non-PUSH WAP PDU. Type = " + pduType);
100             return Intents.RESULT_SMS_HANDLED;
101         }
102
103         WspTypeDecoder pduDecoder = new WspTypeDecoder(pdu);
104
105         /**
106          * Parse HeaderLen(unsigned integer).
107          * From wap-230-wsp-20010705-a section 8.1.2
108          * The maximum size of a uintvar is 32 bits.
109          * So it will be encoded in no more than 5 octets.
110          */
111         if (pduDecoder.decodeUintvarInteger(index) == false) {
112             if (DBG) Rlog.w(TAG, "Received PDU. Header Length error.");
113             return Intents.RESULT_SMS_GENERIC_ERROR;
114         }
115         int headerLength = (int) pduDecoder.getValue32();
116         index += pduDecoder.getDecodedDataLength();
117
118         int headerStartIndex = index;
119
120         /**
121          * Parse Content-Type.
122          * From wap-230-wsp-20010705-a section 8.4.2.24
123          *
124          * Content-type-value = Constrained-media | Content-general-form
125          * Content-general-form = Value-length Media-type
126          * Media-type = (Well-known-media | Extension-Media) *(Parameter)
127          * Value-length = Short-length | (Length-quote Length)
128          * Short-length = <Any octet 0-30>   (octet <= WAP_PDU_SHORT_LENGTH_MAX)
129          * Length-quote = <Octet 31>         (WAP_PDU_LENGTH_QUOTE)
130          * Length = Uintvar-integer
131          */
132         if (pduDecoder.decodeContentType(index) == false) {
133             if (DBG) Rlog.w(TAG, "Received PDU. Header Content-Type error.");
134             return Intents.RESULT_SMS_GENERIC_ERROR;
135         }
136
137         String mimeType = pduDecoder.getValueString();
138         long binaryContentType = pduDecoder.getValue32();
139         index += pduDecoder.getDecodedDataLength();
140
141         byte[] header = new byte[headerLength];
142         System.arraycopy(pdu, headerStartIndex, header, 0, header.length);
143
144         byte[] intentData;
145
146         if (mimeType != null && mimeType.equals(WspTypeDecoder.CONTENT_TYPE_B_PUSH_CO)) {
147             intentData = pdu;
148         } else {
149             int dataIndex = headerStartIndex + headerLength;
150             intentData = new byte[pdu.length - dataIndex];
151             System.arraycopy(pdu, dataIndex, intentData, 0, intentData.length);
152         }
153
154         /**
155          * Seek for application ID field in WSP header.
156          * If application ID is found, WapPushManager substitute the message
157          * processing. Since WapPushManager is optional module, if WapPushManager
158          * is not found, legacy message processing will be continued.
159          */
160         if (pduDecoder.seekXWapApplicationId(index, index + headerLength - 1)) {
161             index = (int) pduDecoder.getValue32();
162             pduDecoder.decodeXWapApplicationId(index);
163             String wapAppId = pduDecoder.getValueString();
164             if (wapAppId == null) {
165                 wapAppId = Integer.toString((int) pduDecoder.getValue32());
166             }
167
168             String contentType = ((mimeType == null) ?
169                                   Long.toString(binaryContentType) : mimeType);
170             if (DBG) Rlog.v(TAG, "appid found: " + wapAppId + ":" + contentType);
171
172             try {
173                 boolean processFurther = true;
174                 IWapPushManager wapPushMan = mWapPushManager;
175
176                 if (wapPushMan == null) {
177                     if (DBG) Rlog.w(TAG, "wap push manager not found!");
178                 } else {
179                     Intent intent = new Intent();
180                     intent.putExtra("transactionId", transactionId);
181                     intent.putExtra("pduType", pduType);
182                     intent.putExtra("header", header);
183                     intent.putExtra("data", intentData);
184                     intent.putExtra("contentTypeParameters",
185                             pduDecoder.getContentParameters());
186
187                     int procRet = wapPushMan.processMessage(wapAppId, contentType, intent);
188                     if (DBG) Rlog.v(TAG, "procRet:" + procRet);
189                     if ((procRet & WapPushManagerParams.MESSAGE_HANDLED) > 0
190                         && (procRet & WapPushManagerParams.FURTHER_PROCESSING) == 0) {
191                         processFurther = false;
192                     }
193                 }
194                 if (!processFurther) {
195                     return Intents.RESULT_SMS_HANDLED;
196                 }
197             } catch (RemoteException e) {
198                 if (DBG) Rlog.w(TAG, "remote func failed...");
199             }
200         }
201         if (DBG) Rlog.v(TAG, "fall back to existing handler");
202
203         if (mimeType == null) {
204             if (DBG) Rlog.w(TAG, "Header Content-Type error.");
205             return Intents.RESULT_SMS_GENERIC_ERROR;
206         }
207
208         String permission;
209         int appOp;
210
211         if (mimeType.equals(WspTypeDecoder.CONTENT_TYPE_B_MMS)) {
212             permission = android.Manifest.permission.RECEIVE_MMS;
213             appOp = AppOpsManager.OP_RECEIVE_MMS;
214         } else {
215             permission = android.Manifest.permission.RECEIVE_WAP_PUSH;
216             appOp = AppOpsManager.OP_RECEIVE_WAP_PUSH;
217         }
218
219         Intent intent = new Intent(Intents.WAP_PUSH_DELIVER_ACTION);
220         intent.setType(mimeType);
221         intent.putExtra("transactionId", transactionId);
222         intent.putExtra("pduType", pduType);
223         intent.putExtra("header", header);
224         intent.putExtra("data", intentData);
225         intent.putExtra("contentTypeParameters", pduDecoder.getContentParameters());
226
227         // Direct the intent to only the default MMS app. If we can't find a default MMS app
228         // then sent it to all broadcast receivers.
229         ComponentName componentName = SmsApplication.getDefaultMmsApplication(mContext, true);
230         if (componentName != null) {
231             // Deliver MMS message only to this receiver
232             intent.setComponent(componentName);
233             if (DBG) Rlog.v(TAG, "Delivering MMS to: " + componentName.getPackageName() +
234                   " " + componentName.getClassName());
235         }
236
237         handler.dispatchIntent(intent, permission, appOp, receiver);
238         return Activity.RESULT_OK;
239     }
240 }