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