1 /* 2 * Copyright (C) 2019 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.dataconnection; 18 19 import android.annotation.NonNull; 20 import android.net.LinkProperties; 21 import android.net.NattKeepalivePacketData; 22 import android.net.NetworkAgent; 23 import android.net.NetworkCapabilities; 24 import android.net.NetworkInfo; 25 import android.net.NetworkMisc; 26 import android.net.SocketKeepalive; 27 import android.os.Message; 28 import android.telephony.AccessNetworkConstants; 29 import android.telephony.AccessNetworkConstants.TransportType; 30 import android.telephony.Rlog; 31 import android.util.LocalLog; 32 import android.util.SparseArray; 33 34 import com.android.internal.telephony.DctConstants; 35 import com.android.internal.telephony.Phone; 36 import com.android.internal.telephony.RILConstants; 37 import com.android.internal.util.IndentingPrintWriter; 38 39 import java.io.FileDescriptor; 40 import java.io.PrintWriter; 41 import java.util.concurrent.atomic.AtomicInteger; 42 43 /** 44 * This class represents a network agent which is communication channel between 45 * {@link DataConnection} and {@link com.android.server.ConnectivityService}. The agent is 46 * created when data connection enters {@link DataConnection.DcActiveState} until it exits that 47 * state. 48 * 49 * Note that in IWLAN handover scenario, this agent could be transferred to the new 50 * {@link DataConnection} so for a short window of time this object might be accessed by two 51 * different {@link DataConnection}. Thus each method in this class needs to be synchronized. 52 */ 53 public class DcNetworkAgent extends NetworkAgent { 54 private String mTag; 55 56 private Phone mPhone; 57 58 private int mTransportType; 59 60 private NetworkCapabilities mNetworkCapabilities; 61 62 public final DcKeepaliveTracker keepaliveTracker = new DcKeepaliveTracker(); 63 64 private DataConnection mDataConnection; 65 66 private final LocalLog mNetCapsLocalLog = new LocalLog(50); 67 68 private static AtomicInteger sSerialNumber = new AtomicInteger(0); 69 DcNetworkAgent(DataConnection dc, String tag, Phone phone, NetworkInfo ni, int score, NetworkMisc misc, int factorySerialNumber, int transportType)70 private DcNetworkAgent(DataConnection dc, String tag, Phone phone, NetworkInfo ni, 71 int score, NetworkMisc misc, int factorySerialNumber, 72 int transportType) { 73 super(dc.getHandler().getLooper(), phone.getContext(), tag, ni, 74 dc.getNetworkCapabilities(), dc.getLinkProperties(), score, misc, 75 factorySerialNumber); 76 mTag = tag; 77 mPhone = phone; 78 mNetworkCapabilities = dc.getNetworkCapabilities(); 79 mTransportType = transportType; 80 mDataConnection = dc; 81 logd(tag + " created for data connection " + dc.getName()); 82 } 83 84 /** 85 * Constructor 86 * 87 * @param dc The data connection owns this network agent. 88 * @param phone The phone object. 89 * @param ni Network info. 90 * @param score Score of the data connection. 91 * @param misc The miscellaneous information of the data connection. 92 * @param factorySerialNumber Serial number of telephony network factory. 93 * @param transportType The transport of the data connection. 94 * @return The network agent 95 */ createDcNetworkAgent(DataConnection dc, Phone phone, NetworkInfo ni, int score, NetworkMisc misc, int factorySerialNumber, int transportType)96 public static DcNetworkAgent createDcNetworkAgent(DataConnection dc, Phone phone, 97 NetworkInfo ni, int score, NetworkMisc misc, 98 int factorySerialNumber, int transportType) { 99 // Use serial number only. Do not use transport type because it can be transferred to 100 // a different transport. 101 String tag = "DcNetworkAgent-" + sSerialNumber.incrementAndGet(); 102 return new DcNetworkAgent(dc, tag, phone, ni, score, misc, factorySerialNumber, 103 transportType); 104 } 105 106 /** 107 * Set the data connection that owns this network agent. 108 * 109 * @param dc Data connection owning this network agent. 110 * @param transportType Transport that this data connection is on. 111 */ acquireOwnership(@onNull DataConnection dc, @TransportType int transportType)112 public synchronized void acquireOwnership(@NonNull DataConnection dc, 113 @TransportType int transportType) { 114 mDataConnection = dc; 115 mTransportType = transportType; 116 logd(dc.getName() + " acquired the ownership of this agent."); 117 } 118 119 /** 120 * Release the ownership of network agent. 121 */ releaseOwnership(DataConnection dc)122 public synchronized void releaseOwnership(DataConnection dc) { 123 if (mDataConnection == null) { 124 loge("releaseOwnership called on no-owner DcNetworkAgent!"); 125 return; 126 } else if (mDataConnection != dc) { 127 log("releaseOwnership: This agent belongs to " 128 + mDataConnection.getName() + ", ignored the request from " + dc.getName()); 129 return; 130 } 131 logd("Data connection " + mDataConnection.getName() + " released the ownership."); 132 mDataConnection = null; 133 } 134 135 @Override unwanted()136 protected synchronized void unwanted() { 137 if (mDataConnection == null) { 138 loge("Unwanted found called on no-owner DcNetworkAgent!"); 139 return; 140 } 141 142 logd("unwanted called. Now tear down the data connection " 143 + mDataConnection.getName()); 144 mDataConnection.tearDownAll(Phone.REASON_RELEASED_BY_CONNECTIVITY_SERVICE, 145 DcTracker.RELEASE_TYPE_DETACH, null); 146 } 147 148 @Override pollLceData()149 protected synchronized void pollLceData() { 150 if (mDataConnection == null) { 151 loge("pollLceData called on no-owner DcNetworkAgent!"); 152 return; 153 } 154 155 if (mPhone.getLceStatus() == RILConstants.LCE_ACTIVE // active LCE service 156 && mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) { 157 mPhone.mCi.pullLceData(mDataConnection.obtainMessage( 158 DataConnection.EVENT_BW_REFRESH_RESPONSE)); 159 } 160 } 161 162 @Override networkStatus(int status, String redirectUrl)163 protected synchronized void networkStatus(int status, String redirectUrl) { 164 if (mDataConnection == null) { 165 loge("networkStatus called on no-owner DcNetworkAgent!"); 166 return; 167 } 168 169 logd("validation status: " + status + " with redirection URL: " + redirectUrl); 170 DcTracker dct = mPhone.getDcTracker(mTransportType); 171 if (dct != null) { 172 Message msg = dct.obtainMessage(DctConstants.EVENT_NETWORK_STATUS_CHANGED, 173 status, 0, redirectUrl); 174 msg.sendToTarget(); 175 } 176 } 177 178 /** 179 * Set the network capabilities. 180 * 181 * @param networkCapabilities The network capabilities. 182 * @param dc The data connection that invokes this method. 183 */ sendNetworkCapabilities(NetworkCapabilities networkCapabilities, DataConnection dc)184 public synchronized void sendNetworkCapabilities(NetworkCapabilities networkCapabilities, 185 DataConnection dc) { 186 if (mDataConnection == null) { 187 loge("sendNetworkCapabilities called on no-owner DcNetworkAgent!"); 188 return; 189 } else if (mDataConnection != dc) { 190 loge("sendNetworkCapabilities: This agent belongs to " 191 + mDataConnection.getName() + ", ignored the request from " + dc.getName()); 192 return; 193 } 194 195 if (!networkCapabilities.equals(mNetworkCapabilities)) { 196 String logStr = "Changed from " + mNetworkCapabilities + " to " 197 + networkCapabilities + ", Data RAT=" 198 + mPhone.getServiceState().getRilDataRadioTechnology() 199 + ", dc=" + mDataConnection.getName(); 200 logd(logStr); 201 mNetCapsLocalLog.log(logStr); 202 mNetworkCapabilities = networkCapabilities; 203 } 204 sendNetworkCapabilities(networkCapabilities); 205 } 206 207 /** 208 * Set the link properties 209 * 210 * @param linkProperties The link properties 211 * @param dc The data connection that invokes this method. 212 */ sendLinkProperties(LinkProperties linkProperties, DataConnection dc)213 public synchronized void sendLinkProperties(LinkProperties linkProperties, 214 DataConnection dc) { 215 if (mDataConnection == null) { 216 loge("sendLinkProperties called on no-owner DcNetworkAgent!"); 217 return; 218 } else if (mDataConnection != dc) { 219 loge("sendLinkProperties: This agent belongs to " 220 + mDataConnection.getName() + ", ignored the request from " + dc.getName()); 221 return; 222 } 223 sendLinkProperties(linkProperties); 224 } 225 226 /** 227 * Set the network score. 228 * 229 * @param score The network score. 230 * @param dc The data connection that invokes this method. 231 */ sendNetworkScore(int score, DataConnection dc)232 public synchronized void sendNetworkScore(int score, DataConnection dc) { 233 if (mDataConnection == null) { 234 loge("sendNetworkScore called on no-owner DcNetworkAgent!"); 235 return; 236 } else if (mDataConnection != dc) { 237 loge("sendNetworkScore: This agent belongs to " 238 + mDataConnection.getName() + ", ignored the request from " + dc.getName()); 239 return; 240 } 241 sendNetworkScore(score); 242 } 243 244 /** 245 * Set the network info. 246 * 247 * @param networkInfo The network info. 248 * @param dc The data connection that invokes this method. 249 */ sendNetworkInfo(NetworkInfo networkInfo, DataConnection dc)250 public synchronized void sendNetworkInfo(NetworkInfo networkInfo, DataConnection dc) { 251 if (mDataConnection == null) { 252 loge("sendNetworkInfo called on no-owner DcNetworkAgent!"); 253 return; 254 } else if (mDataConnection != dc) { 255 loge("sendNetworkInfo: This agent belongs to " 256 + mDataConnection.getName() + ", ignored the request from " + dc.getName()); 257 return; 258 } 259 sendNetworkInfo(networkInfo); 260 } 261 262 @Override startSocketKeepalive(Message msg)263 protected synchronized void startSocketKeepalive(Message msg) { 264 if (mDataConnection == null) { 265 loge("startSocketKeepalive called on no-owner DcNetworkAgent!"); 266 return; 267 } 268 269 if (msg.obj instanceof NattKeepalivePacketData) { 270 mDataConnection.obtainMessage(DataConnection.EVENT_KEEPALIVE_START_REQUEST, 271 msg.arg1, msg.arg2, msg.obj).sendToTarget(); 272 } else { 273 onSocketKeepaliveEvent(msg.arg1, SocketKeepalive.ERROR_UNSUPPORTED); 274 } 275 } 276 277 @Override stopSocketKeepalive(Message msg)278 protected synchronized void stopSocketKeepalive(Message msg) { 279 if (mDataConnection == null) { 280 loge("stopSocketKeepalive called on no-owner DcNetworkAgent!"); 281 return; 282 } 283 284 mDataConnection.obtainMessage(DataConnection.EVENT_KEEPALIVE_STOP_REQUEST, 285 msg.arg1, msg.arg2, msg.obj).sendToTarget(); 286 } 287 288 @Override toString()289 public String toString() { 290 return "DcNetworkAgent:" 291 + " mDataConnection=" 292 + ((mDataConnection != null) ? mDataConnection.getName() : null) 293 + " mTransportType=" 294 + AccessNetworkConstants.transportTypeToString(mTransportType) 295 + " mNetworkCapabilities=" + mNetworkCapabilities; 296 } 297 298 /** 299 * Dump the state of transport manager 300 * 301 * @param fd File descriptor 302 * @param printWriter Print writer 303 * @param args Arguments 304 */ dump(FileDescriptor fd, PrintWriter printWriter, String[] args)305 public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) { 306 IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " "); 307 pw.println(toString()); 308 pw.increaseIndent(); 309 pw.println("Net caps logs:"); 310 mNetCapsLocalLog.dump(fd, pw, args); 311 pw.decreaseIndent(); 312 } 313 314 /** 315 * Log with debug level 316 * 317 * @param s is string log 318 */ logd(String s)319 private void logd(String s) { 320 Rlog.d(mTag, s); 321 } 322 323 /** 324 * Log with error level 325 * 326 * @param s is string log 327 */ loge(String s)328 private void loge(String s) { 329 Rlog.e(mTag, s); 330 } 331 332 class DcKeepaliveTracker { 333 private class KeepaliveRecord { 334 public int slotId; 335 public int currentStatus; 336 KeepaliveRecord(int slotId, int status)337 KeepaliveRecord(int slotId, int status) { 338 this.slotId = slotId; 339 this.currentStatus = status; 340 } 341 } 342 343 private final SparseArray<KeepaliveRecord> mKeepalives = new SparseArray(); 344 getHandleForSlot(int slotId)345 int getHandleForSlot(int slotId) { 346 for (int i = 0; i < mKeepalives.size(); i++) { 347 KeepaliveRecord kr = mKeepalives.valueAt(i); 348 if (kr.slotId == slotId) return mKeepalives.keyAt(i); 349 } 350 return -1; 351 } 352 keepaliveStatusErrorToPacketKeepaliveError(int error)353 int keepaliveStatusErrorToPacketKeepaliveError(int error) { 354 switch(error) { 355 case KeepaliveStatus.ERROR_NONE: 356 return SocketKeepalive.SUCCESS; 357 case KeepaliveStatus.ERROR_UNSUPPORTED: 358 return SocketKeepalive.ERROR_UNSUPPORTED; 359 case KeepaliveStatus.ERROR_NO_RESOURCES: 360 return SocketKeepalive.ERROR_INSUFFICIENT_RESOURCES; 361 case KeepaliveStatus.ERROR_UNKNOWN: 362 default: 363 return SocketKeepalive.ERROR_HARDWARE_ERROR; 364 } 365 } 366 handleKeepaliveStarted(final int slot, KeepaliveStatus ks)367 void handleKeepaliveStarted(final int slot, KeepaliveStatus ks) { 368 switch (ks.statusCode) { 369 case KeepaliveStatus.STATUS_INACTIVE: 370 DcNetworkAgent.this.onSocketKeepaliveEvent(slot, 371 keepaliveStatusErrorToPacketKeepaliveError(ks.errorCode)); 372 break; 373 case KeepaliveStatus.STATUS_ACTIVE: 374 DcNetworkAgent.this.onSocketKeepaliveEvent( 375 slot, SocketKeepalive.SUCCESS); 376 // fall through to add record 377 case KeepaliveStatus.STATUS_PENDING: 378 logd("Adding keepalive handle=" 379 + ks.sessionHandle + " slot = " + slot); 380 mKeepalives.put(ks.sessionHandle, 381 new KeepaliveRecord( 382 slot, ks.statusCode)); 383 break; 384 default: 385 logd("Invalid KeepaliveStatus Code: " + ks.statusCode); 386 break; 387 } 388 } 389 handleKeepaliveStatus(KeepaliveStatus ks)390 void handleKeepaliveStatus(KeepaliveStatus ks) { 391 final KeepaliveRecord kr; 392 kr = mKeepalives.get(ks.sessionHandle); 393 394 if (kr == null) { 395 // If there is no slot for the session handle, we received an event 396 // for a different data connection. This is not an error because the 397 // keepalive session events are broadcast to all listeners. 398 loge("Discarding keepalive event for different data connection:" + ks); 399 return; 400 } 401 // Switch on the current state, to see what we do with the status update 402 switch (kr.currentStatus) { 403 case KeepaliveStatus.STATUS_INACTIVE: 404 logd("Inactive Keepalive received status!"); 405 DcNetworkAgent.this.onSocketKeepaliveEvent( 406 kr.slotId, SocketKeepalive.ERROR_HARDWARE_ERROR); 407 break; 408 case KeepaliveStatus.STATUS_PENDING: 409 switch (ks.statusCode) { 410 case KeepaliveStatus.STATUS_INACTIVE: 411 DcNetworkAgent.this.onSocketKeepaliveEvent(kr.slotId, 412 keepaliveStatusErrorToPacketKeepaliveError(ks.errorCode)); 413 kr.currentStatus = KeepaliveStatus.STATUS_INACTIVE; 414 mKeepalives.remove(ks.sessionHandle); 415 break; 416 case KeepaliveStatus.STATUS_ACTIVE: 417 logd("Pending Keepalive received active status!"); 418 kr.currentStatus = KeepaliveStatus.STATUS_ACTIVE; 419 DcNetworkAgent.this.onSocketKeepaliveEvent( 420 kr.slotId, SocketKeepalive.SUCCESS); 421 break; 422 case KeepaliveStatus.STATUS_PENDING: 423 loge("Invalid unsolicied Keepalive Pending Status!"); 424 break; 425 default: 426 loge("Invalid Keepalive Status received, " + ks.statusCode); 427 } 428 break; 429 case KeepaliveStatus.STATUS_ACTIVE: 430 switch (ks.statusCode) { 431 case KeepaliveStatus.STATUS_INACTIVE: 432 logd("Keepalive received stopped status!"); 433 DcNetworkAgent.this.onSocketKeepaliveEvent( 434 kr.slotId, SocketKeepalive.SUCCESS); 435 436 kr.currentStatus = KeepaliveStatus.STATUS_INACTIVE; 437 mKeepalives.remove(ks.sessionHandle); 438 break; 439 case KeepaliveStatus.STATUS_PENDING: 440 case KeepaliveStatus.STATUS_ACTIVE: 441 loge("Active Keepalive received invalid status!"); 442 break; 443 default: 444 loge("Invalid Keepalive Status received, " + ks.statusCode); 445 } 446 break; 447 default: 448 loge("Invalid Keepalive Status received, " + kr.currentStatus); 449 } 450 } 451 } 452 } 453