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.annotation.Nullable; 21 import android.net.KeepalivePacketData; 22 import android.net.LinkProperties; 23 import android.net.NattKeepalivePacketData; 24 import android.net.NetworkAgent; 25 import android.net.NetworkAgentConfig; 26 import android.net.NetworkCapabilities; 27 import android.net.NetworkInfo; 28 import android.net.NetworkProvider; 29 import android.net.SocketKeepalive; 30 import android.net.Uri; 31 import android.os.Message; 32 import android.telephony.AccessNetworkConstants; 33 import android.telephony.AccessNetworkConstants.TransportType; 34 import android.telephony.AnomalyReporter; 35 import android.telephony.TelephonyManager; 36 import android.util.ArrayMap; 37 import android.util.LocalLog; 38 import android.util.SparseArray; 39 40 import com.android.internal.telephony.DctConstants; 41 import com.android.internal.telephony.Phone; 42 import com.android.internal.telephony.RILConstants; 43 import com.android.internal.telephony.metrics.TelephonyMetrics; 44 import com.android.internal.util.IndentingPrintWriter; 45 import com.android.telephony.Rlog; 46 47 import java.io.FileDescriptor; 48 import java.io.PrintWriter; 49 import java.time.Duration; 50 import java.util.Map; 51 import java.util.Objects; 52 import java.util.UUID; 53 54 /** 55 * This class represents a network agent which is communication channel between 56 * {@link DataConnection} and {@link com.android.server.ConnectivityService}. The agent is 57 * created when data connection enters {@link DataConnection.DcActiveState} until it exits that 58 * state. 59 * 60 * Note that in IWLAN handover scenario, this agent could be transferred to the new 61 * {@link DataConnection} so for a short window of time this object might be accessed by two 62 * different {@link DataConnection}. Thus each method in this class needs to be synchronized. 63 */ 64 public class DcNetworkAgent extends NetworkAgent { 65 private final String mTag; 66 67 private final int mId; 68 69 private Phone mPhone; 70 71 private int mTransportType; 72 73 private NetworkCapabilities mNetworkCapabilities; 74 75 public final DcKeepaliveTracker keepaliveTracker = new DcKeepaliveTracker(); 76 77 private DataConnection mDataConnection; 78 79 private final LocalLog mNetCapsLocalLog = new LocalLog(50); 80 81 private NetworkInfo mNetworkInfo; 82 83 // For interface duplicate detection. Key is the net id, value is the interface name in string. 84 private static Map<Integer, String> sInterfaceNames = new ArrayMap<>(); 85 DcNetworkAgent(DataConnection dc, Phone phone, NetworkInfo ni, int score, NetworkAgentConfig config, NetworkProvider networkProvider, int transportType)86 DcNetworkAgent(DataConnection dc, Phone phone, NetworkInfo ni, int score, 87 NetworkAgentConfig config, NetworkProvider networkProvider, int transportType) { 88 super(phone.getContext(), dc.getHandler().getLooper(), "DcNetworkAgent", 89 dc.getNetworkCapabilities(), dc.getLinkProperties(), score, config, 90 networkProvider); 91 register(); 92 mId = getNetwork().netId; 93 mTag = "DcNetworkAgent" + "-" + mId; 94 mPhone = phone; 95 mNetworkCapabilities = dc.getNetworkCapabilities(); 96 mTransportType = transportType; 97 mDataConnection = dc; 98 mNetworkInfo = new NetworkInfo(ni); 99 setLegacySubtype(ni.getSubtype(), ni.getSubtypeName()); 100 setLegacyExtraInfo(ni.getExtraInfo()); 101 if (dc.getLinkProperties() != null) { 102 checkDuplicateInterface(mId, dc.getLinkProperties().getInterfaceName()); 103 logd("created for data connection " + dc.getName() + ", " 104 + dc.getLinkProperties().getInterfaceName()); 105 } else { 106 loge("The connection does not have a valid link properties."); 107 } 108 } 109 checkDuplicateInterface(int netId, @Nullable String interfaceName)110 private void checkDuplicateInterface(int netId, @Nullable String interfaceName) { 111 for (Map.Entry<Integer, String> entry: sInterfaceNames.entrySet()) { 112 if (Objects.equals(interfaceName, entry.getValue())) { 113 String message = "Duplicate interface " + interfaceName 114 + " is detected. DcNetworkAgent-" + entry.getKey() 115 + " already used this interface name."; 116 loge(message); 117 // Using fixed UUID to avoid duplicate bugreport notification 118 AnomalyReporter.reportAnomaly( 119 UUID.fromString("02f3d3f6-4613-4415-b6cb-8d92c8a938a6"), 120 message); 121 return; 122 } 123 } 124 sInterfaceNames.put(netId, interfaceName); 125 } 126 127 /** 128 * @return The tag 129 */ getTag()130 String getTag() { 131 return mTag; 132 } 133 134 /** 135 * Set the data connection that owns this network agent. 136 * 137 * @param dc Data connection owning this network agent. 138 * @param transportType Transport that this data connection is on. 139 */ acquireOwnership(@onNull DataConnection dc, @TransportType int transportType)140 public synchronized void acquireOwnership(@NonNull DataConnection dc, 141 @TransportType int transportType) { 142 mDataConnection = dc; 143 mTransportType = transportType; 144 logd(dc.getName() + " acquired the ownership of this agent."); 145 } 146 147 /** 148 * Release the ownership of network agent. 149 */ releaseOwnership(DataConnection dc)150 public synchronized void releaseOwnership(DataConnection dc) { 151 if (mDataConnection == null) { 152 loge("releaseOwnership called on no-owner DcNetworkAgent!"); 153 return; 154 } else if (mDataConnection != dc) { 155 loge("releaseOwnership: This agent belongs to " 156 + mDataConnection.getName() + ", ignored the request from " + dc.getName()); 157 return; 158 } 159 logd("Data connection " + mDataConnection.getName() + " released the ownership."); 160 mDataConnection = null; 161 } 162 163 /** 164 * @return The data connection that owns this agent 165 */ getDataConnection()166 public synchronized DataConnection getDataConnection() { 167 return mDataConnection; 168 } 169 170 @Override onNetworkUnwanted()171 public synchronized void onNetworkUnwanted() { 172 if (mDataConnection == null) { 173 loge("onNetworkUnwanted found called on no-owner DcNetworkAgent!"); 174 return; 175 } 176 177 logd("onNetworkUnwanted called. Now tear down the data connection " 178 + mDataConnection.getName()); 179 mDataConnection.tearDownAll(Phone.REASON_RELEASED_BY_CONNECTIVITY_SERVICE, 180 DcTracker.RELEASE_TYPE_DETACH, null); 181 } 182 183 @Override onBandwidthUpdateRequested()184 public synchronized void onBandwidthUpdateRequested() { 185 if (mDataConnection == null) { 186 loge("onBandwidthUpdateRequested called on no-owner DcNetworkAgent!"); 187 return; 188 } 189 190 if (mPhone.getLceStatus() == RILConstants.LCE_ACTIVE // active LCE service 191 && mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) { 192 mPhone.mCi.pullLceData(mDataConnection.obtainMessage( 193 DataConnection.EVENT_BW_REFRESH_RESPONSE)); 194 } 195 } 196 197 @Override onValidationStatus(int status, Uri redirectUri)198 public synchronized void onValidationStatus(int status, Uri redirectUri) { 199 if (mDataConnection == null) { 200 loge("onValidationStatus called on no-owner DcNetworkAgent!"); 201 return; 202 } 203 204 logd("validation status: " + status + " with redirection URL: " + redirectUri); 205 DcTracker dct = mPhone.getDcTracker(mTransportType); 206 if (dct != null) { 207 Message msg = dct.obtainMessage(DctConstants.EVENT_NETWORK_STATUS_CHANGED, 208 status, mDataConnection.getCid(), redirectUri.toString()); 209 msg.sendToTarget(); 210 } 211 } 212 isOwned(DataConnection dc, String reason)213 private synchronized boolean isOwned(DataConnection dc, String reason) { 214 if (mDataConnection == null) { 215 loge(reason + " called on no-owner DcNetworkAgent!"); 216 return false; 217 } else if (mDataConnection != dc) { 218 loge(reason + ": This agent belongs to " 219 + mDataConnection.getName() + ", ignored the request from " + dc.getName()); 220 return false; 221 } 222 return true; 223 } 224 225 /** 226 * Set the network capabilities. 227 * 228 * @param networkCapabilities The network capabilities. 229 * @param dc The data connection that invokes this method. 230 */ sendNetworkCapabilities(NetworkCapabilities networkCapabilities, DataConnection dc)231 public synchronized void sendNetworkCapabilities(NetworkCapabilities networkCapabilities, 232 DataConnection dc) { 233 if (!isOwned(dc, "sendNetworkCapabilities")) return; 234 235 if (!networkCapabilities.equals(mNetworkCapabilities)) { 236 String logStr = "Changed from " + mNetworkCapabilities + " to " 237 + networkCapabilities + ", Data RAT=" 238 + mPhone.getServiceState().getRilDataRadioTechnology() 239 + ", dc=" + mDataConnection.getName(); 240 logd(logStr); 241 mNetCapsLocalLog.log(logStr); 242 if (networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) { 243 // only log metrics for DataConnection with NET_CAPABILITY_INTERNET 244 if (mNetworkCapabilities == null 245 || networkCapabilities.hasCapability( 246 NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED) 247 != mNetworkCapabilities.hasCapability( 248 NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED)) { 249 TelephonyMetrics.getInstance().writeNetworkCapabilitiesChangedEvent( 250 mPhone.getPhoneId(), networkCapabilities); 251 } 252 } 253 mNetworkCapabilities = networkCapabilities; 254 } 255 sendNetworkCapabilities(networkCapabilities); 256 } 257 258 /** 259 * Set the link properties 260 * 261 * @param linkProperties The link properties 262 * @param dc The data connection that invokes this method. 263 */ sendLinkProperties(@onNull LinkProperties linkProperties, DataConnection dc)264 public synchronized void sendLinkProperties(@NonNull LinkProperties linkProperties, 265 DataConnection dc) { 266 if (!isOwned(dc, "sendLinkProperties")) return; 267 268 sInterfaceNames.put(mId, dc.getLinkProperties().getInterfaceName()); 269 sendLinkProperties(linkProperties); 270 } 271 272 /** 273 * Set the network score. 274 * 275 * @param score The network score. 276 * @param dc The data connection that invokes this method. 277 */ sendNetworkScore(int score, DataConnection dc)278 public synchronized void sendNetworkScore(int score, DataConnection dc) { 279 if (!isOwned(dc, "sendNetworkScore")) return; 280 sendNetworkScore(score); 281 } 282 283 /** 284 * Unregister the network agent from connectivity service. 285 * 286 * @param dc The data connection that invokes this method. 287 */ unregister(DataConnection dc)288 public synchronized void unregister(DataConnection dc) { 289 if (!isOwned(dc, "unregister")) return; 290 291 logd("Unregister from connectivity service. " + sInterfaceNames.get(mId) + " removed."); 292 sInterfaceNames.remove(mId); 293 super.unregister(); 294 } 295 296 /** 297 * Set the network info. 298 * 299 * @param networkInfo The network info. 300 * @param dc The data connection that invokes this method. 301 */ sendNetworkInfo(NetworkInfo networkInfo, DataConnection dc)302 public synchronized void sendNetworkInfo(NetworkInfo networkInfo, DataConnection dc) { 303 if (!isOwned(dc, "sendNetworkInfo")) return; 304 final NetworkInfo.State oldState = mNetworkInfo.getState(); 305 final NetworkInfo.State state = networkInfo.getState(); 306 if (mNetworkInfo.getExtraInfo() != networkInfo.getExtraInfo()) { 307 setLegacyExtraInfo(networkInfo.getExtraInfo()); 308 } 309 int subType = networkInfo.getSubtype(); 310 if (mNetworkInfo.getSubtype() != subType) { 311 setLegacySubtype(subType, TelephonyManager.getNetworkTypeName(subType)); 312 } 313 if ((oldState == NetworkInfo.State.SUSPENDED || oldState == NetworkInfo.State.CONNECTED) 314 && state == NetworkInfo.State.DISCONNECTED) { 315 unregister(dc); 316 } 317 mNetworkInfo = new NetworkInfo(networkInfo); 318 } 319 320 /** 321 * Get the latest sent network info. 322 * 323 * @return network info 324 */ getNetworkInfo()325 public synchronized NetworkInfo getNetworkInfo() { 326 return mNetworkInfo; 327 } 328 329 @Override onStartSocketKeepalive(int slot, @NonNull Duration interval, @NonNull KeepalivePacketData packet)330 public synchronized void onStartSocketKeepalive(int slot, @NonNull Duration interval, 331 @NonNull KeepalivePacketData packet) { 332 if (mDataConnection == null) { 333 loge("onStartSocketKeepalive called on no-owner DcNetworkAgent!"); 334 return; 335 } 336 337 if (packet instanceof NattKeepalivePacketData) { 338 mDataConnection.obtainMessage(DataConnection.EVENT_KEEPALIVE_START_REQUEST, 339 slot, (int) interval.getSeconds(), packet).sendToTarget(); 340 } else { 341 sendSocketKeepaliveEvent(slot, SocketKeepalive.ERROR_UNSUPPORTED); 342 } 343 } 344 345 @Override onStopSocketKeepalive(int slot)346 public synchronized void onStopSocketKeepalive(int slot) { 347 if (mDataConnection == null) { 348 loge("onStopSocketKeepalive called on no-owner DcNetworkAgent!"); 349 return; 350 } 351 352 mDataConnection.obtainMessage(DataConnection.EVENT_KEEPALIVE_STOP_REQUEST, slot) 353 .sendToTarget(); 354 } 355 356 @Override toString()357 public String toString() { 358 return "DcNetworkAgent-" 359 + mId 360 + " mDataConnection=" 361 + ((mDataConnection != null) ? mDataConnection.getName() : null) 362 + " mTransportType=" 363 + AccessNetworkConstants.transportTypeToString(mTransportType) 364 + " " + ((mDataConnection != null) ? mDataConnection.getLinkProperties() : null) 365 + " mNetworkCapabilities=" + mNetworkCapabilities; 366 } 367 368 /** 369 * Dump the state of transport manager 370 * 371 * @param fd File descriptor 372 * @param printWriter Print writer 373 * @param args Arguments 374 */ dump(FileDescriptor fd, PrintWriter printWriter, String[] args)375 public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) { 376 IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " "); 377 pw.println(toString()); 378 pw.increaseIndent(); 379 pw.println("Net caps logs:"); 380 mNetCapsLocalLog.dump(fd, pw, args); 381 pw.decreaseIndent(); 382 } 383 384 /** 385 * Log with debug level 386 * 387 * @param s is string log 388 */ logd(String s)389 private void logd(String s) { 390 Rlog.d(mTag, s); 391 } 392 393 /** 394 * Log with error level 395 * 396 * @param s is string log 397 */ loge(String s)398 private void loge(String s) { 399 Rlog.e(mTag, s); 400 } 401 402 class DcKeepaliveTracker { 403 private class KeepaliveRecord { 404 public int slotId; 405 public int currentStatus; 406 KeepaliveRecord(int slotId, int status)407 KeepaliveRecord(int slotId, int status) { 408 this.slotId = slotId; 409 this.currentStatus = status; 410 } 411 } 412 413 private final SparseArray<KeepaliveRecord> mKeepalives = new SparseArray(); 414 getHandleForSlot(int slotId)415 int getHandleForSlot(int slotId) { 416 for (int i = 0; i < mKeepalives.size(); i++) { 417 KeepaliveRecord kr = mKeepalives.valueAt(i); 418 if (kr.slotId == slotId) return mKeepalives.keyAt(i); 419 } 420 return -1; 421 } 422 keepaliveStatusErrorToPacketKeepaliveError(int error)423 int keepaliveStatusErrorToPacketKeepaliveError(int error) { 424 switch(error) { 425 case KeepaliveStatus.ERROR_NONE: 426 return SocketKeepalive.SUCCESS; 427 case KeepaliveStatus.ERROR_UNSUPPORTED: 428 return SocketKeepalive.ERROR_UNSUPPORTED; 429 case KeepaliveStatus.ERROR_NO_RESOURCES: 430 return SocketKeepalive.ERROR_INSUFFICIENT_RESOURCES; 431 case KeepaliveStatus.ERROR_UNKNOWN: 432 default: 433 return SocketKeepalive.ERROR_HARDWARE_ERROR; 434 } 435 } 436 handleKeepaliveStarted(final int slot, KeepaliveStatus ks)437 void handleKeepaliveStarted(final int slot, KeepaliveStatus ks) { 438 switch (ks.statusCode) { 439 case KeepaliveStatus.STATUS_INACTIVE: 440 DcNetworkAgent.this.sendSocketKeepaliveEvent(slot, 441 keepaliveStatusErrorToPacketKeepaliveError(ks.errorCode)); 442 break; 443 case KeepaliveStatus.STATUS_ACTIVE: 444 DcNetworkAgent.this.sendSocketKeepaliveEvent( 445 slot, SocketKeepalive.SUCCESS); 446 // fall through to add record 447 case KeepaliveStatus.STATUS_PENDING: 448 logd("Adding keepalive handle=" 449 + ks.sessionHandle + " slot = " + slot); 450 mKeepalives.put(ks.sessionHandle, 451 new KeepaliveRecord( 452 slot, ks.statusCode)); 453 break; 454 default: 455 logd("Invalid KeepaliveStatus Code: " + ks.statusCode); 456 break; 457 } 458 } 459 handleKeepaliveStatus(KeepaliveStatus ks)460 void handleKeepaliveStatus(KeepaliveStatus ks) { 461 final KeepaliveRecord kr; 462 kr = mKeepalives.get(ks.sessionHandle); 463 464 if (kr == null) { 465 // If there is no slot for the session handle, we received an event 466 // for a different data connection. This is not an error because the 467 // keepalive session events are broadcast to all listeners. 468 loge("Discarding keepalive event for different data connection:" + ks); 469 return; 470 } 471 // Switch on the current state, to see what we do with the status update 472 switch (kr.currentStatus) { 473 case KeepaliveStatus.STATUS_INACTIVE: 474 logd("Inactive Keepalive received status!"); 475 DcNetworkAgent.this.sendSocketKeepaliveEvent( 476 kr.slotId, SocketKeepalive.ERROR_HARDWARE_ERROR); 477 break; 478 case KeepaliveStatus.STATUS_PENDING: 479 switch (ks.statusCode) { 480 case KeepaliveStatus.STATUS_INACTIVE: 481 DcNetworkAgent.this.sendSocketKeepaliveEvent(kr.slotId, 482 keepaliveStatusErrorToPacketKeepaliveError(ks.errorCode)); 483 kr.currentStatus = KeepaliveStatus.STATUS_INACTIVE; 484 mKeepalives.remove(ks.sessionHandle); 485 break; 486 case KeepaliveStatus.STATUS_ACTIVE: 487 logd("Pending Keepalive received active status!"); 488 kr.currentStatus = KeepaliveStatus.STATUS_ACTIVE; 489 DcNetworkAgent.this.sendSocketKeepaliveEvent( 490 kr.slotId, SocketKeepalive.SUCCESS); 491 break; 492 case KeepaliveStatus.STATUS_PENDING: 493 loge("Invalid unsolicied Keepalive Pending Status!"); 494 break; 495 default: 496 loge("Invalid Keepalive Status received, " + ks.statusCode); 497 } 498 break; 499 case KeepaliveStatus.STATUS_ACTIVE: 500 switch (ks.statusCode) { 501 case KeepaliveStatus.STATUS_INACTIVE: 502 logd("Keepalive received stopped status!"); 503 DcNetworkAgent.this.sendSocketKeepaliveEvent( 504 kr.slotId, SocketKeepalive.SUCCESS); 505 506 kr.currentStatus = KeepaliveStatus.STATUS_INACTIVE; 507 mKeepalives.remove(ks.sessionHandle); 508 break; 509 case KeepaliveStatus.STATUS_PENDING: 510 case KeepaliveStatus.STATUS_ACTIVE: 511 loge("Active Keepalive received invalid status!"); 512 break; 513 default: 514 loge("Invalid Keepalive Status received, " + ks.statusCode); 515 } 516 break; 517 default: 518 loge("Invalid Keepalive Status received, " + kr.currentStatus); 519 } 520 } 521 } 522 } 523