1 /* 2 * Copyright (C) 2020 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.server.location.gnss; 18 19 import android.content.Context; 20 import android.net.ConnectivityManager; 21 import android.net.LinkAddress; 22 import android.net.LinkProperties; 23 import android.net.Network; 24 import android.net.NetworkCapabilities; 25 import android.net.NetworkInfo; 26 import android.net.NetworkRequest; 27 import android.os.Handler; 28 import android.os.Looper; 29 import android.os.PowerManager; 30 import android.telephony.PhoneStateListener; 31 import android.telephony.PreciseCallState; 32 import android.telephony.SubscriptionInfo; 33 import android.telephony.SubscriptionManager; 34 import android.telephony.TelephonyManager; 35 import android.util.Log; 36 37 import com.android.internal.location.GpsNetInitiatedHandler; 38 39 import java.net.Inet4Address; 40 import java.net.Inet6Address; 41 import java.net.InetAddress; 42 import java.net.UnknownHostException; 43 import java.util.Arrays; 44 import java.util.HashMap; 45 import java.util.HashSet; 46 import java.util.Iterator; 47 import java.util.List; 48 import java.util.Map; 49 50 51 /** 52 * Handles network connection requests and network state change updates for AGPS data download. 53 */ 54 class GnssNetworkConnectivityHandler { 55 static final String TAG = "GnssNetworkConnectivityHandler"; 56 57 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 58 private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE); 59 60 // for mAGpsDataConnectionState 61 private static final int AGPS_DATA_CONNECTION_CLOSED = 0; 62 private static final int AGPS_DATA_CONNECTION_OPENING = 1; 63 private static final int AGPS_DATA_CONNECTION_OPEN = 2; 64 65 // these need to match AGnssStatusValue enum in IAGnssCallback.hal 66 /** AGPS status event values. */ 67 private static final int GPS_REQUEST_AGPS_DATA_CONN = 1; 68 private static final int GPS_RELEASE_AGPS_DATA_CONN = 2; 69 private static final int GPS_AGPS_DATA_CONNECTED = 3; 70 private static final int GPS_AGPS_DATA_CONN_DONE = 4; 71 private static final int GPS_AGPS_DATA_CONN_FAILED = 5; 72 73 // these must match the ApnIpType enum in IAGnss.hal 74 private static final int APN_INVALID = 0; 75 private static final int APN_IPV4 = 1; 76 private static final int APN_IPV6 = 2; 77 private static final int APN_IPV4V6 = 3; 78 79 // these must match the NetworkCapability enum flags in IAGnssRil.hal 80 private static final int AGNSS_NET_CAPABILITY_NOT_METERED = 1 << 0; 81 private static final int AGNSS_NET_CAPABILITY_NOT_ROAMING = 1 << 1; 82 83 // these need to match AGnssType enum in IAGnssCallback.hal 84 public static final int AGPS_TYPE_SUPL = 1; 85 public static final int AGPS_TYPE_C2K = 2; 86 private static final int AGPS_TYPE_EIMS = 3; 87 private static final int AGPS_TYPE_IMS = 4; 88 89 // Default time limit in milliseconds for the ConnectivityManager to find a suitable 90 // network with SUPL connectivity or report an error. 91 private static final int SUPL_NETWORK_REQUEST_TIMEOUT_MILLIS = 20 * 1000; 92 93 private static final int HASH_MAP_INITIAL_CAPACITY_TO_TRACK_CONNECTED_NETWORKS = 5; 94 95 // Keeps track of networks and their state as notified by the network request callbacks. 96 // Limit initial capacity to 5 as the number of connected networks will likely be small. 97 // NOTE: Must be accessed/modified only through the mHandler thread. 98 private HashMap<Network, NetworkAttributes> mAvailableNetworkAttributes = 99 new HashMap<>(HASH_MAP_INITIAL_CAPACITY_TO_TRACK_CONNECTED_NETWORKS); 100 101 // Phone State Listeners to track all the active sub IDs 102 private HashMap<Integer, SubIdPhoneStateListener> mPhoneStateListeners; 103 104 private final ConnectivityManager mConnMgr; 105 106 private final Handler mHandler; 107 private final GnssNetworkListener mGnssNetworkListener; 108 109 private int mAGpsDataConnectionState; 110 private InetAddress mAGpsDataConnectionIpAddr; 111 private int mAGpsType; 112 private int mActiveSubId = -1; 113 private final GpsNetInitiatedHandler mNiHandler; 114 115 116 private final Context mContext; 117 118 // Wakelocks 119 private static final String WAKELOCK_KEY = "GnssNetworkConnectivityHandler"; 120 private static final long WAKELOCK_TIMEOUT_MILLIS = 60 * 1000; 121 private final PowerManager.WakeLock mWakeLock; 122 123 /** 124 * Network attributes needed when updating HAL about network connectivity status changes. 125 */ 126 private static class NetworkAttributes { 127 private NetworkCapabilities mCapabilities; 128 private String mApn; 129 private int mType = ConnectivityManager.TYPE_NONE; 130 131 /** 132 * Returns true if the capabilities that we pass on to HAL change between {@curCapabilities} 133 * and {@code newCapabilities}. 134 */ hasCapabilitiesChanged(NetworkCapabilities curCapabilities, NetworkCapabilities newCapabilities)135 private static boolean hasCapabilitiesChanged(NetworkCapabilities curCapabilities, 136 NetworkCapabilities newCapabilities) { 137 if (curCapabilities == null || newCapabilities == null) { 138 return true; 139 } 140 141 // Monitor for roaming and metered capability changes. 142 return hasCapabilityChanged(curCapabilities, newCapabilities, 143 NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING) 144 || hasCapabilityChanged(curCapabilities, newCapabilities, 145 NetworkCapabilities.NET_CAPABILITY_NOT_METERED); 146 } 147 hasCapabilityChanged(NetworkCapabilities curCapabilities, NetworkCapabilities newCapabilities, int capability)148 private static boolean hasCapabilityChanged(NetworkCapabilities curCapabilities, 149 NetworkCapabilities newCapabilities, int capability) { 150 return curCapabilities.hasCapability(capability) 151 != newCapabilities.hasCapability(capability); 152 } 153 getCapabilityFlags(NetworkCapabilities capabilities)154 private static short getCapabilityFlags(NetworkCapabilities capabilities) { 155 short capabilityFlags = 0; 156 if (capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING)) { 157 capabilityFlags |= AGNSS_NET_CAPABILITY_NOT_ROAMING; 158 } 159 if (capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)) { 160 capabilityFlags |= AGNSS_NET_CAPABILITY_NOT_METERED; 161 } 162 return capabilityFlags; 163 } 164 } 165 166 /** 167 * Callback used to listen for data connectivity changes. 168 */ 169 private ConnectivityManager.NetworkCallback mNetworkConnectivityCallback; 170 171 /** 172 * Callback used to listen for availability of a requested SUPL connection. 173 * It is kept as a separate instance from {@link #mNetworkConnectivityCallback} to be able to 174 * manage the registration/un-registration lifetimes separately. 175 */ 176 private ConnectivityManager.NetworkCallback mSuplConnectivityCallback; 177 178 /** 179 * Interface to listen for network availability changes. 180 */ 181 interface GnssNetworkListener { onNetworkAvailable()182 void onNetworkAvailable(); 183 } 184 GnssNetworkConnectivityHandler(Context context, GnssNetworkListener gnssNetworkListener, Looper looper, GpsNetInitiatedHandler niHandler)185 GnssNetworkConnectivityHandler(Context context, 186 GnssNetworkListener gnssNetworkListener, 187 Looper looper, 188 GpsNetInitiatedHandler niHandler) { 189 mContext = context; 190 mGnssNetworkListener = gnssNetworkListener; 191 192 SubscriptionManager subManager = mContext.getSystemService(SubscriptionManager.class); 193 if (subManager != null) { 194 subManager.addOnSubscriptionsChangedListener(mOnSubscriptionsChangeListener); 195 } 196 197 PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); 198 mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY); 199 200 mHandler = new Handler(looper); 201 mNiHandler = niHandler; 202 mConnMgr = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE); 203 mSuplConnectivityCallback = createSuplConnectivityCallback(); 204 } 205 206 /** 207 * SubId Phone State Listener is used cache the last active Sub ID when a call is made, 208 * which will be used during an emergency call to set the Network Specifier to the particular 209 * sub when an emergency supl connection is requested 210 */ 211 private final class SubIdPhoneStateListener extends PhoneStateListener { 212 private Integer mSubId; SubIdPhoneStateListener(Integer subId)213 SubIdPhoneStateListener(Integer subId) { 214 mSubId = subId; 215 } 216 @Override onPreciseCallStateChanged(PreciseCallState state)217 public void onPreciseCallStateChanged(PreciseCallState state) { 218 if (state.PRECISE_CALL_STATE_ACTIVE == state.getForegroundCallState()) { 219 mActiveSubId = mSubId; 220 if (DEBUG) Log.d(TAG, "mActiveSubId: " + mActiveSubId); 221 } 222 } 223 }; 224 225 /** 226 * Subscription Changed Listener is used to get all active subscriptions and create a 227 * Phone State Listener for each Sub ID that we find in the active subscription list 228 */ 229 private final SubscriptionManager.OnSubscriptionsChangedListener mOnSubscriptionsChangeListener 230 = new SubscriptionManager.OnSubscriptionsChangedListener() { 231 @Override 232 public void onSubscriptionsChanged() { 233 if (mPhoneStateListeners == null) { 234 // Capacity=2 Load-Factor=1.0, as typically no more than 2 SIMs 235 mPhoneStateListeners = new HashMap<Integer, SubIdPhoneStateListener>(2,1); 236 } 237 SubscriptionManager subManager = mContext.getSystemService(SubscriptionManager.class); 238 TelephonyManager telManager = mContext.getSystemService(TelephonyManager.class); 239 if (subManager != null && telManager != null) { 240 List<SubscriptionInfo> subscriptionInfoList = 241 subManager.getActiveSubscriptionInfoList(); 242 HashSet<Integer> activeSubIds = new HashSet<Integer>(); 243 if (subscriptionInfoList != null) { 244 if (DEBUG) Log.d(TAG, "Active Sub List size: " + subscriptionInfoList.size()); 245 // populate phone state listeners with all new active subs 246 for (SubscriptionInfo subInfo : subscriptionInfoList) { 247 activeSubIds.add(subInfo.getSubscriptionId()); 248 if (!mPhoneStateListeners.containsKey(subInfo.getSubscriptionId())) { 249 TelephonyManager subIdTelManager = 250 telManager.createForSubscriptionId(subInfo.getSubscriptionId()); 251 if (subIdTelManager != null) { 252 if (DEBUG) Log.d(TAG, "Listener sub" + subInfo.getSubscriptionId()); 253 SubIdPhoneStateListener subIdPhoneStateListener = 254 new SubIdPhoneStateListener(subInfo.getSubscriptionId()); 255 mPhoneStateListeners.put(subInfo.getSubscriptionId(), 256 subIdPhoneStateListener); 257 subIdTelManager.listen(subIdPhoneStateListener, 258 PhoneStateListener.LISTEN_PRECISE_CALL_STATE); 259 } 260 } 261 } 262 } 263 // clean up phone state listeners than no longer have active subs 264 Iterator<Map.Entry<Integer, SubIdPhoneStateListener> > iterator = 265 mPhoneStateListeners.entrySet().iterator(); 266 while (iterator.hasNext()) { 267 Map.Entry<Integer, SubIdPhoneStateListener> element = iterator.next(); 268 if (!activeSubIds.contains(element.getKey())) { 269 TelephonyManager subIdTelManager = 270 telManager.createForSubscriptionId(element.getKey()); 271 if (subIdTelManager != null) { 272 if (DEBUG) Log.d(TAG, "unregister listener sub " + element.getKey()); 273 subIdTelManager.listen(element.getValue(), 274 PhoneStateListener.LISTEN_NONE); 275 // removes the element from mPhoneStateListeners 276 iterator.remove(); 277 } else { 278 Log.e(TAG, "Telephony Manager for Sub " + element.getKey() + " null"); 279 } 280 } 281 } 282 // clean up cached active phone call sub if it is no longer an active sub 283 if (!activeSubIds.contains(mActiveSubId)) { 284 mActiveSubId = -1; 285 } 286 } 287 } 288 }; 289 registerNetworkCallbacks()290 void registerNetworkCallbacks() { 291 // register for connectivity change events. 292 NetworkRequest.Builder networkRequestBuilder = new NetworkRequest.Builder(); 293 networkRequestBuilder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); 294 networkRequestBuilder.addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED); 295 networkRequestBuilder.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN); 296 NetworkRequest networkRequest = networkRequestBuilder.build(); 297 mNetworkConnectivityCallback = createNetworkConnectivityCallback(); 298 mConnMgr.registerNetworkCallback(networkRequest, mNetworkConnectivityCallback, mHandler); 299 } 300 301 /** 302 * @return {@code true} if there is a data network available for outgoing connections, 303 * {@code false} otherwise. 304 */ isDataNetworkConnected()305 boolean isDataNetworkConnected() { 306 NetworkInfo activeNetworkInfo = mConnMgr.getActiveNetworkInfo(); 307 return activeNetworkInfo != null && activeNetworkInfo.isConnected(); 308 } 309 310 /** 311 * Called from native code to update AGPS connection status, or to request or release a SUPL 312 * connection. 313 * 314 * <p>Note: {@code suplIpAddr} parameter is not present from IAGnssCallback.hal@2.0 onwards 315 * and is set to {@code null}. 316 */ onReportAGpsStatus(int agpsType, int agpsStatus, byte[] suplIpAddr)317 void onReportAGpsStatus(int agpsType, int agpsStatus, byte[] suplIpAddr) { 318 if (DEBUG) Log.d(TAG, "AGPS_DATA_CONNECTION: " + agpsDataConnStatusAsString(agpsStatus)); 319 switch (agpsStatus) { 320 case GPS_REQUEST_AGPS_DATA_CONN: 321 runOnHandler(() -> handleRequestSuplConnection(agpsType, suplIpAddr)); 322 break; 323 case GPS_RELEASE_AGPS_DATA_CONN: 324 runOnHandler(() -> handleReleaseSuplConnection(GPS_RELEASE_AGPS_DATA_CONN)); 325 break; 326 case GPS_AGPS_DATA_CONNECTED: 327 case GPS_AGPS_DATA_CONN_DONE: 328 case GPS_AGPS_DATA_CONN_FAILED: 329 break; 330 default: 331 Log.w(TAG, "Received unknown AGPS status: " + agpsStatus); 332 } 333 } 334 createNetworkConnectivityCallback()335 private ConnectivityManager.NetworkCallback createNetworkConnectivityCallback() { 336 return new ConnectivityManager.NetworkCallback() { 337 // Used to filter out network capabilities changes that we are not interested in. 338 // NOTE: Not using a ConcurrentHashMap and also not using locking around updates 339 // and access to the map object because it is all done inside the same 340 // handler thread invoking the callback methods. 341 private HashMap<Network, NetworkCapabilities> 342 mAvailableNetworkCapabilities = new HashMap<>( 343 HASH_MAP_INITIAL_CAPACITY_TO_TRACK_CONNECTED_NETWORKS); 344 345 @Override 346 public void onCapabilitiesChanged(Network network, 347 NetworkCapabilities capabilities) { 348 // This callback is invoked for any change in the network capabilities including 349 // initial availability, and changes while still available. Only process if the 350 // capabilities that we pass on to HAL change. 351 if (!NetworkAttributes.hasCapabilitiesChanged( 352 mAvailableNetworkCapabilities.get(network), capabilities)) { 353 if (VERBOSE) { 354 Log.v(TAG, "Relevant network capabilities unchanged. Capabilities: " 355 + capabilities); 356 } 357 return; 358 } 359 360 mAvailableNetworkCapabilities.put(network, capabilities); 361 if (DEBUG) { 362 Log.d(TAG, "Network connected/capabilities updated. Available networks count: " 363 + mAvailableNetworkCapabilities.size()); 364 } 365 366 mGnssNetworkListener.onNetworkAvailable(); 367 368 // Always on, notify HAL so it can get data it needs 369 handleUpdateNetworkState(network, true, capabilities); 370 } 371 372 @Override 373 public void onLost(Network network) { 374 if (mAvailableNetworkCapabilities.remove(network) == null) { 375 Log.w(TAG, "Incorrectly received network callback onLost() before" 376 + " onCapabilitiesChanged() for network: " + network); 377 return; 378 } 379 380 Log.i(TAG, "Network connection lost. Available networks count: " 381 + mAvailableNetworkCapabilities.size()); 382 handleUpdateNetworkState(network, false, null); 383 } 384 }; 385 } 386 createSuplConnectivityCallback()387 private ConnectivityManager.NetworkCallback createSuplConnectivityCallback() { 388 return new ConnectivityManager.NetworkCallback() { 389 @Override 390 public void onLinkPropertiesChanged(Network network, LinkProperties linkProperties) { 391 if (DEBUG) Log.d(TAG, "SUPL network connection available."); 392 // Specific to a change to a SUPL enabled network becoming ready 393 handleSuplConnectionAvailable(network, linkProperties); 394 } 395 396 @Override 397 public void onLost(Network network) { 398 Log.i(TAG, "SUPL network connection lost."); 399 handleReleaseSuplConnection(GPS_RELEASE_AGPS_DATA_CONN); 400 } 401 402 @Override 403 public void onUnavailable() { 404 Log.i(TAG, "SUPL network connection request timed out."); 405 // Could not setup the connection to the network in the specified time duration. 406 handleReleaseSuplConnection(GPS_AGPS_DATA_CONN_FAILED); 407 } 408 }; 409 } 410 411 private void runOnHandler(Runnable event) { 412 // hold a wake lock until this message is delivered 413 // note that this assumes the message will not be removed from the queue before 414 // it is handled (otherwise the wake lock would be leaked). 415 mWakeLock.acquire(WAKELOCK_TIMEOUT_MILLIS); 416 if (!mHandler.post(runEventAndReleaseWakeLock(event))) { 417 mWakeLock.release(); 418 } 419 } 420 421 private Runnable runEventAndReleaseWakeLock(Runnable event) { 422 return () -> { 423 try { 424 event.run(); 425 } finally { 426 mWakeLock.release(); 427 } 428 }; 429 } 430 431 private void handleUpdateNetworkState(Network network, boolean isConnected, 432 NetworkCapabilities capabilities) { 433 boolean networkAvailable = false; 434 TelephonyManager telephonyManager = mContext.getSystemService(TelephonyManager.class); 435 if (telephonyManager != null) { 436 networkAvailable = isConnected && telephonyManager.getDataEnabled(); 437 } 438 NetworkAttributes networkAttributes = updateTrackedNetworksState(isConnected, network, 439 capabilities); 440 String apn = networkAttributes.mApn; 441 int type = networkAttributes.mType; 442 // When isConnected is false, capabilities argument is null. So, use last received 443 // capabilities. 444 capabilities = networkAttributes.mCapabilities; 445 Log.i(TAG, String.format( 446 "updateNetworkState, state=%s, connected=%s, network=%s, capabilities=%s" 447 + ", apn: %s, availableNetworkCount: %d", 448 agpsDataConnStateAsString(), 449 isConnected, 450 network, 451 capabilities, 452 apn, 453 mAvailableNetworkAttributes.size())); 454 455 if (native_is_agps_ril_supported()) { 456 native_update_network_state( 457 isConnected, 458 type, 459 !capabilities.hasTransport( 460 NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING), /* isRoaming */ 461 networkAvailable, 462 apn != null ? apn : "", 463 network.getNetworkHandle(), 464 NetworkAttributes.getCapabilityFlags(capabilities)); 465 } else if (DEBUG) { 466 Log.d(TAG, "Skipped network state update because GPS HAL AGPS-RIL is not supported"); 467 } 468 } 469 470 private NetworkAttributes updateTrackedNetworksState(boolean isConnected, Network network, 471 NetworkCapabilities capabilities) { 472 if (!isConnected) { 473 // Connection lost event. So, remove it from tracked networks. 474 return mAvailableNetworkAttributes.remove(network); 475 } 476 477 NetworkAttributes networkAttributes = mAvailableNetworkAttributes.get(network); 478 if (networkAttributes != null) { 479 // Capabilities updated event for the connected network. 480 networkAttributes.mCapabilities = capabilities; 481 return networkAttributes; 482 } 483 484 // Initial capabilities event (equivalent to connection available event). 485 networkAttributes = new NetworkAttributes(); 486 networkAttributes.mCapabilities = capabilities; 487 488 // TODO: The synchronous method ConnectivityManager.getNetworkInfo() should not be called 489 // inside the asynchronous ConnectivityManager.NetworkCallback methods. 490 NetworkInfo info = mConnMgr.getNetworkInfo(network); 491 if (info != null) { 492 networkAttributes.mApn = info.getExtraInfo(); 493 networkAttributes.mType = info.getType(); 494 } 495 496 // Start tracking this network for connection status updates. 497 mAvailableNetworkAttributes.put(network, networkAttributes); 498 return networkAttributes; 499 } 500 501 private void handleSuplConnectionAvailable(Network network, LinkProperties linkProperties) { 502 // TODO: The synchronous method ConnectivityManager.getNetworkInfo() should not be called 503 // inside the asynchronous ConnectivityManager.NetworkCallback methods. 504 NetworkInfo info = mConnMgr.getNetworkInfo(network); 505 String apn = null; 506 if (info != null) { 507 apn = info.getExtraInfo(); 508 } 509 510 if (DEBUG) { 511 String message = String.format( 512 "handleSuplConnectionAvailable: state=%s, suplNetwork=%s, info=%s", 513 agpsDataConnStateAsString(), 514 network, 515 info); 516 Log.d(TAG, message); 517 } 518 519 if (mAGpsDataConnectionState == AGPS_DATA_CONNECTION_OPENING) { 520 if (apn == null) { 521 // assign a placeholder value in the case of C2K as otherwise we will have a runtime 522 // exception in the following call to native_agps_data_conn_open 523 apn = "dummy-apn"; 524 } 525 526 // Setting route to host is needed for GNSS HAL implementations earlier than 527 // @2.0::IAgnssCallback. The HAL @2.0::IAgnssCallback.agnssStatusCb() method does 528 // not require setting route to SUPL host and hence does not provide an IP address. 529 if (mAGpsDataConnectionIpAddr != null) { 530 setRouting(); 531 } 532 533 int apnIpType = getLinkIpType(linkProperties); 534 if (DEBUG) { 535 String message = String.format( 536 "native_agps_data_conn_open: mAgpsApn=%s, mApnIpType=%s", 537 apn, 538 apnIpType); 539 Log.d(TAG, message); 540 } 541 native_agps_data_conn_open(network.getNetworkHandle(), apn, apnIpType); 542 mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPEN; 543 } 544 } 545 546 private void handleRequestSuplConnection(int agpsType, byte[] suplIpAddr) { 547 mAGpsDataConnectionIpAddr = null; 548 mAGpsType = agpsType; 549 if (suplIpAddr != null) { 550 if (VERBOSE) Log.v(TAG, "Received SUPL IP addr[]: " + Arrays.toString(suplIpAddr)); 551 try { 552 mAGpsDataConnectionIpAddr = InetAddress.getByAddress(suplIpAddr); 553 if (DEBUG) Log.d(TAG, "IP address converted to: " + mAGpsDataConnectionIpAddr); 554 } catch (UnknownHostException e) { 555 Log.e(TAG, "Bad IP Address: " + suplIpAddr, e); 556 } 557 } 558 559 if (DEBUG) { 560 String message = String.format( 561 "requestSuplConnection, state=%s, agpsType=%s, address=%s", 562 agpsDataConnStateAsString(), 563 agpsTypeAsString(agpsType), 564 mAGpsDataConnectionIpAddr); 565 Log.d(TAG, message); 566 } 567 568 if (mAGpsDataConnectionState != AGPS_DATA_CONNECTION_CLOSED) { 569 return; 570 } 571 mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPENING; 572 573 // The transport type must be set to NetworkCapabilities.TRANSPORT_CELLULAR for the 574 // deprecated requestRouteToHostAddress() method in ConnectivityService to work for 575 // pre-gnss@2.0 devices. 576 NetworkRequest.Builder networkRequestBuilder = new NetworkRequest.Builder(); 577 networkRequestBuilder.addCapability(getNetworkCapability(mAGpsType)); 578 networkRequestBuilder.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR); 579 // During an emergency call, and when we have cached the Active Sub Id, we set the 580 // Network Specifier so that the network request goes to the correct Sub Id 581 if (mNiHandler.getInEmergency() && mActiveSubId >= 0) { 582 if (DEBUG) Log.d(TAG, "Adding Network Specifier: " + Integer.toString(mActiveSubId)); 583 networkRequestBuilder.setNetworkSpecifier(Integer.toString(mActiveSubId)); 584 } 585 NetworkRequest networkRequest = networkRequestBuilder.build(); 586 mConnMgr.requestNetwork( 587 networkRequest, 588 mSuplConnectivityCallback, 589 mHandler, 590 SUPL_NETWORK_REQUEST_TIMEOUT_MILLIS); 591 } 592 593 private int getNetworkCapability(int agpsType) { 594 switch (agpsType) { 595 case AGPS_TYPE_C2K: 596 case AGPS_TYPE_SUPL: 597 return NetworkCapabilities.NET_CAPABILITY_SUPL; 598 case AGPS_TYPE_EIMS: 599 return NetworkCapabilities.NET_CAPABILITY_EIMS; 600 case AGPS_TYPE_IMS: 601 return NetworkCapabilities.NET_CAPABILITY_IMS; 602 default: 603 throw new IllegalArgumentException("agpsType: " + agpsType); 604 } 605 } 606 607 private void handleReleaseSuplConnection(int agpsDataConnStatus) { 608 if (DEBUG) { 609 String message = String.format( 610 "releaseSuplConnection, state=%s, status=%s", 611 agpsDataConnStateAsString(), 612 agpsDataConnStatusAsString(agpsDataConnStatus)); 613 Log.d(TAG, message); 614 } 615 616 if (mAGpsDataConnectionState == AGPS_DATA_CONNECTION_CLOSED) { 617 return; 618 } 619 620 mAGpsDataConnectionState = AGPS_DATA_CONNECTION_CLOSED; 621 mConnMgr.unregisterNetworkCallback(mSuplConnectivityCallback); 622 switch (agpsDataConnStatus) { 623 case GPS_AGPS_DATA_CONN_FAILED: 624 native_agps_data_conn_failed(); 625 break; 626 case GPS_RELEASE_AGPS_DATA_CONN: 627 native_agps_data_conn_closed(); 628 break; 629 default: 630 Log.e(TAG, "Invalid status to release SUPL connection: " + agpsDataConnStatus); 631 } 632 } 633 634 // TODO: Delete this method when all devices upgrade to HAL @2.0::IAGnssCallback 635 // interface which does not require setting route to host. 636 private void setRouting() { 637 boolean result = mConnMgr.requestRouteToHostAddress( 638 ConnectivityManager.TYPE_MOBILE_SUPL, 639 mAGpsDataConnectionIpAddr); 640 641 if (!result) { 642 Log.e(TAG, "Error requesting route to host: " + mAGpsDataConnectionIpAddr); 643 } else if (DEBUG) { 644 Log.d(TAG, "Successfully requested route to host: " + mAGpsDataConnectionIpAddr); 645 } 646 } 647 648 /** 649 * Ensures the calling function is running in the thread associated with {@link #mHandler}. 650 */ 651 private void ensureInHandlerThread() { 652 if (mHandler != null && Looper.myLooper() == mHandler.getLooper()) { 653 return; 654 } 655 throw new IllegalStateException("This method must run on the Handler thread."); 656 } 657 658 /** 659 * @return A string representing the current state stored in {@link #mAGpsDataConnectionState}. 660 */ 661 private String agpsDataConnStateAsString() { 662 switch (mAGpsDataConnectionState) { 663 case AGPS_DATA_CONNECTION_CLOSED: 664 return "CLOSED"; 665 case AGPS_DATA_CONNECTION_OPEN: 666 return "OPEN"; 667 case AGPS_DATA_CONNECTION_OPENING: 668 return "OPENING"; 669 default: 670 return "<Unknown>(" + mAGpsDataConnectionState + ")"; 671 } 672 } 673 674 /** 675 * @return A string representing the given GPS_AGPS_DATA status. 676 */ 677 private String agpsDataConnStatusAsString(int agpsDataConnStatus) { 678 switch (agpsDataConnStatus) { 679 case GPS_AGPS_DATA_CONNECTED: 680 return "CONNECTED"; 681 case GPS_AGPS_DATA_CONN_DONE: 682 return "DONE"; 683 case GPS_AGPS_DATA_CONN_FAILED: 684 return "FAILED"; 685 case GPS_RELEASE_AGPS_DATA_CONN: 686 return "RELEASE"; 687 case GPS_REQUEST_AGPS_DATA_CONN: 688 return "REQUEST"; 689 default: 690 return "<Unknown>(" + agpsDataConnStatus + ")"; 691 } 692 } 693 694 private String agpsTypeAsString(int agpsType) { 695 switch (agpsType) { 696 case AGPS_TYPE_SUPL: 697 return "SUPL"; 698 case AGPS_TYPE_C2K: 699 return "C2K"; 700 case AGPS_TYPE_EIMS: 701 return "EIMS"; 702 case AGPS_TYPE_IMS: 703 return "IMS"; 704 default: 705 return "<Unknown>(" + agpsType + ")"; 706 } 707 } 708 709 private int getLinkIpType(LinkProperties linkProperties) { 710 ensureInHandlerThread(); 711 boolean isIPv4 = false; 712 boolean isIPv6 = false; 713 714 List<LinkAddress> linkAddresses = linkProperties.getLinkAddresses(); 715 for (LinkAddress linkAddress : linkAddresses) { 716 InetAddress inetAddress = linkAddress.getAddress(); 717 if (inetAddress instanceof Inet4Address) { 718 isIPv4 = true; 719 } else if (inetAddress instanceof Inet6Address) { 720 isIPv6 = true; 721 } 722 if (DEBUG) Log.d(TAG, "LinkAddress : " + inetAddress.toString()); 723 } 724 725 if (isIPv4 && isIPv6) { 726 return APN_IPV4V6; 727 } 728 if (isIPv4) { 729 return APN_IPV4; 730 } 731 if (isIPv6) { 732 return APN_IPV6; 733 } 734 return APN_INVALID; 735 } 736 737 // AGPS support 738 private native void native_agps_data_conn_open(long networkHandle, String apn, int apnIpType); 739 740 private native void native_agps_data_conn_closed(); 741 742 private native void native_agps_data_conn_failed(); 743 744 // AGPS ril support 745 private static native boolean native_is_agps_ril_supported(); 746 747 private native void native_update_network_state(boolean connected, int type, boolean roaming, 748 boolean available, String apn, long networkHandle, short capabilities); 749 } 750