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