WapPushOverSms.java revision d720945f2be5ea5fe0faf67e67d9ea0e184eba67
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 18package com.android.internal.telephony; 19 20import android.app.Activity; 21import android.content.Context; 22import android.content.ComponentName; 23import android.content.Intent; 24import android.content.ServiceConnection; 25import android.provider.Telephony; 26import android.provider.Telephony.Sms.Intents; 27import android.telephony.Rlog; 28import android.os.IBinder; 29import android.os.RemoteException; 30 31import com.android.internal.telephony.uicc.IccUtils; 32 33/** 34 * WAP push handler class. 35 * 36 * @hide 37 */ 38public class WapPushOverSms { 39 private static final String LOG_TAG = "WAP PUSH"; 40 41 private final Context mContext; 42 private WspTypeDecoder pduDecoder; 43 private SMSDispatcher mSmsDispatcher; 44 45 /** 46 * Hold the wake lock for 5 seconds, which should be enough time for 47 * any receiver(s) to grab its own wake lock. 48 */ 49 private final int WAKE_LOCK_TIMEOUT = 5000; 50 51 private final int BIND_RETRY_INTERVAL = 1000; 52 /** 53 * A handle to WapPushManager interface 54 */ 55 private WapPushConnection mWapConn = null; 56 private class WapPushConnection implements ServiceConnection { 57 private IWapPushManager mWapPushMan; 58 private Context mOwner; 59 60 public WapPushConnection(Context ownerContext) { 61 mOwner = ownerContext; 62 } 63 64 public void onServiceConnected(ComponentName name, IBinder service) { 65 mWapPushMan = IWapPushManager.Stub.asInterface(service); 66 if (false) Rlog.v(LOG_TAG, "wappush manager connected to " + 67 mOwner.hashCode()); 68 } 69 70 public void onServiceDisconnected(ComponentName name) { 71 mWapPushMan = null; 72 if (false) Rlog.v(LOG_TAG, "wappush manager disconnected."); 73 // WapPushManager must be always attached. 74 rebindWapPushManager(); 75 } 76 77 /** 78 * bind WapPushManager 79 */ 80 public void bindWapPushManager() { 81 if (mWapPushMan != null) return; 82 83 final ServiceConnection wapPushConnection = this; 84 85 mOwner.bindService(new Intent(IWapPushManager.class.getName()), 86 wapPushConnection, Context.BIND_AUTO_CREATE); 87 } 88 89 /** 90 * rebind WapPushManager 91 * This method is called when WapPushManager is disconnected unexpectedly. 92 */ 93 private void rebindWapPushManager() { 94 if (mWapPushMan != null) return; 95 96 final ServiceConnection wapPushConnection = this; 97 new Thread() { 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 (false) 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 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 (false) 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 (false) 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 (false) 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 (false) 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 (false) 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 (false) 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 (false) 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 (false) Rlog.w(LOG_TAG, "remote func failed..."); 248 } 249 } 250 if (false) Rlog.v(LOG_TAG, "fall back to existing handler"); 251 252 if (mimeType == null) { 253 if (false) Rlog.w(LOG_TAG, "Header Content-Type error."); 254 return Intents.RESULT_SMS_GENERIC_ERROR; 255 } 256 257 String permission; 258 259 if (mimeType.equals(WspTypeDecoder.CONTENT_TYPE_B_MMS)) { 260 permission = "android.permission.RECEIVE_MMS"; 261 } else { 262 permission = "android.permission.RECEIVE_WAP_PUSH"; 263 } 264 265 Intent intent = new Intent(Intents.WAP_PUSH_RECEIVED_ACTION); 266 intent.setType(mimeType); 267 intent.putExtra("transactionId", transactionId); 268 intent.putExtra("pduType", pduType); 269 intent.putExtra("header", header); 270 intent.putExtra("data", intentData); 271 intent.putExtra("contentTypeParameters", pduDecoder.getContentParameters()); 272 273 mSmsDispatcher.dispatch(intent, permission); 274 275 return Activity.RESULT_OK; 276 } 277} 278