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