• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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