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