blob: 93227bd78a8122e1d584979be1fffbe54ed198ff [file] [log] [blame]
Anil Admal50ba15e2018-11-01 16:42:42 -07001/*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.location;
18
19import android.content.Context;
20import android.database.Cursor;
21import android.net.ConnectivityManager;
22import android.net.Network;
23import android.net.NetworkCapabilities;
24import android.net.NetworkInfo;
25import android.net.NetworkRequest;
Anil Admal50ba15e2018-11-01 16:42:42 -070026import android.os.Handler;
27import android.os.Looper;
28import android.os.PowerManager;
29import android.provider.Telephony.Carriers;
Shinru Hana415dab2019-03-21 16:14:24 +080030import android.telephony.ServiceState;
Anil Admal50ba15e2018-11-01 16:42:42 -070031import android.telephony.TelephonyManager;
32import android.util.Log;
33
34import java.net.InetAddress;
35import java.net.UnknownHostException;
36import java.util.Arrays;
37import java.util.HashMap;
Anil Admal50ba15e2018-11-01 16:42:42 -070038
39/**
40 * Handles network connection requests and network state change updates for AGPS data download.
41 */
42class GnssNetworkConnectivityHandler {
43 static final String TAG = "GnssNetworkConnectivityHandler";
44
45 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
46 private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
47
48 // for mAGpsDataConnectionState
49 private static final int AGPS_DATA_CONNECTION_CLOSED = 0;
50 private static final int AGPS_DATA_CONNECTION_OPENING = 1;
51 private static final int AGPS_DATA_CONNECTION_OPEN = 2;
52
53 // these need to match AGnssStatusValue enum in IAGnssCallback.hal
54 /** AGPS status event values. */
55 private static final int GPS_REQUEST_AGPS_DATA_CONN = 1;
56 private static final int GPS_RELEASE_AGPS_DATA_CONN = 2;
57 private static final int GPS_AGPS_DATA_CONNECTED = 3;
58 private static final int GPS_AGPS_DATA_CONN_DONE = 4;
59 private static final int GPS_AGPS_DATA_CONN_FAILED = 5;
60
61 // these must match the ApnIpType enum in IAGnss.hal
62 private static final int APN_INVALID = 0;
63 private static final int APN_IPV4 = 1;
64 private static final int APN_IPV6 = 2;
65 private static final int APN_IPV4V6 = 3;
66
Anil Admalb4995e22018-11-16 17:36:21 -080067 // these must match the NetworkCapability enum flags in IAGnssRil.hal
68 private static final int AGNSS_NET_CAPABILITY_NOT_METERED = 1 << 0;
69 private static final int AGNSS_NET_CAPABILITY_NOT_ROAMING = 1 << 1;
70
Anil Admalc70344b2018-11-16 14:22:38 -080071 // these need to match AGnssType enum in IAGnssCallback.hal
72 public static final int AGPS_TYPE_SUPL = 1;
73 public static final int AGPS_TYPE_C2K = 2;
74 private static final int AGPS_TYPE_EIMS = 3;
75 private static final int AGPS_TYPE_IMS = 4;
76
Anil Admal50ba15e2018-11-01 16:42:42 -070077 // Default time limit in milliseconds for the ConnectivityManager to find a suitable
78 // network with SUPL connectivity or report an error.
79 private static final int SUPL_NETWORK_REQUEST_TIMEOUT_MILLIS = 10 * 1000;
80
81 private static final int HASH_MAP_INITIAL_CAPACITY_TO_TRACK_CONNECTED_NETWORKS = 5;
82
Anil Admal701c5e32019-01-24 18:10:46 -080083 // Keeps track of networks and their state as notified by the network request callbacks.
Anil Admal50ba15e2018-11-01 16:42:42 -070084 // Limit initial capacity to 5 as the number of connected networks will likely be small.
Anil Admal701c5e32019-01-24 18:10:46 -080085 // NOTE: Must be accessed/modified only through the mHandler thread.
86 private HashMap<Network, NetworkAttributes> mAvailableNetworkAttributes =
87 new HashMap<>(HASH_MAP_INITIAL_CAPACITY_TO_TRACK_CONNECTED_NETWORKS);
Anil Admal50ba15e2018-11-01 16:42:42 -070088
89 private final ConnectivityManager mConnMgr;
90
91 private final Handler mHandler;
92 private final GnssNetworkListener mGnssNetworkListener;
93
94 private int mAGpsDataConnectionState;
95 private InetAddress mAGpsDataConnectionIpAddr;
Anil Admalc70344b2018-11-16 14:22:38 -080096 private int mAGpsType;
Anil Admal50ba15e2018-11-01 16:42:42 -070097
98 private final Context mContext;
99
100 // Wakelocks
101 private static final String WAKELOCK_KEY = "GnssNetworkConnectivityHandler";
102 private static final long WAKELOCK_TIMEOUT_MILLIS = 60 * 1000;
103 private final PowerManager.WakeLock mWakeLock;
104
105 /**
106 * Network attributes needed when updating HAL about network connectivity status changes.
107 */
108 private static class NetworkAttributes {
Anil Admalb4995e22018-11-16 17:36:21 -0800109 private NetworkCapabilities mCapabilities;
110 private String mApn;
111 private int mType = ConnectivityManager.TYPE_NONE;
Anil Admal50ba15e2018-11-01 16:42:42 -0700112
113 /**
114 * Returns true if the capabilities that we pass on to HAL change between {@curCapabilities}
115 * and {@code newCapabilities}.
116 */
Anil Admalb4995e22018-11-16 17:36:21 -0800117 private static boolean hasCapabilitiesChanged(NetworkCapabilities curCapabilities,
Anil Admal50ba15e2018-11-01 16:42:42 -0700118 NetworkCapabilities newCapabilities) {
119 if (curCapabilities == null || newCapabilities == null) {
120 return true;
121 }
122
Anil Admalb4995e22018-11-16 17:36:21 -0800123 // Monitor for roaming and metered capability changes.
124 return hasCapabilityChanged(curCapabilities, newCapabilities,
125 NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING)
126 || hasCapabilityChanged(curCapabilities, newCapabilities,
127 NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
128 }
129
130 private static boolean hasCapabilityChanged(NetworkCapabilities curCapabilities,
131 NetworkCapabilities newCapabilities, int capability) {
132 return curCapabilities.hasCapability(capability)
133 != newCapabilities.hasCapability(capability);
134 }
135
136 private static short getCapabilityFlags(NetworkCapabilities capabilities) {
137 short capabilityFlags = 0;
138 if (capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING)) {
139 capabilityFlags |= AGNSS_NET_CAPABILITY_NOT_ROAMING;
140 }
141 if (capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)) {
142 capabilityFlags |= AGNSS_NET_CAPABILITY_NOT_METERED;
143 }
144 return capabilityFlags;
Anil Admal50ba15e2018-11-01 16:42:42 -0700145 }
146 }
147
148 /**
149 * Callback used to listen for data connectivity changes.
150 */
151 private ConnectivityManager.NetworkCallback mNetworkConnectivityCallback;
152
153 /**
154 * Callback used to listen for availability of a requested SUPL connection.
155 * It is kept as a separate instance from {@link #mNetworkConnectivityCallback} to be able to
156 * manage the registration/un-registration lifetimes separately.
157 */
158 private ConnectivityManager.NetworkCallback mSuplConnectivityCallback;
159
160 /**
161 * Interface to listen for network availability changes.
162 */
Anil Admal701c5e32019-01-24 18:10:46 -0800163 interface GnssNetworkListener {
Anil Admal50ba15e2018-11-01 16:42:42 -0700164 void onNetworkAvailable();
165 }
166
167 GnssNetworkConnectivityHandler(Context context,
168 GnssNetworkListener gnssNetworkListener,
169 Looper looper) {
170 mContext = context;
171 mGnssNetworkListener = gnssNetworkListener;
172
173 PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
174 mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
175
176 mHandler = new Handler(looper);
177 mConnMgr = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
178 mSuplConnectivityCallback = createSuplConnectivityCallback();
179 }
180
Anil Admal701c5e32019-01-24 18:10:46 -0800181 void registerNetworkCallbacks() {
Anil Admal50ba15e2018-11-01 16:42:42 -0700182 // register for connectivity change events.
183 NetworkRequest.Builder networkRequestBuilder = new NetworkRequest.Builder();
184 networkRequestBuilder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
185 networkRequestBuilder.addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED);
186 networkRequestBuilder.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN);
187 NetworkRequest networkRequest = networkRequestBuilder.build();
188 mNetworkConnectivityCallback = createNetworkConnectivityCallback();
Anil Admal701c5e32019-01-24 18:10:46 -0800189 mConnMgr.registerNetworkCallback(networkRequest, mNetworkConnectivityCallback, mHandler);
Anil Admal50ba15e2018-11-01 16:42:42 -0700190 }
191
192 /**
193 * @return {@code true} if there is a data network available for outgoing connections,
194 * {@code false} otherwise.
195 */
Anil Admal701c5e32019-01-24 18:10:46 -0800196 boolean isDataNetworkConnected() {
Anil Admal50ba15e2018-11-01 16:42:42 -0700197 NetworkInfo activeNetworkInfo = mConnMgr.getActiveNetworkInfo();
198 return activeNetworkInfo != null && activeNetworkInfo.isConnected();
199 }
200
201 /**
Anil Admal84b19b42019-04-06 17:37:22 -0700202 * Called from native code to update AGPS connection status, or to request or release a SUPL
203 * connection.
204 *
205 * <p>Note: {@code suplIpAddr} parameter is not present from IAGnssCallback.hal@2.0 onwards
206 * and is set to {@code null}.
Anil Admal50ba15e2018-11-01 16:42:42 -0700207 */
Anil Admal701c5e32019-01-24 18:10:46 -0800208 void onReportAGpsStatus(int agpsType, int agpsStatus, byte[] suplIpAddr) {
Anil Admal84b19b42019-04-06 17:37:22 -0700209 if (DEBUG) Log.d(TAG, "AGPS_DATA_CONNECTION: " + agpsDataConnStatusAsString(agpsStatus));
Anil Admalc70344b2018-11-16 14:22:38 -0800210 switch (agpsStatus) {
Anil Admal50ba15e2018-11-01 16:42:42 -0700211 case GPS_REQUEST_AGPS_DATA_CONN:
Anil Admal701c5e32019-01-24 18:10:46 -0800212 runOnHandler(() -> handleRequestSuplConnection(agpsType, suplIpAddr));
Anil Admal50ba15e2018-11-01 16:42:42 -0700213 break;
214 case GPS_RELEASE_AGPS_DATA_CONN:
Anil Admal701c5e32019-01-24 18:10:46 -0800215 runOnHandler(() -> handleReleaseSuplConnection(GPS_RELEASE_AGPS_DATA_CONN));
Anil Admal50ba15e2018-11-01 16:42:42 -0700216 break;
217 case GPS_AGPS_DATA_CONNECTED:
Anil Admal50ba15e2018-11-01 16:42:42 -0700218 case GPS_AGPS_DATA_CONN_DONE:
Anil Admal50ba15e2018-11-01 16:42:42 -0700219 case GPS_AGPS_DATA_CONN_FAILED:
Anil Admal50ba15e2018-11-01 16:42:42 -0700220 break;
221 default:
Anil Admal84b19b42019-04-06 17:37:22 -0700222 Log.w(TAG, "Received unknown AGPS status: " + agpsStatus);
Anil Admal50ba15e2018-11-01 16:42:42 -0700223 }
224 }
225
226 private ConnectivityManager.NetworkCallback createNetworkConnectivityCallback() {
227 return new ConnectivityManager.NetworkCallback() {
228 // Used to filter out network capabilities changes that we are not interested in.
229 // NOTE: Not using a ConcurrentHashMap and also not using locking around updates
230 // and access to the map object because it is all done inside the same
231 // handler thread invoking the callback methods.
232 private HashMap<Network, NetworkCapabilities>
233 mAvailableNetworkCapabilities = new HashMap<>(
234 HASH_MAP_INITIAL_CAPACITY_TO_TRACK_CONNECTED_NETWORKS);
235
236 @Override
237 public void onCapabilitiesChanged(Network network,
238 NetworkCapabilities capabilities) {
239 // This callback is invoked for any change in the network capabilities including
240 // initial availability, and changes while still available. Only process if the
241 // capabilities that we pass on to HAL change.
242 if (!NetworkAttributes.hasCapabilitiesChanged(
243 mAvailableNetworkCapabilities.get(network), capabilities)) {
244 if (VERBOSE) {
245 Log.v(TAG, "Relevant network capabilities unchanged. Capabilities: "
246 + capabilities);
247 }
248 return;
249 }
250
251 mAvailableNetworkCapabilities.put(network, capabilities);
252 if (DEBUG) {
253 Log.d(TAG, "Network connected/capabilities updated. Available networks count: "
254 + mAvailableNetworkCapabilities.size());
255 }
256
257 mGnssNetworkListener.onNetworkAvailable();
258
259 // Always on, notify HAL so it can get data it needs
Anil Admal701c5e32019-01-24 18:10:46 -0800260 handleUpdateNetworkState(network, true, capabilities);
Anil Admal50ba15e2018-11-01 16:42:42 -0700261 }
262
263 @Override
264 public void onLost(Network network) {
265 if (mAvailableNetworkCapabilities.remove(network) == null) {
266 Log.w(TAG, "Incorrectly received network callback onLost() before"
267 + " onCapabilitiesChanged() for network: " + network);
268 return;
269 }
270
271 Log.i(TAG, "Network connection lost. Available networks count: "
272 + mAvailableNetworkCapabilities.size());
Anil Admal701c5e32019-01-24 18:10:46 -0800273 handleUpdateNetworkState(network, false, null);
Anil Admal50ba15e2018-11-01 16:42:42 -0700274 }
275 };
276 }
277
278 private ConnectivityManager.NetworkCallback createSuplConnectivityCallback() {
279 return new ConnectivityManager.NetworkCallback() {
280 @Override
281 public void onAvailable(Network network) {
282 if (DEBUG) Log.d(TAG, "SUPL network connection available.");
283 // Specific to a change to a SUPL enabled network becoming ready
Anil Admal701c5e32019-01-24 18:10:46 -0800284 handleSuplConnectionAvailable(network);
Anil Admal50ba15e2018-11-01 16:42:42 -0700285 }
286
287 @Override
288 public void onLost(Network network) {
289 Log.i(TAG, "SUPL network connection lost.");
Anil Admal701c5e32019-01-24 18:10:46 -0800290 handleReleaseSuplConnection(GPS_RELEASE_AGPS_DATA_CONN);
Anil Admal50ba15e2018-11-01 16:42:42 -0700291 }
292
293 @Override
294 public void onUnavailable() {
295 Log.i(TAG, "SUPL network connection request timed out.");
296 // Could not setup the connection to the network in the specified time duration.
Anil Admal701c5e32019-01-24 18:10:46 -0800297 handleReleaseSuplConnection(GPS_AGPS_DATA_CONN_FAILED);
Anil Admal50ba15e2018-11-01 16:42:42 -0700298 }
299 };
300 }
301
Anil Admal701c5e32019-01-24 18:10:46 -0800302 private void runOnHandler(Runnable event) {
Anil Admal50ba15e2018-11-01 16:42:42 -0700303 // hold a wake lock until this message is delivered
304 // note that this assumes the message will not be removed from the queue before
305 // it is handled (otherwise the wake lock would be leaked).
306 mWakeLock.acquire(WAKELOCK_TIMEOUT_MILLIS);
307 if (!mHandler.post(runEventAndReleaseWakeLock(event))) {
308 mWakeLock.release();
309 }
310 }
311
312 private Runnable runEventAndReleaseWakeLock(Runnable event) {
313 return () -> {
314 try {
315 event.run();
316 } finally {
317 mWakeLock.release();
318 }
319 };
320 }
321
322 private void handleUpdateNetworkState(Network network, boolean isConnected,
323 NetworkCapabilities capabilities) {
Jayachandran C3b78d752019-11-15 16:26:51 -0800324 boolean networkAvailable = false;
325 TelephonyManager telephonyManager = mContext.getSystemService(TelephonyManager.class);
326 if (telephonyManager != null) {
327 networkAvailable = isConnected && telephonyManager.getDataEnabled();
328 }
Anil Admal50ba15e2018-11-01 16:42:42 -0700329 NetworkAttributes networkAttributes = updateTrackedNetworksState(isConnected, network,
330 capabilities);
Anil Admalb4995e22018-11-16 17:36:21 -0800331 String apn = networkAttributes.mApn;
Anil Admal50ba15e2018-11-01 16:42:42 -0700332 int type = networkAttributes.mType;
333 // When isConnected is false, capabilities argument is null. So, use last received
334 // capabilities.
335 capabilities = networkAttributes.mCapabilities;
Anil Admal50ba15e2018-11-01 16:42:42 -0700336 Log.i(TAG, String.format(
337 "updateNetworkState, state=%s, connected=%s, network=%s, capabilities=%s"
Anil Admalb4995e22018-11-16 17:36:21 -0800338 + ", apn: %s, availableNetworkCount: %d",
Anil Admal50ba15e2018-11-01 16:42:42 -0700339 agpsDataConnStateAsString(),
340 isConnected,
341 network,
342 capabilities,
Anil Admalb4995e22018-11-16 17:36:21 -0800343 apn,
Anil Admal50ba15e2018-11-01 16:42:42 -0700344 mAvailableNetworkAttributes.size()));
345
346 if (native_is_agps_ril_supported()) {
Anil Admal50ba15e2018-11-01 16:42:42 -0700347 native_update_network_state(
348 isConnected,
349 type,
Anil Admalb4995e22018-11-16 17:36:21 -0800350 !capabilities.hasTransport(
351 NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING), /* isRoaming */
Anil Admal50ba15e2018-11-01 16:42:42 -0700352 networkAvailable,
Anil Admalb4995e22018-11-16 17:36:21 -0800353 apn != null ? apn : "",
354 network.getNetworkHandle(),
355 NetworkAttributes.getCapabilityFlags(capabilities));
Anil Admal50ba15e2018-11-01 16:42:42 -0700356 } else if (DEBUG) {
357 Log.d(TAG, "Skipped network state update because GPS HAL AGPS-RIL is not supported");
358 }
359 }
360
361 private NetworkAttributes updateTrackedNetworksState(boolean isConnected, Network network,
362 NetworkCapabilities capabilities) {
363 if (!isConnected) {
364 // Connection lost event. So, remove it from tracked networks.
365 return mAvailableNetworkAttributes.remove(network);
366 }
367
368 NetworkAttributes networkAttributes = mAvailableNetworkAttributes.get(network);
369 if (networkAttributes != null) {
370 // Capabilities updated event for the connected network.
371 networkAttributes.mCapabilities = capabilities;
372 return networkAttributes;
373 }
374
375 // Initial capabilities event (equivalent to connection available event).
376 networkAttributes = new NetworkAttributes();
377 networkAttributes.mCapabilities = capabilities;
378
Anil Admalc25a2712019-02-15 16:48:48 -0800379 // TODO: The synchronous method ConnectivityManager.getNetworkInfo() should not be called
380 // inside the asynchronous ConnectivityManager.NetworkCallback methods.
Anil Admal50ba15e2018-11-01 16:42:42 -0700381 NetworkInfo info = mConnMgr.getNetworkInfo(network);
382 if (info != null) {
383 networkAttributes.mApn = info.getExtraInfo();
384 networkAttributes.mType = info.getType();
385 }
386
387 // Start tracking this network for connection status updates.
388 mAvailableNetworkAttributes.put(network, networkAttributes);
389 return networkAttributes;
390 }
391
392 private void handleSuplConnectionAvailable(Network network) {
Anil Admalc25a2712019-02-15 16:48:48 -0800393 // TODO: The synchronous method ConnectivityManager.getNetworkInfo() should not be called
394 // inside the asynchronous ConnectivityManager.NetworkCallback methods.
Anil Admal50ba15e2018-11-01 16:42:42 -0700395 NetworkInfo info = mConnMgr.getNetworkInfo(network);
Anil Admalb4995e22018-11-16 17:36:21 -0800396 String apn = null;
Anil Admal50ba15e2018-11-01 16:42:42 -0700397 if (info != null) {
Anil Admalb4995e22018-11-16 17:36:21 -0800398 apn = info.getExtraInfo();
Anil Admal50ba15e2018-11-01 16:42:42 -0700399 }
400
401 if (DEBUG) {
402 String message = String.format(
403 "handleSuplConnectionAvailable: state=%s, suplNetwork=%s, info=%s",
404 agpsDataConnStateAsString(),
405 network,
406 info);
407 Log.d(TAG, message);
408 }
409
410 if (mAGpsDataConnectionState == AGPS_DATA_CONNECTION_OPENING) {
Anil Admalb4995e22018-11-16 17:36:21 -0800411 if (apn == null) {
Anil Admal50ba15e2018-11-01 16:42:42 -0700412 // assign a dummy value in the case of C2K as otherwise we will have a runtime
413 // exception in the following call to native_agps_data_conn_open
Anil Admalb4995e22018-11-16 17:36:21 -0800414 apn = "dummy-apn";
Anil Admal50ba15e2018-11-01 16:42:42 -0700415 }
Anil Admalc70344b2018-11-16 14:22:38 -0800416
417 // Setting route to host is needed for GNSS HAL implementations earlier than
418 // @2.0::IAgnssCallback. The HAL @2.0::IAgnssCallback.agnssStatusCb() method does
419 // not require setting route to SUPL host and hence does not provide an IP address.
420 if (mAGpsDataConnectionIpAddr != null) {
421 setRouting();
422 }
423
Anil Admalb4995e22018-11-16 17:36:21 -0800424 int apnIpType = getApnIpType(apn);
Anil Admal50ba15e2018-11-01 16:42:42 -0700425 if (DEBUG) {
426 String message = String.format(
427 "native_agps_data_conn_open: mAgpsApn=%s, mApnIpType=%s",
Anil Admalb4995e22018-11-16 17:36:21 -0800428 apn,
Anil Admal50ba15e2018-11-01 16:42:42 -0700429 apnIpType);
430 Log.d(TAG, message);
431 }
Anil Admalc70344b2018-11-16 14:22:38 -0800432 native_agps_data_conn_open(network.getNetworkHandle(), apn, apnIpType);
Anil Admal50ba15e2018-11-01 16:42:42 -0700433 mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPEN;
434 }
435 }
436
Anil Admalc70344b2018-11-16 14:22:38 -0800437 private void handleRequestSuplConnection(int agpsType, byte[] suplIpAddr) {
438 mAGpsDataConnectionIpAddr = null;
439 mAGpsType = agpsType;
440 if (suplIpAddr != null) {
441 if (VERBOSE) Log.v(TAG, "Received SUPL IP addr[]: " + Arrays.toString(suplIpAddr));
442 try {
443 mAGpsDataConnectionIpAddr = InetAddress.getByAddress(suplIpAddr);
444 if (DEBUG) Log.d(TAG, "IP address converted to: " + mAGpsDataConnectionIpAddr);
445 } catch (UnknownHostException e) {
446 Log.e(TAG, "Bad IP Address: " + suplIpAddr, e);
447 }
448 }
449
Anil Admal50ba15e2018-11-01 16:42:42 -0700450 if (DEBUG) {
451 String message = String.format(
Anil Admalc70344b2018-11-16 14:22:38 -0800452 "requestSuplConnection, state=%s, agpsType=%s, address=%s",
Anil Admal50ba15e2018-11-01 16:42:42 -0700453 agpsDataConnStateAsString(),
Anil Admalc70344b2018-11-16 14:22:38 -0800454 agpsTypeAsString(agpsType),
455 mAGpsDataConnectionIpAddr);
Anil Admal50ba15e2018-11-01 16:42:42 -0700456 Log.d(TAG, message);
457 }
458
459 if (mAGpsDataConnectionState != AGPS_DATA_CONNECTION_CLOSED) {
460 return;
461 }
Anil Admal50ba15e2018-11-01 16:42:42 -0700462 mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPENING;
463
Anil Admalfc5f2d12019-04-10 09:26:42 -0700464 // The transport type must be set to NetworkCapabilities.TRANSPORT_CELLULAR for the
465 // deprecated requestRouteToHostAddress() method in ConnectivityService to work for
466 // pre-gnss@2.0 devices.
467 NetworkRequest.Builder networkRequestBuilder = new NetworkRequest.Builder();
468 networkRequestBuilder.addCapability(getNetworkCapability(mAGpsType));
469 networkRequestBuilder.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
470 NetworkRequest networkRequest = networkRequestBuilder.build();
Anil Admal50ba15e2018-11-01 16:42:42 -0700471 mConnMgr.requestNetwork(
Anil Admal84b19b42019-04-06 17:37:22 -0700472 networkRequest,
Anil Admal50ba15e2018-11-01 16:42:42 -0700473 mSuplConnectivityCallback,
Anil Admal701c5e32019-01-24 18:10:46 -0800474 mHandler,
Anil Admal50ba15e2018-11-01 16:42:42 -0700475 SUPL_NETWORK_REQUEST_TIMEOUT_MILLIS);
476 }
477
Anil Admalc70344b2018-11-16 14:22:38 -0800478 private int getNetworkCapability(int agpsType) {
479 switch (agpsType) {
480 case AGPS_TYPE_C2K:
481 case AGPS_TYPE_SUPL:
482 return NetworkCapabilities.NET_CAPABILITY_SUPL;
483 case AGPS_TYPE_EIMS:
484 return NetworkCapabilities.NET_CAPABILITY_EIMS;
485 case AGPS_TYPE_IMS:
486 return NetworkCapabilities.NET_CAPABILITY_IMS;
487 default:
488 throw new IllegalArgumentException("agpsType: " + agpsType);
489 }
490 }
491
Anil Admal50ba15e2018-11-01 16:42:42 -0700492 private void handleReleaseSuplConnection(int agpsDataConnStatus) {
493 if (DEBUG) {
494 String message = String.format(
495 "releaseSuplConnection, state=%s, status=%s",
496 agpsDataConnStateAsString(),
497 agpsDataConnStatusAsString(agpsDataConnStatus));
498 Log.d(TAG, message);
499 }
500
501 if (mAGpsDataConnectionState == AGPS_DATA_CONNECTION_CLOSED) {
502 return;
503 }
Anil Admal50ba15e2018-11-01 16:42:42 -0700504
Anil Admalc70344b2018-11-16 14:22:38 -0800505 mAGpsDataConnectionState = AGPS_DATA_CONNECTION_CLOSED;
Anil Admal50ba15e2018-11-01 16:42:42 -0700506 mConnMgr.unregisterNetworkCallback(mSuplConnectivityCallback);
507 switch (agpsDataConnStatus) {
508 case GPS_AGPS_DATA_CONN_FAILED:
509 native_agps_data_conn_failed();
510 break;
511 case GPS_RELEASE_AGPS_DATA_CONN:
512 native_agps_data_conn_closed();
513 break;
514 default:
515 Log.e(TAG, "Invalid status to release SUPL connection: " + agpsDataConnStatus);
516 }
517 }
518
Anil Admalc25a2712019-02-15 16:48:48 -0800519 // TODO: Delete this method when all devices upgrade to HAL @2.0::IAGnssCallback
520 // interface which does not require setting route to host.
Anil Admal50ba15e2018-11-01 16:42:42 -0700521 private void setRouting() {
Anil Admal50ba15e2018-11-01 16:42:42 -0700522 boolean result = mConnMgr.requestRouteToHostAddress(
523 ConnectivityManager.TYPE_MOBILE_SUPL,
524 mAGpsDataConnectionIpAddr);
525
526 if (!result) {
527 Log.e(TAG, "Error requesting route to host: " + mAGpsDataConnectionIpAddr);
528 } else if (DEBUG) {
529 Log.d(TAG, "Successfully requested route to host: " + mAGpsDataConnectionIpAddr);
530 }
531 }
532
533 /**
534 * Ensures the calling function is running in the thread associated with {@link #mHandler}.
535 */
536 private void ensureInHandlerThread() {
537 if (mHandler != null && Looper.myLooper() == mHandler.getLooper()) {
538 return;
539 }
540 throw new IllegalStateException("This method must run on the Handler thread.");
541 }
542
543 /**
544 * @return A string representing the current state stored in {@link #mAGpsDataConnectionState}.
545 */
546 private String agpsDataConnStateAsString() {
547 switch (mAGpsDataConnectionState) {
548 case AGPS_DATA_CONNECTION_CLOSED:
549 return "CLOSED";
550 case AGPS_DATA_CONNECTION_OPEN:
551 return "OPEN";
552 case AGPS_DATA_CONNECTION_OPENING:
553 return "OPENING";
554 default:
Anil Admal84b19b42019-04-06 17:37:22 -0700555 return "<Unknown>(" + mAGpsDataConnectionState + ")";
Anil Admal50ba15e2018-11-01 16:42:42 -0700556 }
557 }
558
559 /**
560 * @return A string representing the given GPS_AGPS_DATA status.
561 */
562 private String agpsDataConnStatusAsString(int agpsDataConnStatus) {
563 switch (agpsDataConnStatus) {
564 case GPS_AGPS_DATA_CONNECTED:
565 return "CONNECTED";
566 case GPS_AGPS_DATA_CONN_DONE:
567 return "DONE";
568 case GPS_AGPS_DATA_CONN_FAILED:
569 return "FAILED";
570 case GPS_RELEASE_AGPS_DATA_CONN:
571 return "RELEASE";
572 case GPS_REQUEST_AGPS_DATA_CONN:
573 return "REQUEST";
574 default:
Anil Admal84b19b42019-04-06 17:37:22 -0700575 return "<Unknown>(" + agpsDataConnStatus + ")";
Anil Admal50ba15e2018-11-01 16:42:42 -0700576 }
577 }
578
Anil Admalc70344b2018-11-16 14:22:38 -0800579 private String agpsTypeAsString(int agpsType) {
580 switch (agpsType) {
581 case AGPS_TYPE_SUPL:
582 return "SUPL";
583 case AGPS_TYPE_C2K:
584 return "C2K";
585 case AGPS_TYPE_EIMS:
586 return "EIMS";
587 case AGPS_TYPE_IMS:
588 return "IMS";
589 default:
Anil Admal84b19b42019-04-06 17:37:22 -0700590 return "<Unknown>(" + agpsType + ")";
Anil Admalc70344b2018-11-16 14:22:38 -0800591 }
592 }
593
Anil Admal50ba15e2018-11-01 16:42:42 -0700594 private int getApnIpType(String apn) {
595 ensureInHandlerThread();
596 if (apn == null) {
597 return APN_INVALID;
598 }
599 TelephonyManager phone = (TelephonyManager)
600 mContext.getSystemService(Context.TELEPHONY_SERVICE);
Shinru Hana415dab2019-03-21 16:14:24 +0800601 ServiceState serviceState = phone.getServiceState();
602 String projection = null;
603 String selection = null;
604
Anil Admal50ba15e2018-11-01 16:42:42 -0700605 // Carrier configuration may override framework roaming state, we need to use the actual
606 // modem roaming state instead of the framework roaming state.
Shinru Hana415dab2019-03-21 16:14:24 +0800607 if (serviceState != null && serviceState.getDataRoamingFromRegistration()) {
608 projection = Carriers.ROAMING_PROTOCOL;
609 } else {
610 projection = Carriers.PROTOCOL;
611 }
612 // No SIM case for emergency
613 if (TelephonyManager.NETWORK_TYPE_UNKNOWN == phone.getNetworkType()
614 && AGPS_TYPE_EIMS == mAGpsType) {
615 selection = String.format(
616 "type like '%%emergency%%' and apn = '%s' and carrier_enabled = 1", apn);
617 } else {
618 selection = String.format("current = 1 and apn = '%s' and carrier_enabled = 1", apn);
619 }
Anil Admal50ba15e2018-11-01 16:42:42 -0700620 try (Cursor cursor = mContext.getContentResolver().query(
621 Carriers.CONTENT_URI,
622 new String[]{projection},
623 selection,
624 null,
625 Carriers.DEFAULT_SORT_ORDER)) {
626 if (null != cursor && cursor.moveToFirst()) {
627 return translateToApnIpType(cursor.getString(0), apn);
628 } else {
629 Log.e(TAG, "No entry found in query for APN: " + apn);
630 }
631 } catch (Exception e) {
632 Log.e(TAG, "Error encountered on APN query for: " + apn, e);
633 }
634
Shinru Hana415dab2019-03-21 16:14:24 +0800635 return APN_IPV4V6;
Anil Admal50ba15e2018-11-01 16:42:42 -0700636 }
637
638 private int translateToApnIpType(String ipProtocol, String apn) {
639 if ("IP".equals(ipProtocol)) {
640 return APN_IPV4;
641 }
642 if ("IPV6".equals(ipProtocol)) {
643 return APN_IPV6;
644 }
645 if ("IPV4V6".equals(ipProtocol)) {
646 return APN_IPV4V6;
647 }
648
649 // we hit the default case so the ipProtocol is not recognized
650 String message = String.format("Unknown IP Protocol: %s, for APN: %s", ipProtocol, apn);
651 Log.e(TAG, message);
Shinru Hana415dab2019-03-21 16:14:24 +0800652 return APN_IPV4V6;
Anil Admal50ba15e2018-11-01 16:42:42 -0700653 }
654
Anil Admal50ba15e2018-11-01 16:42:42 -0700655 // AGPS support
Anil Admalc70344b2018-11-16 14:22:38 -0800656 private native void native_agps_data_conn_open(long networkHandle, String apn, int apnIpType);
Anil Admal50ba15e2018-11-01 16:42:42 -0700657
658 private native void native_agps_data_conn_closed();
659
660 private native void native_agps_data_conn_failed();
661
662 // AGPS ril support
663 private static native boolean native_is_agps_ril_supported();
664
Anil Admalb4995e22018-11-16 17:36:21 -0800665 private native void native_update_network_state(boolean connected, int type, boolean roaming,
666 boolean available, String apn, long networkHandle, short capabilities);
Anil Admal84b19b42019-04-06 17:37:22 -0700667}