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