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 (PreciseCallState.PRECISE_CALL_STATE_ACTIVE == state.getForegroundCallState() 219 || PreciseCallState.PRECISE_CALL_STATE_DIALING 220 == state.getForegroundCallState()) { 221 mActiveSubId = mSubId; 222 if (DEBUG) Log.d(TAG, "mActiveSubId: " + mActiveSubId); 223 } 224 } 225 }; 226 227 /** 228 * Subscription Changed Listener is used to get all active subscriptions and create a 229 * Phone State Listener for each Sub ID that we find in the active subscription list 230 */ 231 private final SubscriptionManager.OnSubscriptionsChangedListener mOnSubscriptionsChangeListener 232 = new SubscriptionManager.OnSubscriptionsChangedListener() { 233 @Override 234 public void onSubscriptionsChanged() { 235 if (mPhoneStateListeners == null) { 236 // Capacity=2 Load-Factor=1.0, as typically no more than 2 SIMs 237 mPhoneStateListeners = new HashMap<Integer, SubIdPhoneStateListener>(2,1); 238 } 239 SubscriptionManager subManager = mContext.getSystemService(SubscriptionManager.class); 240 TelephonyManager telManager = mContext.getSystemService(TelephonyManager.class); 241 if (subManager != null && telManager != null) { 242 List<SubscriptionInfo> subscriptionInfoList = 243 subManager.getActiveSubscriptionInfoList(); 244 HashSet<Integer> activeSubIds = new HashSet<Integer>(); 245 if (subscriptionInfoList != null) { 246 if (DEBUG) Log.d(TAG, "Active Sub List size: " + subscriptionInfoList.size()); 247 // populate phone state listeners with all new active subs 248 for (SubscriptionInfo subInfo : subscriptionInfoList) { 249 activeSubIds.add(subInfo.getSubscriptionId()); 250 if (!mPhoneStateListeners.containsKey(subInfo.getSubscriptionId())) { 251 TelephonyManager subIdTelManager = 252 telManager.createForSubscriptionId(subInfo.getSubscriptionId()); 253 if (subIdTelManager != null) { 254 if (DEBUG) Log.d(TAG, "Listener sub" + subInfo.getSubscriptionId()); 255 SubIdPhoneStateListener subIdPhoneStateListener = 256 new SubIdPhoneStateListener(subInfo.getSubscriptionId()); 257 mPhoneStateListeners.put(subInfo.getSubscriptionId(), 258 subIdPhoneStateListener); 259 subIdTelManager.listen(subIdPhoneStateListener, 260 PhoneStateListener.LISTEN_PRECISE_CALL_STATE); 261 } 262 } 263 } 264 } 265 // clean up phone state listeners than no longer have active subs 266 Iterator<Map.Entry<Integer, SubIdPhoneStateListener> > iterator = 267 mPhoneStateListeners.entrySet().iterator(); 268 while (iterator.hasNext()) { 269 Map.Entry<Integer, SubIdPhoneStateListener> element = iterator.next(); 270 if (!activeSubIds.contains(element.getKey())) { 271 TelephonyManager subIdTelManager = 272 telManager.createForSubscriptionId(element.getKey()); 273 if (subIdTelManager != null) { 274 if (DEBUG) Log.d(TAG, "unregister listener sub " + element.getKey()); 275 subIdTelManager.listen(element.getValue(), 276 PhoneStateListener.LISTEN_NONE); 277 // removes the element from mPhoneStateListeners 278 iterator.remove(); 279 } else { 280 Log.e(TAG, "Telephony Manager for Sub " + element.getKey() + " null"); 281 } 282 } 283 } 284 // clean up cached active phone call sub if it is no longer an active sub 285 if (!activeSubIds.contains(mActiveSubId)) { 286 mActiveSubId = -1; 287 } 288 } 289 } 290 }; 291 registerNetworkCallbacks()292 void registerNetworkCallbacks() { 293 // register for connectivity change events. 294 NetworkRequest.Builder networkRequestBuilder = new NetworkRequest.Builder(); 295 networkRequestBuilder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); 296 networkRequestBuilder.addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED); 297 networkRequestBuilder.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN); 298 NetworkRequest networkRequest = networkRequestBuilder.build(); 299 mNetworkConnectivityCallback = createNetworkConnectivityCallback(); 300 mConnMgr.registerNetworkCallback(networkRequest, mNetworkConnectivityCallback, mHandler); 301 } 302 303 /** 304 * @return {@code true} if there is a data network available for outgoing connections, 305 * {@code false} otherwise. 306 */ isDataNetworkConnected()307 boolean isDataNetworkConnected() { 308 NetworkInfo activeNetworkInfo = mConnMgr.getActiveNetworkInfo(); 309 return activeNetworkInfo != null && activeNetworkInfo.isConnected(); 310 } 311 312 /** 313 * Called from native code to update AGPS connection status, or to request or release a SUPL 314 * connection. 315 * 316 * <p>Note: {@code suplIpAddr} parameter is not present from IAGnssCallback.hal@2.0 onwards 317 * and is set to {@code null}. 318 */ onReportAGpsStatus(int agpsType, int agpsStatus, byte[] suplIpAddr)319 void onReportAGpsStatus(int agpsType, int agpsStatus, byte[] suplIpAddr) { 320 if (DEBUG) Log.d(TAG, "AGPS_DATA_CONNECTION: " + agpsDataConnStatusAsString(agpsStatus)); 321 switch (agpsStatus) { 322 case GPS_REQUEST_AGPS_DATA_CONN: 323 runOnHandler(() -> handleRequestSuplConnection(agpsType, suplIpAddr)); 324 break; 325 case GPS_RELEASE_AGPS_DATA_CONN: 326 runOnHandler(() -> handleReleaseSuplConnection(GPS_RELEASE_AGPS_DATA_CONN)); 327 break; 328 case GPS_AGPS_DATA_CONNECTED: 329 case GPS_AGPS_DATA_CONN_DONE: 330 case GPS_AGPS_DATA_CONN_FAILED: 331 break; 332 default: 333 Log.w(TAG, "Received unknown AGPS status: " + agpsStatus); 334 } 335 } 336 createNetworkConnectivityCallback()337 private ConnectivityManager.NetworkCallback createNetworkConnectivityCallback() { 338 return new ConnectivityManager.NetworkCallback() { 339 // Used to filter out network capabilities changes that we are not interested in. 340 // NOTE: Not using a ConcurrentHashMap and also not using locking around updates 341 // and access to the map object because it is all done inside the same 342 // handler thread invoking the callback methods. 343 private HashMap<Network, NetworkCapabilities> 344 mAvailableNetworkCapabilities = new HashMap<>( 345 HASH_MAP_INITIAL_CAPACITY_TO_TRACK_CONNECTED_NETWORKS); 346 347 @Override 348 public void onCapabilitiesChanged(Network network, 349 NetworkCapabilities capabilities) { 350 // This callback is invoked for any change in the network capabilities including 351 // initial availability, and changes while still available. Only process if the 352 // capabilities that we pass on to HAL change. 353 if (!NetworkAttributes.hasCapabilitiesChanged( 354 mAvailableNetworkCapabilities.get(network), capabilities)) { 355 if (VERBOSE) { 356 Log.v(TAG, "Relevant network capabilities unchanged. Capabilities: " 357 + capabilities); 358 } 359 return; 360 } 361 362 mAvailableNetworkCapabilities.put(network, capabilities); 363 if (DEBUG) { 364 Log.d(TAG, "Network connected/capabilities updated. Available networks count: " 365 + mAvailableNetworkCapabilities.size()); 366 } 367 368 mGnssNetworkListener.onNetworkAvailable(); 369 370 // Always on, notify HAL so it can get data it needs 371 handleUpdateNetworkState(network, true, capabilities); 372 } 373 374 @Override 375 public void onLost(Network network) { 376 if (mAvailableNetworkCapabilities.remove(network) == null) { 377 Log.w(TAG, "Incorrectly received network callback onLost() before" 378 + " onCapabilitiesChanged() for network: " + network); 379 return; 380 } 381 382 Log.i(TAG, "Network connection lost. Available networks count: " 383 + mAvailableNetworkCapabilities.size()); 384 handleUpdateNetworkState(network, false, null); 385 } 386 }; 387 } 388 createSuplConnectivityCallback()389 private ConnectivityManager.NetworkCallback createSuplConnectivityCallback() { 390 return new ConnectivityManager.NetworkCallback() { 391 @Override 392 public void onLinkPropertiesChanged(Network network, LinkProperties linkProperties) { 393 if (DEBUG) Log.d(TAG, "SUPL network connection available."); 394 // Specific to a change to a SUPL enabled network becoming ready 395 handleSuplConnectionAvailable(network, linkProperties); 396 } 397 398 @Override 399 public void onLost(Network network) { 400 Log.i(TAG, "SUPL network connection lost."); 401 handleReleaseSuplConnection(GPS_RELEASE_AGPS_DATA_CONN); 402 } 403 404 @Override 405 public void onUnavailable() { 406 Log.i(TAG, "SUPL network connection request timed out."); 407 // Could not setup the connection to the network in the specified time duration. 408 handleReleaseSuplConnection(GPS_AGPS_DATA_CONN_FAILED); 409 } 410 }; 411 } 412 413 private void runOnHandler(Runnable event) { 414 // hold a wake lock until this message is delivered 415 // note that this assumes the message will not be removed from the queue before 416 // it is handled (otherwise the wake lock would be leaked). 417 mWakeLock.acquire(WAKELOCK_TIMEOUT_MILLIS); 418 if (!mHandler.post(runEventAndReleaseWakeLock(event))) { 419 mWakeLock.release(); 420 } 421 } 422 423 private Runnable runEventAndReleaseWakeLock(Runnable event) { 424 return () -> { 425 try { 426 event.run(); 427 } finally { 428 mWakeLock.release(); 429 } 430 }; 431 } 432 433 private void handleUpdateNetworkState(Network network, boolean isConnected, 434 NetworkCapabilities capabilities) { 435 boolean networkAvailable = false; 436 TelephonyManager telephonyManager = mContext.getSystemService(TelephonyManager.class); 437 if (telephonyManager != null) { 438 networkAvailable = isConnected && telephonyManager.getDataEnabled(); 439 } 440 NetworkAttributes networkAttributes = updateTrackedNetworksState(isConnected, network, 441 capabilities); 442 String apn = networkAttributes.mApn; 443 int type = networkAttributes.mType; 444 // When isConnected is false, capabilities argument is null. So, use last received 445 // capabilities. 446 capabilities = networkAttributes.mCapabilities; 447 Log.i(TAG, String.format( 448 "updateNetworkState, state=%s, connected=%s, network=%s, capabilities=%s" 449 + ", availableNetworkCount: %d", 450 agpsDataConnStateAsString(), 451 isConnected, 452 network, 453 capabilities, 454 mAvailableNetworkAttributes.size())); 455 456 if (native_is_agps_ril_supported()) { 457 native_update_network_state( 458 isConnected, 459 type, 460 !capabilities.hasTransport( 461 NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING), /* isRoaming */ 462 networkAvailable, 463 apn != null ? apn : "", 464 network.getNetworkHandle(), 465 NetworkAttributes.getCapabilityFlags(capabilities)); 466 } else if (DEBUG) { 467 Log.d(TAG, "Skipped network state update because GPS HAL AGPS-RIL is not supported"); 468 } 469 } 470 471 private NetworkAttributes updateTrackedNetworksState(boolean isConnected, Network network, 472 NetworkCapabilities capabilities) { 473 if (!isConnected) { 474 // Connection lost event. So, remove it from tracked networks. 475 return mAvailableNetworkAttributes.remove(network); 476 } 477 478 NetworkAttributes networkAttributes = mAvailableNetworkAttributes.get(network); 479 if (networkAttributes != null) { 480 // Capabilities updated event for the connected network. 481 networkAttributes.mCapabilities = capabilities; 482 return networkAttributes; 483 } 484 485 // Initial capabilities event (equivalent to connection available event). 486 networkAttributes = new NetworkAttributes(); 487 networkAttributes.mCapabilities = capabilities; 488 489 // TODO: The synchronous method ConnectivityManager.getNetworkInfo() should not be called 490 // inside the asynchronous ConnectivityManager.NetworkCallback methods. 491 NetworkInfo info = mConnMgr.getNetworkInfo(network); 492 if (info != null) { 493 networkAttributes.mApn = info.getExtraInfo(); 494 networkAttributes.mType = info.getType(); 495 } 496 497 // Start tracking this network for connection status updates. 498 mAvailableNetworkAttributes.put(network, networkAttributes); 499 return networkAttributes; 500 } 501 502 private void handleSuplConnectionAvailable(Network network, LinkProperties linkProperties) { 503 // TODO: The synchronous method ConnectivityManager.getNetworkInfo() should not be called 504 // inside the asynchronous ConnectivityManager.NetworkCallback methods. 505 NetworkInfo info = mConnMgr.getNetworkInfo(network); 506 String apn = null; 507 if (info != null) { 508 apn = info.getExtraInfo(); 509 } 510 511 if (DEBUG) { 512 String message = String.format( 513 "handleSuplConnectionAvailable: state=%s, suplNetwork=%s, info=%s", 514 agpsDataConnStateAsString(), 515 network, 516 info); 517 Log.d(TAG, message); 518 } 519 520 if (mAGpsDataConnectionState == AGPS_DATA_CONNECTION_OPENING) { 521 if (apn == null) { 522 // assign a placeholder value in the case of C2K as otherwise we will have a runtime 523 // exception in the following call to native_agps_data_conn_open 524 apn = "dummy-apn"; 525 } 526 527 // Setting route to host is needed for GNSS HAL implementations earlier than 528 // @2.0::IAgnssCallback. The HAL @2.0::IAgnssCallback.agnssStatusCb() method does 529 // not require setting route to SUPL host and hence does not provide an IP address. 530 if (mAGpsDataConnectionIpAddr != null) { 531 setRouting(); 532 } 533 534 int apnIpType = getLinkIpType(linkProperties); 535 if (DEBUG) { 536 String message = String.format( 537 "native_agps_data_conn_open: mAgpsApn=%s, mApnIpType=%s", 538 apn, 539 apnIpType); 540 Log.d(TAG, message); 541 } 542 native_agps_data_conn_open(network.getNetworkHandle(), apn, apnIpType); 543 mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPEN; 544 } 545 } 546 547 private void handleRequestSuplConnection(int agpsType, byte[] suplIpAddr) { 548 mAGpsDataConnectionIpAddr = null; 549 mAGpsType = agpsType; 550 if (suplIpAddr != null) { 551 if (VERBOSE) Log.v(TAG, "Received SUPL IP addr[]: " + Arrays.toString(suplIpAddr)); 552 try { 553 mAGpsDataConnectionIpAddr = InetAddress.getByAddress(suplIpAddr); 554 if (DEBUG) Log.d(TAG, "IP address converted to: " + mAGpsDataConnectionIpAddr); 555 } catch (UnknownHostException e) { 556 Log.e(TAG, "Bad IP Address: " + suplIpAddr, e); 557 } 558 } 559 560 if (DEBUG) { 561 String message = String.format( 562 "requestSuplConnection, state=%s, agpsType=%s, address=%s", 563 agpsDataConnStateAsString(), 564 agpsTypeAsString(agpsType), 565 mAGpsDataConnectionIpAddr); 566 Log.d(TAG, message); 567 } 568 569 if (mAGpsDataConnectionState != AGPS_DATA_CONNECTION_CLOSED) { 570 return; 571 } 572 mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPENING; 573 574 // The transport type must be set to NetworkCapabilities.TRANSPORT_CELLULAR for the 575 // deprecated requestRouteToHostAddress() method in ConnectivityService to work for 576 // pre-gnss@2.0 devices. 577 NetworkRequest.Builder networkRequestBuilder = new NetworkRequest.Builder(); 578 networkRequestBuilder.addCapability(getNetworkCapability(mAGpsType)); 579 networkRequestBuilder.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR); 580 // During an emergency call, and when we have cached the Active Sub Id, we set the 581 // Network Specifier so that the network request goes to the correct Sub Id 582 if (mNiHandler.getInEmergency() && mActiveSubId >= 0) { 583 if (DEBUG) Log.d(TAG, "Adding Network Specifier: " + Integer.toString(mActiveSubId)); 584 networkRequestBuilder.setNetworkSpecifier(Integer.toString(mActiveSubId)); 585 } 586 NetworkRequest networkRequest = networkRequestBuilder.build(); 587 mConnMgr.requestNetwork( 588 networkRequest, 589 mSuplConnectivityCallback, 590 mHandler, 591 SUPL_NETWORK_REQUEST_TIMEOUT_MILLIS); 592 } 593 594 private int getNetworkCapability(int agpsType) { 595 switch (agpsType) { 596 case AGPS_TYPE_C2K: 597 case AGPS_TYPE_SUPL: 598 return NetworkCapabilities.NET_CAPABILITY_SUPL; 599 case AGPS_TYPE_EIMS: 600 return NetworkCapabilities.NET_CAPABILITY_EIMS; 601 case AGPS_TYPE_IMS: 602 return NetworkCapabilities.NET_CAPABILITY_IMS; 603 default: 604 throw new IllegalArgumentException("agpsType: " + agpsType); 605 } 606 } 607 608 private void handleReleaseSuplConnection(int agpsDataConnStatus) { 609 if (DEBUG) { 610 String message = String.format( 611 "releaseSuplConnection, state=%s, status=%s", 612 agpsDataConnStateAsString(), 613 agpsDataConnStatusAsString(agpsDataConnStatus)); 614 Log.d(TAG, message); 615 } 616 617 if (mAGpsDataConnectionState == AGPS_DATA_CONNECTION_CLOSED) { 618 return; 619 } 620 621 mAGpsDataConnectionState = AGPS_DATA_CONNECTION_CLOSED; 622 mConnMgr.unregisterNetworkCallback(mSuplConnectivityCallback); 623 switch (agpsDataConnStatus) { 624 case GPS_AGPS_DATA_CONN_FAILED: 625 native_agps_data_conn_failed(); 626 break; 627 case GPS_RELEASE_AGPS_DATA_CONN: 628 native_agps_data_conn_closed(); 629 break; 630 default: 631 Log.e(TAG, "Invalid status to release SUPL connection: " + agpsDataConnStatus); 632 } 633 } 634 635 // TODO: Delete this method when all devices upgrade to HAL @2.0::IAGnssCallback 636 // interface which does not require setting route to host. 637 private void setRouting() { 638 boolean result = mConnMgr.requestRouteToHostAddress( 639 ConnectivityManager.TYPE_MOBILE_SUPL, 640 mAGpsDataConnectionIpAddr); 641 642 if (!result) { 643 Log.e(TAG, "Error requesting route to host: " + mAGpsDataConnectionIpAddr); 644 } else if (DEBUG) { 645 Log.d(TAG, "Successfully requested route to host: " + mAGpsDataConnectionIpAddr); 646 } 647 } 648 649 /** 650 * Ensures the calling function is running in the thread associated with {@link #mHandler}. 651 */ 652 private void ensureInHandlerThread() { 653 if (mHandler != null && Looper.myLooper() == mHandler.getLooper()) { 654 return; 655 } 656 throw new IllegalStateException("This method must run on the Handler thread."); 657 } 658 659 /** 660 * @return A string representing the current state stored in {@link #mAGpsDataConnectionState}. 661 */ 662 private String agpsDataConnStateAsString() { 663 switch (mAGpsDataConnectionState) { 664 case AGPS_DATA_CONNECTION_CLOSED: 665 return "CLOSED"; 666 case AGPS_DATA_CONNECTION_OPEN: 667 return "OPEN"; 668 case AGPS_DATA_CONNECTION_OPENING: 669 return "OPENING"; 670 default: 671 return "<Unknown>(" + mAGpsDataConnectionState + ")"; 672 } 673 } 674 675 /** 676 * @return A string representing the given GPS_AGPS_DATA status. 677 */ 678 private String agpsDataConnStatusAsString(int agpsDataConnStatus) { 679 switch (agpsDataConnStatus) { 680 case GPS_AGPS_DATA_CONNECTED: 681 return "CONNECTED"; 682 case GPS_AGPS_DATA_CONN_DONE: 683 return "DONE"; 684 case GPS_AGPS_DATA_CONN_FAILED: 685 return "FAILED"; 686 case GPS_RELEASE_AGPS_DATA_CONN: 687 return "RELEASE"; 688 case GPS_REQUEST_AGPS_DATA_CONN: 689 return "REQUEST"; 690 default: 691 return "<Unknown>(" + agpsDataConnStatus + ")"; 692 } 693 } 694 695 private String agpsTypeAsString(int agpsType) { 696 switch (agpsType) { 697 case AGPS_TYPE_SUPL: 698 return "SUPL"; 699 case AGPS_TYPE_C2K: 700 return "C2K"; 701 case AGPS_TYPE_EIMS: 702 return "EIMS"; 703 case AGPS_TYPE_IMS: 704 return "IMS"; 705 default: 706 return "<Unknown>(" + agpsType + ")"; 707 } 708 } 709 710 private int getLinkIpType(LinkProperties linkProperties) { 711 ensureInHandlerThread(); 712 boolean isIPv4 = false; 713 boolean isIPv6 = false; 714 715 List<LinkAddress> linkAddresses = linkProperties.getLinkAddresses(); 716 for (LinkAddress linkAddress : linkAddresses) { 717 InetAddress inetAddress = linkAddress.getAddress(); 718 if (inetAddress instanceof Inet4Address) { 719 isIPv4 = true; 720 } else if (inetAddress instanceof Inet6Address) { 721 isIPv6 = true; 722 } 723 if (DEBUG) Log.d(TAG, "LinkAddress : " + inetAddress.toString()); 724 } 725 726 if (isIPv4 && isIPv6) { 727 return APN_IPV4V6; 728 } 729 if (isIPv4) { 730 return APN_IPV4; 731 } 732 if (isIPv6) { 733 return APN_IPV6; 734 } 735 return APN_INVALID; 736 } 737 738 // AGPS support 739 private native void native_agps_data_conn_open(long networkHandle, String apn, int apnIpType); 740 741 private native void native_agps_data_conn_closed(); 742 743 private native void native_agps_data_conn_failed(); 744 745 // AGPS ril support 746 private static native boolean native_is_agps_ril_supported(); 747 748 private native void native_update_network_state(boolean connected, int type, boolean roaming, 749 boolean available, String apn, long networkHandle, short capabilities); 750 } 751