blob: b211948f73cf76e67bc1c93cdb8dfcb73f36a23b [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;
26import android.net.Uri;
27import android.os.Handler;
28import android.os.Looper;
29import android.os.PowerManager;
30import android.provider.Telephony.Carriers;
31import android.telephony.TelephonyManager;
32import android.util.Log;
33
34import java.net.InetAddress;
35import java.net.UnknownHostException;
36import java.util.Arrays;
37import java.util.HashMap;
38import java.util.concurrent.ConcurrentHashMap;
39
40/**
41 * Handles network connection requests and network state change updates for AGPS data download.
42 */
43class GnssNetworkConnectivityHandler {
44 static final String TAG = "GnssNetworkConnectivityHandler";
45
46 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
47 private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
48
49 // for mAGpsDataConnectionState
50 private static final int AGPS_DATA_CONNECTION_CLOSED = 0;
51 private static final int AGPS_DATA_CONNECTION_OPENING = 1;
52 private static final int AGPS_DATA_CONNECTION_OPEN = 2;
53
54 // these need to match AGnssStatusValue enum in IAGnssCallback.hal
55 /** AGPS status event values. */
56 private static final int GPS_REQUEST_AGPS_DATA_CONN = 1;
57 private static final int GPS_RELEASE_AGPS_DATA_CONN = 2;
58 private static final int GPS_AGPS_DATA_CONNECTED = 3;
59 private static final int GPS_AGPS_DATA_CONN_DONE = 4;
60 private static final int GPS_AGPS_DATA_CONN_FAILED = 5;
61
62 // these must match the ApnIpType enum in IAGnss.hal
63 private static final int APN_INVALID = 0;
64 private static final int APN_IPV4 = 1;
65 private static final int APN_IPV6 = 2;
66 private static final int APN_IPV4V6 = 3;
67
68 // Default time limit in milliseconds for the ConnectivityManager to find a suitable
69 // network with SUPL connectivity or report an error.
70 private static final int SUPL_NETWORK_REQUEST_TIMEOUT_MILLIS = 10 * 1000;
71
72 private static final int HASH_MAP_INITIAL_CAPACITY_TO_TRACK_CONNECTED_NETWORKS = 5;
73
74 // keeps track of networks and their state as notified by the network request callbacks.
75 // Limit initial capacity to 5 as the number of connected networks will likely be small.
76 private ConcurrentHashMap<Network, NetworkAttributes> mAvailableNetworkAttributes =
77 new ConcurrentHashMap<>(HASH_MAP_INITIAL_CAPACITY_TO_TRACK_CONNECTED_NETWORKS);
78
79 private final ConnectivityManager mConnMgr;
80
81 private final Handler mHandler;
82 private final GnssNetworkListener mGnssNetworkListener;
83
84 private int mAGpsDataConnectionState;
85 private InetAddress mAGpsDataConnectionIpAddr;
86
87 private final Context mContext;
88
89 // Wakelocks
90 private static final String WAKELOCK_KEY = "GnssNetworkConnectivityHandler";
91 private static final long WAKELOCK_TIMEOUT_MILLIS = 60 * 1000;
92 private final PowerManager.WakeLock mWakeLock;
93
94 /**
95 * Network attributes needed when updating HAL about network connectivity status changes.
96 */
97 private static class NetworkAttributes {
98 NetworkCapabilities mCapabilities;
99 String mApn;
100 int mType = ConnectivityManager.TYPE_NONE;
101
102 /**
103 * Returns true if the capabilities that we pass on to HAL change between {@curCapabilities}
104 * and {@code newCapabilities}.
105 */
106 static boolean hasCapabilitiesChanged(NetworkCapabilities curCapabilities,
107 NetworkCapabilities newCapabilities) {
108 if (curCapabilities == null || newCapabilities == null) {
109 return true;
110 }
111
112 return curCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING)
113 != newCapabilities.hasCapability(
114 NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING);
115 }
116 }
117
118 /**
119 * Callback used to listen for data connectivity changes.
120 */
121 private ConnectivityManager.NetworkCallback mNetworkConnectivityCallback;
122
123 /**
124 * Callback used to listen for availability of a requested SUPL connection.
125 * It is kept as a separate instance from {@link #mNetworkConnectivityCallback} to be able to
126 * manage the registration/un-registration lifetimes separately.
127 */
128 private ConnectivityManager.NetworkCallback mSuplConnectivityCallback;
129
130 /**
131 * Interface to listen for network availability changes.
132 */
133 public interface GnssNetworkListener {
134 void onNetworkAvailable();
135 }
136
137 GnssNetworkConnectivityHandler(Context context,
138 GnssNetworkListener gnssNetworkListener,
139 Looper looper) {
140 mContext = context;
141 mGnssNetworkListener = gnssNetworkListener;
142
143 PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
144 mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
145
146 mHandler = new Handler(looper);
147 mConnMgr = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
148 mSuplConnectivityCallback = createSuplConnectivityCallback();
149 }
150
151 public void registerNetworkCallbacks() {
152 mAvailableNetworkAttributes.clear();
153 if (mNetworkConnectivityCallback != null) {
154 mConnMgr.unregisterNetworkCallback(mNetworkConnectivityCallback);
155 }
156
157 // register for connectivity change events.
158 NetworkRequest.Builder networkRequestBuilder = new NetworkRequest.Builder();
159 networkRequestBuilder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
160 networkRequestBuilder.addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED);
161 networkRequestBuilder.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN);
162 NetworkRequest networkRequest = networkRequestBuilder.build();
163 mNetworkConnectivityCallback = createNetworkConnectivityCallback();
164 mConnMgr.registerNetworkCallback(networkRequest, mNetworkConnectivityCallback);
165 }
166
167 /**
168 * @return {@code true} if there is a data network available for outgoing connections,
169 * {@code false} otherwise.
170 */
171 public boolean isDataNetworkConnected() {
172 NetworkInfo activeNetworkInfo = mConnMgr.getActiveNetworkInfo();
173 return activeNetworkInfo != null && activeNetworkInfo.isConnected();
174 }
175
176 /**
177 * called from native code to update AGPS status
178 */
179 public void onReportAGpsStatus(int type, int status, byte[] ipaddr) {
180 switch (status) {
181 case GPS_REQUEST_AGPS_DATA_CONN:
182 if (DEBUG) Log.d(TAG, "GPS_REQUEST_AGPS_DATA_CONN");
183 Log.v(TAG, "Received SUPL IP addr[]: " + Arrays.toString(ipaddr));
184 InetAddress connectionIpAddress = null;
185 if (ipaddr != null) {
186 try {
187 connectionIpAddress = InetAddress.getByAddress(ipaddr);
188 if (DEBUG) Log.d(TAG, "IP address converted to: " + connectionIpAddress);
189 } catch (UnknownHostException e) {
190 Log.e(TAG, "Bad IP Address: " + ipaddr, e);
191 }
192 }
193 requestSuplConnection(connectionIpAddress);
194 break;
195 case GPS_RELEASE_AGPS_DATA_CONN:
196 if (DEBUG) Log.d(TAG, "GPS_RELEASE_AGPS_DATA_CONN");
197 releaseSuplConnection(GPS_RELEASE_AGPS_DATA_CONN);
198 break;
199 case GPS_AGPS_DATA_CONNECTED:
200 if (DEBUG) Log.d(TAG, "GPS_AGPS_DATA_CONNECTED");
201 break;
202 case GPS_AGPS_DATA_CONN_DONE:
203 if (DEBUG) Log.d(TAG, "GPS_AGPS_DATA_CONN_DONE");
204 break;
205 case GPS_AGPS_DATA_CONN_FAILED:
206 if (DEBUG) Log.d(TAG, "GPS_AGPS_DATA_CONN_FAILED");
207 break;
208 default:
209 if (DEBUG) Log.d(TAG, "Received Unknown AGPS status: " + status);
210 }
211 }
212
213 private ConnectivityManager.NetworkCallback createNetworkConnectivityCallback() {
214 return new ConnectivityManager.NetworkCallback() {
215 // Used to filter out network capabilities changes that we are not interested in.
216 // NOTE: Not using a ConcurrentHashMap and also not using locking around updates
217 // and access to the map object because it is all done inside the same
218 // handler thread invoking the callback methods.
219 private HashMap<Network, NetworkCapabilities>
220 mAvailableNetworkCapabilities = new HashMap<>(
221 HASH_MAP_INITIAL_CAPACITY_TO_TRACK_CONNECTED_NETWORKS);
222
223 @Override
224 public void onCapabilitiesChanged(Network network,
225 NetworkCapabilities capabilities) {
226 // This callback is invoked for any change in the network capabilities including
227 // initial availability, and changes while still available. Only process if the
228 // capabilities that we pass on to HAL change.
229 if (!NetworkAttributes.hasCapabilitiesChanged(
230 mAvailableNetworkCapabilities.get(network), capabilities)) {
231 if (VERBOSE) {
232 Log.v(TAG, "Relevant network capabilities unchanged. Capabilities: "
233 + capabilities);
234 }
235 return;
236 }
237
238 mAvailableNetworkCapabilities.put(network, capabilities);
239 if (DEBUG) {
240 Log.d(TAG, "Network connected/capabilities updated. Available networks count: "
241 + mAvailableNetworkCapabilities.size());
242 }
243
244 mGnssNetworkListener.onNetworkAvailable();
245
246 // Always on, notify HAL so it can get data it needs
247 updateNetworkState(network, true, capabilities);
248 }
249
250 @Override
251 public void onLost(Network network) {
252 if (mAvailableNetworkCapabilities.remove(network) == null) {
253 Log.w(TAG, "Incorrectly received network callback onLost() before"
254 + " onCapabilitiesChanged() for network: " + network);
255 return;
256 }
257
258 Log.i(TAG, "Network connection lost. Available networks count: "
259 + mAvailableNetworkCapabilities.size());
260 updateNetworkState(network, false, null);
261 }
262 };
263 }
264
265 private ConnectivityManager.NetworkCallback createSuplConnectivityCallback() {
266 return new ConnectivityManager.NetworkCallback() {
267 @Override
268 public void onAvailable(Network network) {
269 if (DEBUG) Log.d(TAG, "SUPL network connection available.");
270 // Specific to a change to a SUPL enabled network becoming ready
271 suplConnectionAvailable(network);
272 }
273
274 @Override
275 public void onLost(Network network) {
276 Log.i(TAG, "SUPL network connection lost.");
277 releaseSuplConnection(GPS_RELEASE_AGPS_DATA_CONN);
278 }
279
280 @Override
281 public void onUnavailable() {
282 Log.i(TAG, "SUPL network connection request timed out.");
283 // Could not setup the connection to the network in the specified time duration.
284 releaseSuplConnection(GPS_AGPS_DATA_CONN_FAILED);
285 }
286 };
287 }
288
289 private void requestSuplConnection(InetAddress inetAddress) {
290 postEvent(() -> handleRequestSuplConnection(inetAddress));
291 }
292
293 private void suplConnectionAvailable(Network network) {
294 postEvent(() -> handleSuplConnectionAvailable(network));
295 }
296
297 private void releaseSuplConnection(int connStatus) {
298 postEvent(() -> handleReleaseSuplConnection(connStatus));
299 }
300
301 private void updateNetworkState(Network network, boolean isConnected,
302 NetworkCapabilities capabilities) {
303 postEvent(() -> handleUpdateNetworkState(network, isConnected, capabilities));
304 }
305
306 private void postEvent(Runnable event) {
307 // hold a wake lock until this message is delivered
308 // note that this assumes the message will not be removed from the queue before
309 // it is handled (otherwise the wake lock would be leaked).
310 mWakeLock.acquire(WAKELOCK_TIMEOUT_MILLIS);
311 if (!mHandler.post(runEventAndReleaseWakeLock(event))) {
312 mWakeLock.release();
313 }
314 }
315
316 private Runnable runEventAndReleaseWakeLock(Runnable event) {
317 return () -> {
318 try {
319 event.run();
320 } finally {
321 mWakeLock.release();
322 }
323 };
324 }
325
326 private void handleUpdateNetworkState(Network network, boolean isConnected,
327 NetworkCapabilities capabilities) {
328 boolean networkAvailable = isConnected && TelephonyManager.getDefault().getDataEnabled();
329 NetworkAttributes networkAttributes = updateTrackedNetworksState(isConnected, network,
330 capabilities);
331 String apnName = networkAttributes.mApn;
332 int type = networkAttributes.mType;
333 // When isConnected is false, capabilities argument is null. So, use last received
334 // capabilities.
335 capabilities = networkAttributes.mCapabilities;
336 boolean isRoaming = !capabilities.hasTransport(
337 NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING);
338
339 Log.i(TAG, String.format(
340 "updateNetworkState, state=%s, connected=%s, network=%s, capabilities=%s"
341 + ", availableNetworkCount: %d",
342 agpsDataConnStateAsString(),
343 isConnected,
344 network,
345 capabilities,
346 mAvailableNetworkAttributes.size()));
347
348 if (native_is_agps_ril_supported()) {
349 String defaultApn = getSelectedApn();
350 if (defaultApn == null) {
351 defaultApn = "dummy-apn";
352 }
353
354 native_update_network_state(
355 isConnected,
356 type,
357 isRoaming,
358 networkAvailable,
359 apnName,
360 defaultApn);
361 } else if (DEBUG) {
362 Log.d(TAG, "Skipped network state update because GPS HAL AGPS-RIL is not supported");
363 }
364 }
365
366 private NetworkAttributes updateTrackedNetworksState(boolean isConnected, Network network,
367 NetworkCapabilities capabilities) {
368 if (!isConnected) {
369 // Connection lost event. So, remove it from tracked networks.
370 return mAvailableNetworkAttributes.remove(network);
371 }
372
373 NetworkAttributes networkAttributes = mAvailableNetworkAttributes.get(network);
374 if (networkAttributes != null) {
375 // Capabilities updated event for the connected network.
376 networkAttributes.mCapabilities = capabilities;
377 return networkAttributes;
378 }
379
380 // Initial capabilities event (equivalent to connection available event).
381 networkAttributes = new NetworkAttributes();
382 networkAttributes.mCapabilities = capabilities;
383
384 // TODO(b/119278134): The synchronous method ConnectivityManager.getNetworkInfo() must
385 // not be called inside the asynchronous ConnectivityManager.NetworkCallback methods.
386 NetworkInfo info = mConnMgr.getNetworkInfo(network);
387 if (info != null) {
388 networkAttributes.mApn = info.getExtraInfo();
389 networkAttributes.mType = info.getType();
390 }
391
392 // Start tracking this network for connection status updates.
393 mAvailableNetworkAttributes.put(network, networkAttributes);
394 return networkAttributes;
395 }
396
397 private void handleSuplConnectionAvailable(Network network) {
398 // TODO(b/119278134): The synchronous method ConnectivityManager.getNetworkInfo() must
399 // not be called inside the asynchronous ConnectivityManager.NetworkCallback methods.
400 NetworkInfo info = mConnMgr.getNetworkInfo(network);
401 String apnName = null;
402 if (info != null) {
403 apnName = info.getExtraInfo();
404 }
405
406 if (DEBUG) {
407 String message = String.format(
408 "handleSuplConnectionAvailable: state=%s, suplNetwork=%s, info=%s",
409 agpsDataConnStateAsString(),
410 network,
411 info);
412 Log.d(TAG, message);
413 }
414
415 if (mAGpsDataConnectionState == AGPS_DATA_CONNECTION_OPENING) {
416 if (apnName == null) {
417 // assign a dummy value in the case of C2K as otherwise we will have a runtime
418 // exception in the following call to native_agps_data_conn_open
419 apnName = "dummy-apn";
420 }
421 int apnIpType = getApnIpType(apnName);
422 setRouting();
423 if (DEBUG) {
424 String message = String.format(
425 "native_agps_data_conn_open: mAgpsApn=%s, mApnIpType=%s",
426 apnName,
427 apnIpType);
428 Log.d(TAG, message);
429 }
430 native_agps_data_conn_open(apnName, apnIpType);
431 mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPEN;
432 }
433 }
434
435 private void handleRequestSuplConnection(InetAddress address) {
436 if (DEBUG) {
437 String message = String.format(
438 "requestSuplConnection, state=%s, address=%s",
439 agpsDataConnStateAsString(),
440 address);
441 Log.d(TAG, message);
442 }
443
444 if (mAGpsDataConnectionState != AGPS_DATA_CONNECTION_CLOSED) {
445 return;
446 }
447 mAGpsDataConnectionIpAddr = address;
448 mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPENING;
449
450 NetworkRequest.Builder requestBuilder = new NetworkRequest.Builder();
451 requestBuilder.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
452 requestBuilder.addCapability(NetworkCapabilities.NET_CAPABILITY_SUPL);
453 NetworkRequest request = requestBuilder.build();
454 mConnMgr.requestNetwork(
455 request,
456 mSuplConnectivityCallback,
457 SUPL_NETWORK_REQUEST_TIMEOUT_MILLIS);
458 }
459
460 private void handleReleaseSuplConnection(int agpsDataConnStatus) {
461 if (DEBUG) {
462 String message = String.format(
463 "releaseSuplConnection, state=%s, status=%s",
464 agpsDataConnStateAsString(),
465 agpsDataConnStatusAsString(agpsDataConnStatus));
466 Log.d(TAG, message);
467 }
468
469 if (mAGpsDataConnectionState == AGPS_DATA_CONNECTION_CLOSED) {
470 return;
471 }
472 mAGpsDataConnectionState = AGPS_DATA_CONNECTION_CLOSED;
473
474 mConnMgr.unregisterNetworkCallback(mSuplConnectivityCallback);
475 switch (agpsDataConnStatus) {
476 case GPS_AGPS_DATA_CONN_FAILED:
477 native_agps_data_conn_failed();
478 break;
479 case GPS_RELEASE_AGPS_DATA_CONN:
480 native_agps_data_conn_closed();
481 break;
482 default:
483 Log.e(TAG, "Invalid status to release SUPL connection: " + agpsDataConnStatus);
484 }
485 }
486
487 private void setRouting() {
488 if (mAGpsDataConnectionIpAddr == null) {
489 return;
490 }
491
492 // TODO(25876485): replace the use of this deprecated API
493 boolean result = mConnMgr.requestRouteToHostAddress(
494 ConnectivityManager.TYPE_MOBILE_SUPL,
495 mAGpsDataConnectionIpAddr);
496
497 if (!result) {
498 Log.e(TAG, "Error requesting route to host: " + mAGpsDataConnectionIpAddr);
499 } else if (DEBUG) {
500 Log.d(TAG, "Successfully requested route to host: " + mAGpsDataConnectionIpAddr);
501 }
502 }
503
504 /**
505 * Ensures the calling function is running in the thread associated with {@link #mHandler}.
506 */
507 private void ensureInHandlerThread() {
508 if (mHandler != null && Looper.myLooper() == mHandler.getLooper()) {
509 return;
510 }
511 throw new IllegalStateException("This method must run on the Handler thread.");
512 }
513
514 /**
515 * @return A string representing the current state stored in {@link #mAGpsDataConnectionState}.
516 */
517 private String agpsDataConnStateAsString() {
518 switch (mAGpsDataConnectionState) {
519 case AGPS_DATA_CONNECTION_CLOSED:
520 return "CLOSED";
521 case AGPS_DATA_CONNECTION_OPEN:
522 return "OPEN";
523 case AGPS_DATA_CONNECTION_OPENING:
524 return "OPENING";
525 default:
526 return "<Unknown>";
527 }
528 }
529
530 /**
531 * @return A string representing the given GPS_AGPS_DATA status.
532 */
533 private String agpsDataConnStatusAsString(int agpsDataConnStatus) {
534 switch (agpsDataConnStatus) {
535 case GPS_AGPS_DATA_CONNECTED:
536 return "CONNECTED";
537 case GPS_AGPS_DATA_CONN_DONE:
538 return "DONE";
539 case GPS_AGPS_DATA_CONN_FAILED:
540 return "FAILED";
541 case GPS_RELEASE_AGPS_DATA_CONN:
542 return "RELEASE";
543 case GPS_REQUEST_AGPS_DATA_CONN:
544 return "REQUEST";
545 default:
546 return "<Unknown>";
547 }
548 }
549
550 private int getApnIpType(String apn) {
551 ensureInHandlerThread();
552 if (apn == null) {
553 return APN_INVALID;
554 }
555 TelephonyManager phone = (TelephonyManager)
556 mContext.getSystemService(Context.TELEPHONY_SERVICE);
557 // Carrier configuration may override framework roaming state, we need to use the actual
558 // modem roaming state instead of the framework roaming state.
559 boolean isDataRoamingFromRegistration = phone.getServiceState()
560 .getDataRoamingFromRegistration();
561 String projection = isDataRoamingFromRegistration ? Carriers.ROAMING_PROTOCOL :
562 Carriers.PROTOCOL;
563 String selection = String.format("current = 1 and apn = '%s' and carrier_enabled = 1", apn);
564 try (Cursor cursor = mContext.getContentResolver().query(
565 Carriers.CONTENT_URI,
566 new String[]{projection},
567 selection,
568 null,
569 Carriers.DEFAULT_SORT_ORDER)) {
570 if (null != cursor && cursor.moveToFirst()) {
571 return translateToApnIpType(cursor.getString(0), apn);
572 } else {
573 Log.e(TAG, "No entry found in query for APN: " + apn);
574 }
575 } catch (Exception e) {
576 Log.e(TAG, "Error encountered on APN query for: " + apn, e);
577 }
578
579 return APN_INVALID;
580 }
581
582 private int translateToApnIpType(String ipProtocol, String apn) {
583 if ("IP".equals(ipProtocol)) {
584 return APN_IPV4;
585 }
586 if ("IPV6".equals(ipProtocol)) {
587 return APN_IPV6;
588 }
589 if ("IPV4V6".equals(ipProtocol)) {
590 return APN_IPV4V6;
591 }
592
593 // we hit the default case so the ipProtocol is not recognized
594 String message = String.format("Unknown IP Protocol: %s, for APN: %s", ipProtocol, apn);
595 Log.e(TAG, message);
596 return APN_INVALID;
597 }
598
599 private String getSelectedApn() {
600 Uri uri = Uri.parse("content://telephony/carriers/preferapn");
601 try (Cursor cursor = mContext.getContentResolver().query(
602 uri,
603 new String[]{"apn"},
604 null /* selection */,
605 null /* selectionArgs */,
606 Carriers.DEFAULT_SORT_ORDER)) {
607 if (cursor != null && cursor.moveToFirst()) {
608 return cursor.getString(0);
609 } else {
610 Log.e(TAG, "No APN found to select.");
611 }
612 } catch (Exception e) {
613 Log.e(TAG, "Error encountered on selecting the APN.", e);
614 }
615
616 return null;
617 }
618
619 // AGPS support
620 private native void native_agps_data_conn_open(String apn, int apnIpType);
621
622 private native void native_agps_data_conn_closed();
623
624 private native void native_agps_data_conn_failed();
625
626 // AGPS ril support
627 private static native boolean native_is_agps_ril_supported();
628
629 private native void native_update_network_state(boolean connected, int type,
630 boolean roaming, boolean available, String extraInfo, String defaultAPN);
631}