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