1 /* 2 * Copyright (C) 2016 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 static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; 20 21 import android.net.NetworkCapabilities; 22 import android.net.NetworkFactory; 23 import android.net.NetworkRequest; 24 import android.net.TelephonyNetworkSpecifier; 25 import android.os.AsyncResult; 26 import android.os.Bundle; 27 import android.os.Handler; 28 import android.os.Looper; 29 import android.os.Message; 30 import android.telephony.AccessNetworkConstants; 31 import android.telephony.Annotation.ApnType; 32 import android.telephony.SubscriptionManager; 33 import android.telephony.data.ApnSetting; 34 import android.util.LocalLog; 35 36 import com.android.internal.annotations.VisibleForTesting; 37 import com.android.internal.telephony.Phone; 38 import com.android.internal.telephony.PhoneSwitcher; 39 import com.android.internal.telephony.SubscriptionController; 40 import com.android.internal.telephony.dataconnection.DcTracker.ReleaseNetworkType; 41 import com.android.internal.telephony.dataconnection.DcTracker.RequestNetworkType; 42 import com.android.internal.telephony.dataconnection.TransportManager.HandoverParams; 43 import com.android.internal.telephony.metrics.NetworkRequestsStats; 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.util.HashMap; 50 import java.util.Map; 51 52 public class TelephonyNetworkFactory extends NetworkFactory { 53 public final String LOG_TAG; 54 protected static final boolean DBG = true; 55 56 private static final int REQUEST_LOG_SIZE = 40; 57 58 private static final int ACTION_NO_OP = 0; 59 private static final int ACTION_REQUEST = 1; 60 private static final int ACTION_RELEASE = 2; 61 62 private static final int TELEPHONY_NETWORK_SCORE = 50; 63 64 @VisibleForTesting 65 public static final int EVENT_ACTIVE_PHONE_SWITCH = 1; 66 @VisibleForTesting 67 public static final int EVENT_SUBSCRIPTION_CHANGED = 2; 68 private static final int EVENT_NETWORK_REQUEST = 3; 69 private static final int EVENT_NETWORK_RELEASE = 4; 70 private static final int EVENT_DATA_HANDOVER_NEEDED = 5; 71 private static final int EVENT_DATA_HANDOVER_COMPLETED = 6; 72 73 private final PhoneSwitcher mPhoneSwitcher; 74 private final SubscriptionController mSubscriptionController; 75 private final LocalLog mLocalLog = new LocalLog(REQUEST_LOG_SIZE); 76 77 // Key: network request. Value: the transport of DcTracker it applies to, 78 // AccessNetworkConstants.TRANSPORT_TYPE_INVALID if not applied. 79 private final Map<NetworkRequest, Integer> mNetworkRequests = new HashMap<>(); 80 81 private final Map<Message, HandoverParams> mPendingHandovers = new HashMap<>(); 82 83 private final Phone mPhone; 84 85 private final TransportManager mTransportManager; 86 87 private int mSubscriptionId; 88 89 @VisibleForTesting 90 public final Handler mInternalHandler; 91 92 TelephonyNetworkFactory(Looper looper, Phone phone)93 public TelephonyNetworkFactory(Looper looper, Phone phone) { 94 super(looper, phone.getContext(), "TelephonyNetworkFactory[" + phone.getPhoneId() 95 + "]", null); 96 mPhone = phone; 97 mTransportManager = mPhone.getTransportManager(); 98 mInternalHandler = new InternalHandler(looper); 99 100 mSubscriptionController = SubscriptionController.getInstance(); 101 102 setCapabilityFilter(makeNetworkFilter(mSubscriptionController, mPhone.getPhoneId())); 103 setScoreFilter(TELEPHONY_NETWORK_SCORE); 104 105 mPhoneSwitcher = PhoneSwitcher.getInstance(); 106 LOG_TAG = "TelephonyNetworkFactory[" + mPhone.getPhoneId() + "]"; 107 108 mPhoneSwitcher.registerForActivePhoneSwitch(mInternalHandler, EVENT_ACTIVE_PHONE_SWITCH, 109 null); 110 mTransportManager.registerForHandoverNeededEvent(mInternalHandler, 111 EVENT_DATA_HANDOVER_NEEDED); 112 113 mSubscriptionId = INVALID_SUBSCRIPTION_ID; 114 SubscriptionManager.from(mPhone.getContext()).addOnSubscriptionsChangedListener( 115 mSubscriptionsChangedListener); 116 117 register(); 118 } 119 120 private final SubscriptionManager.OnSubscriptionsChangedListener mSubscriptionsChangedListener = 121 new SubscriptionManager.OnSubscriptionsChangedListener() { 122 @Override 123 public void onSubscriptionsChanged() { 124 mInternalHandler.sendEmptyMessage(EVENT_SUBSCRIPTION_CHANGED); 125 } 126 }; 127 makeNetworkFilter(SubscriptionController subscriptionController, int phoneId)128 private NetworkCapabilities makeNetworkFilter(SubscriptionController subscriptionController, 129 int phoneId) { 130 final int subscriptionId = subscriptionController.getSubIdUsingPhoneId(phoneId); 131 return makeNetworkFilter(subscriptionId); 132 } 133 134 /** 135 * Build the network request filter used by this factory. 136 * @param subscriptionId the subscription ID to listen to 137 * @return the filter to send to the system server 138 */ 139 // This is used by the test to simulate the behavior of the system server, which is to 140 // send requests that match the network filter. 141 @VisibleForTesting makeNetworkFilter(int subscriptionId)142 public NetworkCapabilities makeNetworkFilter(int subscriptionId) { 143 final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder() 144 .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) 145 .addCapability(NetworkCapabilities.NET_CAPABILITY_MMS) 146 .addCapability(NetworkCapabilities.NET_CAPABILITY_SUPL) 147 .addCapability(NetworkCapabilities.NET_CAPABILITY_DUN) 148 .addCapability(NetworkCapabilities.NET_CAPABILITY_FOTA) 149 .addCapability(NetworkCapabilities.NET_CAPABILITY_IMS) 150 .addCapability(NetworkCapabilities.NET_CAPABILITY_CBS) 151 .addCapability(NetworkCapabilities.NET_CAPABILITY_IA) 152 .addCapability(NetworkCapabilities.NET_CAPABILITY_RCS) 153 .addCapability(NetworkCapabilities.NET_CAPABILITY_XCAP) 154 .addCapability(NetworkCapabilities.NET_CAPABILITY_ENTERPRISE) 155 .addCapability(NetworkCapabilities.NET_CAPABILITY_EIMS) 156 .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) 157 .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED) 158 .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) 159 .setNetworkSpecifier(new TelephonyNetworkSpecifier.Builder() 160 .setSubscriptionId(subscriptionId).build()); 161 return builder.build(); 162 } 163 164 private class InternalHandler extends Handler { InternalHandler(Looper looper)165 public InternalHandler(Looper looper) { 166 super(looper); 167 } 168 169 @Override handleMessage(Message msg)170 public void handleMessage(Message msg) { 171 switch (msg.what) { 172 case EVENT_ACTIVE_PHONE_SWITCH: { 173 onActivePhoneSwitch(); 174 break; 175 } 176 case EVENT_SUBSCRIPTION_CHANGED: { 177 onSubIdChange(); 178 break; 179 } 180 case EVENT_NETWORK_REQUEST: { 181 onNeedNetworkFor(msg); 182 break; 183 } 184 case EVENT_NETWORK_RELEASE: { 185 onReleaseNetworkFor(msg); 186 break; 187 } 188 case EVENT_DATA_HANDOVER_NEEDED: { 189 AsyncResult ar = (AsyncResult) msg.obj; 190 HandoverParams handoverParams = (HandoverParams) ar.result; 191 onDataHandoverNeeded(handoverParams.apnType, handoverParams.targetTransport, 192 handoverParams); 193 break; 194 } 195 case EVENT_DATA_HANDOVER_COMPLETED: { 196 Bundle bundle = msg.getData(); 197 NetworkRequest nr = bundle.getParcelable( 198 DcTracker.DATA_COMPLETE_MSG_EXTRA_NETWORK_REQUEST); 199 boolean success = bundle.getBoolean( 200 DcTracker.DATA_COMPLETE_MSG_EXTRA_SUCCESS); 201 int transport = bundle.getInt( 202 DcTracker.DATA_COMPLETE_MSG_EXTRA_TRANSPORT_TYPE); 203 boolean fallback = bundle.getBoolean( 204 DcTracker.DATA_COMPLETE_MSG_EXTRA_HANDOVER_FAILURE_FALLBACK); 205 HandoverParams handoverParams = mPendingHandovers.remove(msg); 206 if (handoverParams != null) { 207 onDataHandoverSetupCompleted(nr, success, transport, fallback, 208 handoverParams); 209 } else { 210 logl("Handover completed but cannot find handover entry!"); 211 } 212 break; 213 } 214 } 215 } 216 } 217 getTransportTypeFromNetworkRequest(NetworkRequest networkRequest)218 private int getTransportTypeFromNetworkRequest(NetworkRequest networkRequest) { 219 int apnType = ApnContext.getApnTypeFromNetworkRequest(networkRequest); 220 return mTransportManager.getCurrentTransport(apnType); 221 } 222 223 /** 224 * Request network 225 * 226 * @param networkRequest Network request from clients 227 * @param requestType The request type 228 * @param transport Transport type 229 * @param onHandoverCompleteMsg When request type is handover, this message will be sent when 230 * handover is completed. For normal request, this should be null. 231 */ requestNetworkInternal(NetworkRequest networkRequest, @RequestNetworkType int requestType, int transport, Message onHandoverCompleteMsg)232 private void requestNetworkInternal(NetworkRequest networkRequest, 233 @RequestNetworkType int requestType, int transport, Message onHandoverCompleteMsg) { 234 NetworkRequestsStats.addNetworkRequest(networkRequest, mSubscriptionId); 235 if (mPhone.getDcTracker(transport) != null) { 236 mPhone.getDcTracker(transport).requestNetwork(networkRequest, requestType, 237 onHandoverCompleteMsg); 238 } 239 } 240 releaseNetworkInternal(NetworkRequest networkRequest, @ReleaseNetworkType int releaseType, int transport)241 private void releaseNetworkInternal(NetworkRequest networkRequest, 242 @ReleaseNetworkType int releaseType, 243 int transport) { 244 NetworkRequestsStats.addNetworkRelease(networkRequest, mSubscriptionId); 245 if (mPhone.getDcTracker(transport) != null) { 246 mPhone.getDcTracker(transport).releaseNetwork(networkRequest, releaseType); 247 } 248 } 249 getAction(boolean wasActive, boolean isActive)250 private static int getAction(boolean wasActive, boolean isActive) { 251 if (!wasActive && isActive) { 252 return ACTION_REQUEST; 253 } else if (wasActive && !isActive) { 254 return ACTION_RELEASE; 255 } else { 256 return ACTION_NO_OP; 257 } 258 } 259 260 // apply or revoke requests if our active-ness changes onActivePhoneSwitch()261 private void onActivePhoneSwitch() { 262 for (HashMap.Entry<NetworkRequest, Integer> entry : mNetworkRequests.entrySet()) { 263 NetworkRequest networkRequest = entry.getKey(); 264 boolean applied = entry.getValue() != AccessNetworkConstants.TRANSPORT_TYPE_INVALID; 265 266 boolean shouldApply = mPhoneSwitcher.shouldApplyNetworkRequest( 267 networkRequest, mPhone.getPhoneId()); 268 269 int action = getAction(applied, shouldApply); 270 if (action == ACTION_NO_OP) continue; 271 272 logl("onActivePhoneSwitch: " + ((action == ACTION_REQUEST) 273 ? "Requesting" : "Releasing") + " network request " + networkRequest); 274 int transportType = getTransportTypeFromNetworkRequest(networkRequest); 275 if (action == ACTION_REQUEST) { 276 requestNetworkInternal(networkRequest, DcTracker.REQUEST_TYPE_NORMAL, 277 getTransportTypeFromNetworkRequest(networkRequest), null); 278 } else if (action == ACTION_RELEASE) { 279 releaseNetworkInternal(networkRequest, DcTracker.RELEASE_TYPE_DETACH, 280 getTransportTypeFromNetworkRequest(networkRequest)); 281 } 282 283 mNetworkRequests.put(networkRequest, 284 shouldApply ? transportType : AccessNetworkConstants.TRANSPORT_TYPE_INVALID); 285 } 286 } 287 288 // watch for phone->subId changes, reapply new filter and let 289 // that flow through to apply/revoke of requests onSubIdChange()290 private void onSubIdChange() { 291 final int newSubscriptionId = mSubscriptionController.getSubIdUsingPhoneId( 292 mPhone.getPhoneId()); 293 if (mSubscriptionId != newSubscriptionId) { 294 if (DBG) log("onSubIdChange " + mSubscriptionId + "->" + newSubscriptionId); 295 mSubscriptionId = newSubscriptionId; 296 setCapabilityFilter(makeNetworkFilter(mSubscriptionId)); 297 } 298 } 299 300 @Override needNetworkFor(NetworkRequest networkRequest)301 public void needNetworkFor(NetworkRequest networkRequest) { 302 Message msg = mInternalHandler.obtainMessage(EVENT_NETWORK_REQUEST); 303 msg.obj = networkRequest; 304 msg.sendToTarget(); 305 } 306 onNeedNetworkFor(Message msg)307 private void onNeedNetworkFor(Message msg) { 308 NetworkRequest networkRequest = (NetworkRequest) msg.obj; 309 boolean shouldApply = mPhoneSwitcher.shouldApplyNetworkRequest( 310 networkRequest, mPhone.getPhoneId()); 311 312 mNetworkRequests.put(networkRequest, shouldApply 313 ? getTransportTypeFromNetworkRequest(networkRequest) 314 : AccessNetworkConstants.TRANSPORT_TYPE_INVALID); 315 316 logl("onNeedNetworkFor " + networkRequest + " shouldApply " + shouldApply); 317 318 if (shouldApply) { 319 requestNetworkInternal(networkRequest, DcTracker.REQUEST_TYPE_NORMAL, 320 getTransportTypeFromNetworkRequest(networkRequest), null); 321 } 322 } 323 324 @Override releaseNetworkFor(NetworkRequest networkRequest)325 public void releaseNetworkFor(NetworkRequest networkRequest) { 326 Message msg = mInternalHandler.obtainMessage(EVENT_NETWORK_RELEASE); 327 msg.obj = networkRequest; 328 msg.sendToTarget(); 329 } 330 onReleaseNetworkFor(Message msg)331 private void onReleaseNetworkFor(Message msg) { 332 NetworkRequest networkRequest = (NetworkRequest) msg.obj; 333 boolean applied = mNetworkRequests.get(networkRequest) 334 != AccessNetworkConstants.TRANSPORT_TYPE_INVALID; 335 336 mNetworkRequests.remove(networkRequest); 337 338 logl("onReleaseNetworkFor " + networkRequest + " applied " + applied); 339 340 if (applied) { 341 // Most of the time, the network request only exists in one of the DcTracker, but in the 342 // middle of handover, the network request temporarily exists in both DcTrackers. If 343 // connectivity service releases the network request while handover is ongoing, we need 344 // to remove network requests from both DcTrackers. 345 // Note that this part will be refactored in T, where we won't even have DcTracker at 346 // all. 347 releaseNetworkInternal(networkRequest, DcTracker.RELEASE_TYPE_NORMAL, 348 AccessNetworkConstants.TRANSPORT_TYPE_WWAN); 349 releaseNetworkInternal(networkRequest, DcTracker.RELEASE_TYPE_NORMAL, 350 AccessNetworkConstants.TRANSPORT_TYPE_WLAN); 351 } 352 } 353 onDataHandoverNeeded(@pnType int apnType, int targetTransport, HandoverParams handoverParams)354 private void onDataHandoverNeeded(@ApnType int apnType, int targetTransport, 355 HandoverParams handoverParams) { 356 log("onDataHandoverNeeded: apnType=" + ApnSetting.getApnTypeString(apnType) 357 + ", target transport=" 358 + AccessNetworkConstants.transportTypeToString(targetTransport)); 359 if (mTransportManager.getCurrentTransport(apnType) == targetTransport) { 360 log("APN type " + ApnSetting.getApnTypeString(apnType) + " is already on " 361 + AccessNetworkConstants.transportTypeToString(targetTransport)); 362 return; 363 } 364 365 boolean handoverPending = false; 366 for (HashMap.Entry<NetworkRequest, Integer> entry : mNetworkRequests.entrySet()) { 367 NetworkRequest networkRequest = entry.getKey(); 368 int currentTransport = entry.getValue(); 369 boolean applied = currentTransport != AccessNetworkConstants.TRANSPORT_TYPE_INVALID; 370 if (ApnContext.getApnTypeFromNetworkRequest(networkRequest) == apnType 371 && applied 372 && currentTransport != targetTransport) { 373 DcTracker dcTracker = mPhone.getDcTracker(currentTransport); 374 if (dcTracker != null) { 375 DataConnection dc = dcTracker.getDataConnectionByApnType( 376 ApnSetting.getApnTypeString(apnType)); 377 if (dc != null && (dc.isActive())) { 378 Message onCompleteMsg = mInternalHandler.obtainMessage( 379 EVENT_DATA_HANDOVER_COMPLETED); 380 onCompleteMsg.getData().putParcelable( 381 DcTracker.DATA_COMPLETE_MSG_EXTRA_NETWORK_REQUEST, networkRequest); 382 mPendingHandovers.put(onCompleteMsg, handoverParams); 383 requestNetworkInternal(networkRequest, DcTracker.REQUEST_TYPE_HANDOVER, 384 targetTransport, onCompleteMsg); 385 log("Requested handover " + ApnSetting.getApnTypeString(apnType) 386 + " to " 387 + AccessNetworkConstants.transportTypeToString(targetTransport) 388 + ". " + networkRequest); 389 handoverPending = true; 390 } else { 391 // Request is there, but no actual data connection. In this case, just move 392 // the request to the new transport. 393 log("The network request is on transport " + AccessNetworkConstants 394 .transportTypeToString(currentTransport) + ", but no live data " 395 + "connection. Just move the request to transport " 396 + AccessNetworkConstants.transportTypeToString(targetTransport) 397 + ", dc=" + dc); 398 entry.setValue(targetTransport); 399 releaseNetworkInternal(networkRequest, DcTracker.RELEASE_TYPE_NORMAL, 400 currentTransport); 401 requestNetworkInternal(networkRequest, DcTracker.REQUEST_TYPE_NORMAL, 402 targetTransport, null); 403 } 404 } else { 405 log("DcTracker on " + AccessNetworkConstants.transportTypeToString( 406 currentTransport) + " is not available."); 407 } 408 } 409 } 410 411 if (!handoverPending) { 412 log("No handover request pending. Handover process is now completed"); 413 handoverParams.callback.onCompleted(true, false); 414 } 415 } 416 onDataHandoverSetupCompleted(NetworkRequest networkRequest, boolean success, int targetTransport, boolean fallback, HandoverParams handoverParams)417 private void onDataHandoverSetupCompleted(NetworkRequest networkRequest, boolean success, 418 int targetTransport, boolean fallback, 419 HandoverParams handoverParams) { 420 log("onDataHandoverSetupCompleted: " + networkRequest + ", success=" + success 421 + ", targetTransport=" 422 + AccessNetworkConstants.transportTypeToString(targetTransport) 423 + ", fallback=" + fallback); 424 425 // At this point, handover setup has been completed on the target transport. 426 // If it succeeded, or it failed without falling back to the original transport, 427 // we should release the request from the original transport. 428 if (!fallback) { 429 int originTransport = (targetTransport == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) 430 ? AccessNetworkConstants.TRANSPORT_TYPE_WLAN 431 : AccessNetworkConstants.TRANSPORT_TYPE_WWAN; 432 int releaseType = success 433 ? DcTracker.RELEASE_TYPE_HANDOVER 434 // If handover fails, we need to tear down the existing connection, so the 435 // new data connection can be re-established on the new transport. If we leave 436 // the existing data connection in current transport, then DCT and qualified 437 // network service will be out of sync. Specifying release type to detach 438 // the transport is moved to the other transport, but network request is still 439 // there, connectivity service will not call unwanted to tear down the network. 440 // We need explicitly tear down the data connection here so the new data 441 // connection can be re-established on the other transport. 442 : DcTracker.RELEASE_TYPE_DETACH; 443 releaseNetworkInternal(networkRequest, releaseType, originTransport); 444 445 // Before updating the network request with the target transport, make sure the request 446 // is still there because it's possible that connectivity service has already released 447 // the network while handover is ongoing. If connectivity service already released 448 // the network request, we need to tear down the just-handovered data connection on the 449 // target transport. 450 if (mNetworkRequests.containsKey(networkRequest)) { 451 // Update it with the target transport. 452 mNetworkRequests.put(networkRequest, targetTransport); 453 } 454 } else { 455 // If handover fails and requires to fallback, the context of target transport needs to 456 // be released 457 if (!success) { 458 releaseNetworkInternal(networkRequest, 459 DcTracker.RELEASE_TYPE_NORMAL, targetTransport); 460 } 461 } 462 463 handoverParams.callback.onCompleted(success, fallback); 464 } 465 log(String s)466 protected void log(String s) { 467 Rlog.d(LOG_TAG, s); 468 } 469 logl(String s)470 protected void logl(String s) { 471 log(s); 472 mLocalLog.log(s); 473 } 474 dump(FileDescriptor fd, PrintWriter writer, String[] args)475 public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { 476 final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " "); 477 pw.println("Network Requests:"); 478 pw.increaseIndent(); 479 for (HashMap.Entry<NetworkRequest, Integer> entry : mNetworkRequests.entrySet()) { 480 NetworkRequest nr = entry.getKey(); 481 int transport = entry.getValue(); 482 pw.println(nr + (transport != AccessNetworkConstants.TRANSPORT_TYPE_INVALID 483 ? (" applied on " + transport) : " not applied")); 484 } 485 mLocalLog.dump(fd, pw, args); 486 pw.decreaseIndent(); 487 } 488 } 489