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