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