1/* 2 * Copyright (C) 2006 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.gsm; 18 19import android.os.*; 20import android.text.util.Regex; 21import android.util.EventLog; 22import android.util.Log; 23 24import com.android.internal.telephony.CommandException; 25import com.android.internal.telephony.CommandsInterface; 26import com.android.internal.telephony.DataConnection; 27import com.android.internal.telephony.DataLink; 28import com.android.internal.telephony.Phone; 29import com.android.internal.telephony.RILConstants; 30import com.android.internal.telephony.TelephonyEventLog; 31 32/** 33 * {@hide} 34 */ 35public class PdpConnection extends DataConnection { 36 37 private static final String LOG_TAG = "GSM"; 38 private static final boolean DBG = true; 39 40 /** Fail cause of last PDP activate, from RIL_LastPDPActivateFailCause */ 41 private static final int PDP_FAIL_OPERATOR_BARRED = 0x08; 42 private static final int PDP_FAIL_INSUFFICIENT_RESOURCES = 0x1A; 43 private static final int PDP_FAIL_MISSING_UKNOWN_APN = 0x1B; 44 private static final int PDP_FAIL_UNKNOWN_PDP_ADDRESS_TYPE = 0x1C; 45 private static final int PDP_FAIL_USER_AUTHENTICATION = 0x1D; 46 private static final int PDP_FAIL_ACTIVATION_REJECT_GGSN = 0x1E; 47 private static final int PDP_FAIL_ACTIVATION_REJECT_UNSPECIFIED = 0x1F; 48 private static final int PDP_FAIL_SERVICE_OPTION_NOT_SUPPORTED = 0x20; 49 private static final int PDP_FAIL_SERVICE_OPTION_NOT_SUBSCRIBED = 0x21; 50 private static final int PDP_FAIL_SERVICE_OPTION_OUT_OF_ORDER = 0x22; 51 private static final int PDP_FAIL_NSAPI_IN_USE = 0x23; 52 private static final int PDP_FAIL_PROTOCOL_ERRORS = 0x6F; 53 private static final int PDP_FAIL_ERROR_UNSPECIFIED = 0xffff; 54 55 private static final int PDP_FAIL_REGISTRATION_FAIL = -1; 56 private static final int PDP_FAIL_GPRS_REGISTRATION_FAIL = -2; 57 58 //***** Instance Variables 59 private String pdp_name; 60 private ApnSetting apn; 61 62 //***** Constructor 63 PdpConnection(GSMPhone phone) { 64 super(phone); 65 } 66 67 /** 68 * Setup PDP connection for provided apn 69 * @param apn for this connection 70 * @param onCompleted notify success or not after down 71 */ 72 void connect(ApnSetting apn, Message onCompleted) { 73 if (DBG) log("Connecting to carrier: '" + apn.carrier 74 + "' APN: '" + apn.apn 75 + "' proxy: '" + apn.proxy + "' port: '" + apn.port); 76 77 setHttpProxy (apn.proxy, apn.port); 78 79 state = State.ACTIVATING; 80 this.apn = apn; 81 onConnectCompleted = onCompleted; 82 createTime = -1; 83 lastFailTime = -1; 84 lastFailCause = FailCause.NONE; 85 receivedDisconnectReq = false; 86 87 int authType = apn.authType; 88 if (authType == -1) { 89 authType = (apn.user != null) ? RILConstants.SETUP_DATA_AUTH_PAP_CHAP : 90 RILConstants.SETUP_DATA_AUTH_NONE; 91 } 92 phone.mCM.setupDataCall(Integer.toString(RILConstants.SETUP_DATA_TECH_GSM), 93 Integer.toString(RILConstants.DATA_PROFILE_DEFAULT), apn.apn, apn.user, 94 apn.password, Integer.toString(authType), 95 obtainMessage(EVENT_SETUP_DATA_CONNECTION_DONE)); 96 } 97 98 private void tearDownData(Message msg) { 99 if (phone.mCM.getRadioState().isOn()) { 100 phone.mCM.deactivateDataCall(cid, obtainMessage(EVENT_DEACTIVATE_DONE, msg)); 101 } 102 } 103 104 protected void disconnect(Message msg) { 105 onDisconnect = msg; 106 if (state == State.ACTIVE) { 107 tearDownData(msg); 108 } else if (state == State.ACTIVATING) { 109 receivedDisconnectReq = true; 110 } else { 111 // state == INACTIVE. Nothing to do, so notify immediately. 112 notifyDisconnect(msg); 113 } 114 } 115 116 public void clearSettings() { 117 super.clearSettings(); 118 apn = null; 119 } 120 121 public String toString() { 122 return "State=" + state + " Apn=" + apn + 123 " create=" + createTime + " lastFail=" + lastFailTime + 124 " lastFailCause=" + lastFailCause; 125 } 126 127 128 protected void notifyFail(FailCause cause, Message onCompleted) { 129 if (onCompleted == null) return; 130 131 state = State.INACTIVE; 132 lastFailCause = cause; 133 lastFailTime = System.currentTimeMillis(); 134 onConnectCompleted = null; 135 136 if (DBG) { 137 log("Notify PDP fail at " + lastFailTime + 138 " due to " + lastFailCause); 139 } 140 141 AsyncResult.forMessage(onCompleted, cause, new Exception()); 142 onCompleted.sendToTarget(); 143 } 144 145 protected void notifySuccess(Message onCompleted) { 146 if (onCompleted == null) { 147 return; 148 } 149 150 state = State.ACTIVE; 151 createTime = System.currentTimeMillis(); 152 onConnectCompleted = null; 153 onCompleted.arg1 = cid; 154 155 if (DBG) log("Notify PDP success at " + createTime); 156 157 AsyncResult.forMessage(onCompleted); 158 onCompleted.sendToTarget(); 159 } 160 161 protected void notifyDisconnect(Message msg) { 162 if (DBG) log("Notify PDP disconnect"); 163 164 if (msg != null) { 165 AsyncResult.forMessage(msg); 166 msg.sendToTarget(); 167 } 168 clearSettings(); 169 } 170 171 protected void onLinkStateChanged(DataLink.LinkState linkState) { 172 switch (linkState) { 173 case LINK_UP: 174 notifySuccess(onConnectCompleted); 175 break; 176 177 case LINK_DOWN: 178 case LINK_EXITED: 179 phone.mCM.getLastPdpFailCause( 180 obtainMessage (EVENT_GET_LAST_FAIL_DONE)); 181 break; 182 } 183 } 184 185 protected FailCause getFailCauseFromRequest(int rilCause) { 186 FailCause cause; 187 188 switch (rilCause) { 189 case PDP_FAIL_OPERATOR_BARRED: 190 cause = FailCause.OPERATOR_BARRED; 191 break; 192 case PDP_FAIL_INSUFFICIENT_RESOURCES: 193 cause = FailCause.INSUFFICIENT_RESOURCES; 194 break; 195 case PDP_FAIL_MISSING_UKNOWN_APN: 196 cause = FailCause.MISSING_UKNOWN_APN; 197 break; 198 case PDP_FAIL_UNKNOWN_PDP_ADDRESS_TYPE: 199 cause = FailCause.UNKNOWN_PDP_ADDRESS; 200 break; 201 case PDP_FAIL_USER_AUTHENTICATION: 202 cause = FailCause.USER_AUTHENTICATION; 203 break; 204 case PDP_FAIL_ACTIVATION_REJECT_GGSN: 205 cause = FailCause.ACTIVATION_REJECT_GGSN; 206 break; 207 case PDP_FAIL_ACTIVATION_REJECT_UNSPECIFIED: 208 cause = FailCause.ACTIVATION_REJECT_UNSPECIFIED; 209 break; 210 case PDP_FAIL_SERVICE_OPTION_OUT_OF_ORDER: 211 cause = FailCause.SERVICE_OPTION_OUT_OF_ORDER; 212 break; 213 case PDP_FAIL_SERVICE_OPTION_NOT_SUPPORTED: 214 cause = FailCause.SERVICE_OPTION_NOT_SUPPORTED; 215 break; 216 case PDP_FAIL_SERVICE_OPTION_NOT_SUBSCRIBED: 217 cause = FailCause.SERVICE_OPTION_NOT_SUBSCRIBED; 218 break; 219 case PDP_FAIL_NSAPI_IN_USE: 220 cause = FailCause.NSAPI_IN_USE; 221 break; 222 case PDP_FAIL_PROTOCOL_ERRORS: 223 cause = FailCause.PROTOCOL_ERRORS; 224 break; 225 case PDP_FAIL_ERROR_UNSPECIFIED: 226 cause = FailCause.UNKNOWN; 227 break; 228 case PDP_FAIL_REGISTRATION_FAIL: 229 cause = FailCause.REGISTRATION_FAIL; 230 break; 231 case PDP_FAIL_GPRS_REGISTRATION_FAIL: 232 cause = FailCause.GPRS_REGISTRATION_FAIL; 233 break; 234 default: 235 cause = FailCause.UNKNOWN; 236 } 237 return cause; 238 } 239 240 protected void log(String s) { 241 Log.d(LOG_TAG, "[PdpConnection] " + s); 242 } 243 244 @Override 245 protected void onDeactivated(AsyncResult ar) { 246 notifyDisconnect((Message) ar.userObj); 247 if (DBG) log("PDP Connection Deactivated"); 248 } 249 250 @Override 251 protected void onSetupConnectionCompleted(AsyncResult ar) { 252 if (ar.exception != null) { 253 Log.e(LOG_TAG, "PDP Context Init failed " + ar.exception); 254 255 if (receivedDisconnectReq) { 256 // Don't bother reporting the error if there's already a 257 // pending disconnect request, since DataConnectionTracker 258 // has already updated its state. 259 notifyDisconnect(onDisconnect); 260 } else { 261 if ( ar.exception instanceof CommandException && 262 ((CommandException) (ar.exception)).getCommandError() 263 == CommandException.Error.RADIO_NOT_AVAILABLE) { 264 notifyFail(FailCause.RADIO_NOT_AVAILABLE, 265 onConnectCompleted); 266 } else { 267 phone.mCM.getLastPdpFailCause( 268 obtainMessage(EVENT_GET_LAST_FAIL_DONE)); 269 } 270 } 271 } else { 272 if (receivedDisconnectReq) { 273 // Don't bother reporting success if there's already a 274 // pending disconnect request, since DataConnectionTracker 275 // has already updated its state. 276 tearDownData(onDisconnect); 277 } else { 278 String[] response = ((String[]) ar.result); 279 cid = Integer.parseInt(response[0]); 280 281 if (response.length > 2) { 282 interfaceName = response[1]; 283 ipAddress = response[2]; 284 String prefix = "net." + interfaceName + "."; 285 gatewayAddress = SystemProperties.get(prefix + "gw"); 286 dnsServers[0] = SystemProperties.get(prefix + "dns1"); 287 dnsServers[1] = SystemProperties.get(prefix + "dns2"); 288 if (DBG) { 289 log("interface=" + interfaceName + " ipAddress=" + ipAddress 290 + " gateway=" + gatewayAddress + " DNS1=" + dnsServers[0] 291 + " DNS2=" + dnsServers[1]); 292 } 293 294 if (NULL_IP.equals(dnsServers[0]) && NULL_IP.equals(dnsServers[1]) 295 && !((GSMPhone) phone).isDnsCheckDisabled()) { 296 // Work around a race condition where QMI does not fill in DNS: 297 // Deactivate PDP and let DataConnectionTracker retry. 298 // Do not apply the race condition workaround for MMS APN 299 // if Proxy is an IP-address. 300 // Otherwise, the default APN will not be restored anymore. 301 if (!apn.types[0].equals(Phone.APN_TYPE_MMS) 302 || !isIpAddress(apn.mmsProxy)) { 303 EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_BAD_DNS_ADDRESS, 304 dnsServers[0]); 305 phone.mCM.deactivateDataCall(cid, obtainMessage(EVENT_FORCE_RETRY)); 306 return; 307 } 308 } 309 } 310 311 onLinkStateChanged(DataLink.LinkState.LINK_UP); 312 313 if (DBG) log("PDP setup on cid = " + cid); 314 } 315 } 316 } 317 318 private boolean isIpAddress(String address) { 319 if (address == null) return false; 320 321 return Regex.IP_ADDRESS_PATTERN.matcher(apn.mmsProxy).matches(); 322 } 323 324 public ApnSetting getApn() { 325 return this.apn; 326 } 327} 328