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