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