1 /*
2  * Copyright (C) 2014 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 android.net;
18 
19 import android.annotation.IntDef;
20 import android.annotation.IntRange;
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.annotation.SystemApi;
24 import android.compat.annotation.UnsupportedAppUsage;
25 import android.content.Context;
26 import android.os.Build;
27 import android.os.Bundle;
28 import android.os.ConditionVariable;
29 import android.os.Handler;
30 import android.os.Looper;
31 import android.os.Message;
32 import android.os.Messenger;
33 import android.util.Log;
34 
35 import com.android.internal.annotations.VisibleForTesting;
36 import com.android.internal.util.AsyncChannel;
37 import com.android.internal.util.Protocol;
38 
39 import java.lang.annotation.Retention;
40 import java.lang.annotation.RetentionPolicy;
41 import java.time.Duration;
42 import java.util.ArrayList;
43 import java.util.Objects;
44 import java.util.concurrent.atomic.AtomicBoolean;
45 
46 /**
47  * A utility class for handling for communicating between bearer-specific
48  * code and ConnectivityService.
49  *
50  * An agent manages the life cycle of a network. A network starts its
51  * life cycle when {@link register} is called on NetworkAgent. The network
52  * is then connecting. When full L3 connectivity has been established,
53  * the agent shoud call {@link markConnected} to inform the system that
54  * this network is ready to use. When the network disconnects its life
55  * ends and the agent should call {@link unregister}, at which point the
56  * system will clean up and free resources.
57  * Any reconnection becomes a new logical network, so after a network
58  * is disconnected the agent cannot be used any more. Network providers
59  * should create a new NetworkAgent instance to handle new connections.
60  *
61  * A bearer may have more than one NetworkAgent if it can simultaneously
62  * support separate networks (IMS / Internet / MMS Apns on cellular, or
63  * perhaps connections with different SSID or P2P for Wi-Fi).
64  *
65  * This class supports methods to start and stop sending keepalive packets.
66  * Keepalive packets are typically sent at periodic intervals over a network
67  * with NAT when there is no other traffic to avoid the network forcefully
68  * closing the connection. NetworkAgents that manage technologies that
69  * have hardware support for keepalive should implement the related
70  * methods to save battery life. NetworkAgent that cannot get support
71  * without waking up the CPU should not, as this would be prohibitive in
72  * terms of battery - these agents should simply not override the related
73  * methods, which results in the implementation returning
74  * {@link SocketKeepalive.ERROR_UNSUPPORTED} as appropriate.
75  *
76  * Keepalive packets need to be sent at relatively frequent intervals
77  * (a few seconds to a few minutes). As the contents of keepalive packets
78  * depend on the current network status, hardware needs to be configured
79  * to send them and has a limited amount of memory to do so. The HAL
80  * formalizes this as slots that an implementation can configure to send
81  * the correct packets. Devices typically have a small number of slots
82  * per radio technology, and the specific number of slots for each
83  * technology is specified in configuration files.
84  * {@see SocketKeepalive} for details.
85  *
86  * @hide
87  */
88 @SystemApi
89 public abstract class NetworkAgent {
90     /**
91      * The {@link Network} corresponding to this object.
92      */
93     @Nullable
94     private volatile Network mNetwork;
95 
96     // Whether this NetworkAgent is using the legacy (never unhidden) API. The difference is
97     // that the legacy API uses NetworkInfo to convey the state, while the current API is
98     // exposing methods to manage it and generate it internally instead.
99     // TODO : remove this as soon as all agents have been converted.
100     private final boolean mIsLegacy;
101 
102     private final Handler mHandler;
103     private volatile AsyncChannel mAsyncChannel;
104     private final String LOG_TAG;
105     private static final boolean DBG = true;
106     private static final boolean VDBG = false;
107     private final ArrayList<Message> mPreConnectedQueue = new ArrayList<Message>();
108     private volatile long mLastBwRefreshTime = 0;
109     private static final long BW_REFRESH_MIN_WIN_MS = 500;
110     private boolean mBandwidthUpdateScheduled = false;
111     private AtomicBoolean mBandwidthUpdatePending = new AtomicBoolean(false);
112     // Not used by legacy agents. Non-legacy agents use this to convert the NetworkAgent system API
113     // into the internal API of ConnectivityService.
114     @NonNull
115     private NetworkInfo mNetworkInfo;
116     @NonNull
117     private final Object mRegisterLock = new Object();
118 
119     /**
120      * The ID of the {@link NetworkProvider} that created this object, or
121      * {@link NetworkProvider#ID_NONE} if unknown.
122      * @hide
123      */
124     public final int providerId;
125 
126     private static final int BASE = Protocol.BASE_NETWORK_AGENT;
127 
128     /**
129      * Sent by ConnectivityService to the NetworkAgent to inform it of
130      * suspected connectivity problems on its network.  The NetworkAgent
131      * should take steps to verify and correct connectivity.
132      * @hide
133      */
134     public static final int CMD_SUSPECT_BAD = BASE;
135 
136     /**
137      * Sent by the NetworkAgent (note the EVENT vs CMD prefix) to
138      * ConnectivityService to pass the current NetworkInfo (connection state).
139      * Sent when the NetworkInfo changes, mainly due to change of state.
140      * obj = NetworkInfo
141      * @hide
142      */
143     public static final int EVENT_NETWORK_INFO_CHANGED = BASE + 1;
144 
145     /**
146      * Sent by the NetworkAgent to ConnectivityService to pass the current
147      * NetworkCapabilties.
148      * obj = NetworkCapabilities
149      * @hide
150      */
151     public static final int EVENT_NETWORK_CAPABILITIES_CHANGED = BASE + 2;
152 
153     /**
154      * Sent by the NetworkAgent to ConnectivityService to pass the current
155      * NetworkProperties.
156      * obj = NetworkProperties
157      * @hide
158      */
159     public static final int EVENT_NETWORK_PROPERTIES_CHANGED = BASE + 3;
160 
161     /**
162      * Centralize the place where base network score, and network score scaling, will be
163      * stored, so as we can consistently compare apple and oranges, or wifi, ethernet and LTE
164      * @hide
165      */
166     public static final int WIFI_BASE_SCORE = 60;
167 
168     /**
169      * Sent by the NetworkAgent to ConnectivityService to pass the current
170      * network score.
171      * arg1 = network score int
172      * @hide
173      */
174     public static final int EVENT_NETWORK_SCORE_CHANGED = BASE + 4;
175 
176     /**
177      * Sent by ConnectivityService to the NetworkAgent to inform the agent of the
178      * networks status - whether we could use the network or could not, due to
179      * either a bad network configuration (no internet link) or captive portal.
180      *
181      * arg1 = either {@code VALID_NETWORK} or {@code INVALID_NETWORK}
182      * obj = Bundle containing map from {@code REDIRECT_URL_KEY} to {@code String}
183      *       representing URL that Internet probe was redirect to, if it was redirected,
184      *       or mapping to {@code null} otherwise.
185      * @hide
186      */
187     public static final int CMD_REPORT_NETWORK_STATUS = BASE + 7;
188 
189 
190     /**
191      * Network validation suceeded.
192      * Corresponds to {@link NetworkCapabilities.NET_CAPABILITY_VALIDATED}.
193      */
194     public static final int VALIDATION_STATUS_VALID = 1;
195 
196     /**
197      * Network validation was attempted and failed. This may be received more than once as
198      * subsequent validation attempts are made.
199      */
200     public static final int VALIDATION_STATUS_NOT_VALID = 2;
201 
202     /** @hide */
203     @Retention(RetentionPolicy.SOURCE)
204     @IntDef(prefix = { "VALIDATION_STATUS_" }, value = {
205             VALIDATION_STATUS_VALID,
206             VALIDATION_STATUS_NOT_VALID
207     })
208     public @interface ValidationStatus {}
209 
210     // TODO: remove.
211     /** @hide */
212     public static final int VALID_NETWORK = 1;
213     /** @hide */
214     public static final int INVALID_NETWORK = 2;
215 
216     /**
217      * The key for the redirect URL in the Bundle argument of {@code CMD_REPORT_NETWORK_STATUS}.
218      * @hide
219      */
220     public static String REDIRECT_URL_KEY = "redirect URL";
221 
222      /**
223      * Sent by the NetworkAgent to ConnectivityService to indicate this network was
224      * explicitly selected.  This should be sent before the NetworkInfo is marked
225      * CONNECTED so it can be given special treatment at that time.
226      *
227      * obj = boolean indicating whether to use this network even if unvalidated
228      * @hide
229      */
230     public static final int EVENT_SET_EXPLICITLY_SELECTED = BASE + 8;
231 
232     /**
233      * Sent by ConnectivityService to the NetworkAgent to inform the agent of
234      * whether the network should in the future be used even if not validated.
235      * This decision is made by the user, but it is the network transport's
236      * responsibility to remember it.
237      *
238      * arg1 = 1 if true, 0 if false
239      * @hide
240      */
241     public static final int CMD_SAVE_ACCEPT_UNVALIDATED = BASE + 9;
242 
243     /**
244      * Sent by ConnectivityService to the NetworkAgent to inform the agent to pull
245      * the underlying network connection for updated bandwidth information.
246      * @hide
247      */
248     public static final int CMD_REQUEST_BANDWIDTH_UPDATE = BASE + 10;
249 
250     /**
251      * Sent by ConnectivityService to the NetworkAgent to request that the specified packet be sent
252      * periodically on the given interval.
253      *
254      *   arg1 = the hardware slot number of the keepalive to start
255      *   arg2 = interval in seconds
256      *   obj = KeepalivePacketData object describing the data to be sent
257      *
258      * Also used internally by ConnectivityService / KeepaliveTracker, with different semantics.
259      * @hide
260      */
261     public static final int CMD_START_SOCKET_KEEPALIVE = BASE + 11;
262 
263     /**
264      * Requests that the specified keepalive packet be stopped.
265      *
266      * arg1 = hardware slot number of the keepalive to stop.
267      *
268      * Also used internally by ConnectivityService / KeepaliveTracker, with different semantics.
269      * @hide
270      */
271     public static final int CMD_STOP_SOCKET_KEEPALIVE = BASE + 12;
272 
273     /**
274      * Sent by the NetworkAgent to ConnectivityService to provide status on a socket keepalive
275      * request. This may either be the reply to a CMD_START_SOCKET_KEEPALIVE, or an asynchronous
276      * error notification.
277      *
278      * This is also sent by KeepaliveTracker to the app's {@link SocketKeepalive},
279      * so that the app's {@link SocketKeepalive.Callback} methods can be called.
280      *
281      * arg1 = hardware slot number of the keepalive
282      * arg2 = error code
283      * @hide
284      */
285     public static final int EVENT_SOCKET_KEEPALIVE = BASE + 13;
286 
287     /**
288      * Sent by ConnectivityService to inform this network transport of signal strength thresholds
289      * that when crossed should trigger a system wakeup and a NetworkCapabilities update.
290      *
291      *   obj = int[] describing signal strength thresholds.
292      * @hide
293      */
294     public static final int CMD_SET_SIGNAL_STRENGTH_THRESHOLDS = BASE + 14;
295 
296     /**
297      * Sent by ConnectivityService to the NeworkAgent to inform the agent to avoid
298      * automatically reconnecting to this network (e.g. via autojoin).  Happens
299      * when user selects "No" option on the "Stay connected?" dialog box.
300      * @hide
301      */
302     public static final int CMD_PREVENT_AUTOMATIC_RECONNECT = BASE + 15;
303 
304     /**
305      * Sent by the KeepaliveTracker to NetworkAgent to add a packet filter.
306      *
307      * For TCP keepalive offloads, keepalive packets are sent by the firmware. However, because the
308      * remote site will send ACK packets in response to the keepalive packets, the firmware also
309      * needs to be configured to properly filter the ACKs to prevent the system from waking up.
310      * This does not happen with UDP, so this message is TCP-specific.
311      * arg1 = hardware slot number of the keepalive to filter for.
312      * obj = the keepalive packet to send repeatedly.
313      * @hide
314      */
315     public static final int CMD_ADD_KEEPALIVE_PACKET_FILTER = BASE + 16;
316 
317     /**
318      * Sent by the KeepaliveTracker to NetworkAgent to remove a packet filter. See
319      * {@link #CMD_ADD_KEEPALIVE_PACKET_FILTER}.
320      * arg1 = hardware slot number of the keepalive packet filter to remove.
321      * @hide
322      */
323     public static final int CMD_REMOVE_KEEPALIVE_PACKET_FILTER = BASE + 17;
324 
325     /** @hide TODO: remove and replace usage with the public constructor. */
NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni, NetworkCapabilities nc, LinkProperties lp, int score)326     public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
327             NetworkCapabilities nc, LinkProperties lp, int score) {
328         this(looper, context, logTag, ni, nc, lp, score, null, NetworkProvider.ID_NONE);
329         // Register done by the constructor called in the previous line
330     }
331 
332     /** @hide TODO: remove and replace usage with the public constructor. */
NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni, NetworkCapabilities nc, LinkProperties lp, int score, NetworkAgentConfig config)333     public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
334             NetworkCapabilities nc, LinkProperties lp, int score, NetworkAgentConfig config) {
335         this(looper, context, logTag, ni, nc, lp, score, config, NetworkProvider.ID_NONE);
336         // Register done by the constructor called in the previous line
337     }
338 
339     /** @hide TODO: remove and replace usage with the public constructor. */
NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni, NetworkCapabilities nc, LinkProperties lp, int score, int providerId)340     public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
341             NetworkCapabilities nc, LinkProperties lp, int score, int providerId) {
342         this(looper, context, logTag, ni, nc, lp, score, null, providerId);
343         // Register done by the constructor called in the previous line
344     }
345 
346     /** @hide TODO: remove and replace usage with the public constructor. */
NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni, NetworkCapabilities nc, LinkProperties lp, int score, NetworkAgentConfig config, int providerId)347     public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
348             NetworkCapabilities nc, LinkProperties lp, int score, NetworkAgentConfig config,
349             int providerId) {
350         this(looper, context, logTag, nc, lp, score, config, providerId, ni, true /* legacy */);
351         register();
352     }
353 
getLegacyNetworkInfo(final NetworkAgentConfig config)354     private static NetworkInfo getLegacyNetworkInfo(final NetworkAgentConfig config) {
355         // The subtype can be changed with (TODO) setLegacySubtype, but it starts
356         // with the type and an empty description.
357         final NetworkInfo ni = new NetworkInfo(config.legacyType, config.legacyType,
358                 config.legacyTypeName, "");
359         ni.setIsAvailable(true);
360         ni.setExtraInfo(config.getLegacyExtraInfo());
361         return ni;
362     }
363 
364     /**
365      * Create a new network agent.
366      * @param context a {@link Context} to get system services from.
367      * @param looper the {@link Looper} on which to invoke the callbacks.
368      * @param logTag the tag for logs
369      * @param nc the initial {@link NetworkCapabilities} of this network. Update with
370      *           sendNetworkCapabilities.
371      * @param lp the initial {@link LinkProperties} of this network. Update with sendLinkProperties.
372      * @param score the initial score of this network. Update with sendNetworkScore.
373      * @param config an immutable {@link NetworkAgentConfig} for this agent.
374      * @param provider the {@link NetworkProvider} managing this agent.
375      */
NetworkAgent(@onNull Context context, @NonNull Looper looper, @NonNull String logTag, @NonNull NetworkCapabilities nc, @NonNull LinkProperties lp, int score, @NonNull NetworkAgentConfig config, @Nullable NetworkProvider provider)376     public NetworkAgent(@NonNull Context context, @NonNull Looper looper, @NonNull String logTag,
377             @NonNull NetworkCapabilities nc, @NonNull LinkProperties lp, int score,
378             @NonNull NetworkAgentConfig config, @Nullable NetworkProvider provider) {
379         this(looper, context, logTag, nc, lp, score, config,
380                 provider == null ? NetworkProvider.ID_NONE : provider.getProviderId(),
381                 getLegacyNetworkInfo(config), false /* legacy */);
382     }
383 
384     private static class InitialConfiguration {
385         public final Context context;
386         public final NetworkCapabilities capabilities;
387         public final LinkProperties properties;
388         public final int score;
389         public final NetworkAgentConfig config;
390         public final NetworkInfo info;
InitialConfiguration(@onNull Context context, @NonNull NetworkCapabilities capabilities, @NonNull LinkProperties properties, int score, @NonNull NetworkAgentConfig config, @NonNull NetworkInfo info)391         InitialConfiguration(@NonNull Context context, @NonNull NetworkCapabilities capabilities,
392                 @NonNull LinkProperties properties, int score, @NonNull NetworkAgentConfig config,
393                 @NonNull NetworkInfo info) {
394             this.context = context;
395             this.capabilities = capabilities;
396             this.properties = properties;
397             this.score = score;
398             this.config = config;
399             this.info = info;
400         }
401     }
402     private volatile InitialConfiguration mInitialConfiguration;
403 
NetworkAgent(@onNull Looper looper, @NonNull Context context, @NonNull String logTag, @NonNull NetworkCapabilities nc, @NonNull LinkProperties lp, int score, @NonNull NetworkAgentConfig config, int providerId, @NonNull NetworkInfo ni, boolean legacy)404     private NetworkAgent(@NonNull Looper looper, @NonNull Context context, @NonNull String logTag,
405             @NonNull NetworkCapabilities nc, @NonNull LinkProperties lp, int score,
406             @NonNull NetworkAgentConfig config, int providerId, @NonNull NetworkInfo ni,
407             boolean legacy) {
408         mHandler = new NetworkAgentHandler(looper);
409         LOG_TAG = logTag;
410         mIsLegacy = legacy;
411         mNetworkInfo = new NetworkInfo(ni);
412         this.providerId = providerId;
413         if (ni == null || nc == null || lp == null) {
414             throw new IllegalArgumentException();
415         }
416 
417         mInitialConfiguration = new InitialConfiguration(context, new NetworkCapabilities(nc),
418                 new LinkProperties(lp), score, config, ni);
419     }
420 
421     private class NetworkAgentHandler extends Handler {
NetworkAgentHandler(Looper looper)422         NetworkAgentHandler(Looper looper) {
423             super(looper);
424         }
425 
426         @Override
handleMessage(Message msg)427         public void handleMessage(Message msg) {
428             switch (msg.what) {
429                 case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: {
430                     if (mAsyncChannel != null) {
431                         log("Received new connection while already connected!");
432                     } else {
433                         if (VDBG) log("NetworkAgent fully connected");
434                         AsyncChannel ac = new AsyncChannel();
435                         ac.connected(null, this, msg.replyTo);
436                         ac.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
437                                 AsyncChannel.STATUS_SUCCESSFUL);
438                         synchronized (mPreConnectedQueue) {
439                             mAsyncChannel = ac;
440                             for (Message m : mPreConnectedQueue) {
441                                 ac.sendMessage(m);
442                             }
443                             mPreConnectedQueue.clear();
444                         }
445                     }
446                     break;
447                 }
448                 case AsyncChannel.CMD_CHANNEL_DISCONNECT: {
449                     if (VDBG) log("CMD_CHANNEL_DISCONNECT");
450                     if (mAsyncChannel != null) mAsyncChannel.disconnect();
451                     break;
452                 }
453                 case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
454                     if (DBG) log("NetworkAgent channel lost");
455                     // let the client know CS is done with us.
456                     onNetworkUnwanted();
457                     synchronized (mPreConnectedQueue) {
458                         mAsyncChannel = null;
459                     }
460                     break;
461                 }
462                 case CMD_SUSPECT_BAD: {
463                     log("Unhandled Message " + msg);
464                     break;
465                 }
466                 case CMD_REQUEST_BANDWIDTH_UPDATE: {
467                     long currentTimeMs = System.currentTimeMillis();
468                     if (VDBG) {
469                         log("CMD_REQUEST_BANDWIDTH_UPDATE request received.");
470                     }
471                     if (currentTimeMs >= (mLastBwRefreshTime + BW_REFRESH_MIN_WIN_MS)) {
472                         mBandwidthUpdateScheduled = false;
473                         if (!mBandwidthUpdatePending.getAndSet(true)) {
474                             onBandwidthUpdateRequested();
475                         }
476                     } else {
477                         // deliver the request at a later time rather than discard it completely.
478                         if (!mBandwidthUpdateScheduled) {
479                             long waitTime = mLastBwRefreshTime + BW_REFRESH_MIN_WIN_MS
480                                     - currentTimeMs + 1;
481                             mBandwidthUpdateScheduled = sendEmptyMessageDelayed(
482                                     CMD_REQUEST_BANDWIDTH_UPDATE, waitTime);
483                         }
484                     }
485                     break;
486                 }
487                 case CMD_REPORT_NETWORK_STATUS: {
488                     String redirectUrl = ((Bundle) msg.obj).getString(REDIRECT_URL_KEY);
489                     if (VDBG) {
490                         log("CMD_REPORT_NETWORK_STATUS("
491                                 + (msg.arg1 == VALID_NETWORK ? "VALID, " : "INVALID, ")
492                                 + redirectUrl);
493                     }
494                     Uri uri = null;
495                     try {
496                         if (null != redirectUrl) {
497                             uri = Uri.parse(redirectUrl);
498                         }
499                     } catch (Exception e) {
500                         Log.wtf(LOG_TAG, "Surprising URI : " + redirectUrl, e);
501                     }
502                     onValidationStatus(msg.arg1 /* status */, uri);
503                     break;
504                 }
505                 case CMD_SAVE_ACCEPT_UNVALIDATED: {
506                     onSaveAcceptUnvalidated(msg.arg1 != 0);
507                     break;
508                 }
509                 case CMD_START_SOCKET_KEEPALIVE: {
510                     onStartSocketKeepalive(msg.arg1 /* slot */,
511                             Duration.ofSeconds(msg.arg2) /* interval */,
512                             (KeepalivePacketData) msg.obj /* packet */);
513                     break;
514                 }
515                 case CMD_STOP_SOCKET_KEEPALIVE: {
516                     onStopSocketKeepalive(msg.arg1 /* slot */);
517                     break;
518                 }
519 
520                 case CMD_SET_SIGNAL_STRENGTH_THRESHOLDS: {
521                     ArrayList<Integer> thresholds =
522                             ((Bundle) msg.obj).getIntegerArrayList("thresholds");
523                     // TODO: Change signal strength thresholds API to use an ArrayList<Integer>
524                     // rather than convert to int[].
525                     int[] intThresholds = new int[(thresholds != null) ? thresholds.size() : 0];
526                     for (int i = 0; i < intThresholds.length; i++) {
527                         intThresholds[i] = thresholds.get(i);
528                     }
529                     onSignalStrengthThresholdsUpdated(intThresholds);
530                     break;
531                 }
532                 case CMD_PREVENT_AUTOMATIC_RECONNECT: {
533                     onAutomaticReconnectDisabled();
534                     break;
535                 }
536                 case CMD_ADD_KEEPALIVE_PACKET_FILTER: {
537                     onAddKeepalivePacketFilter(msg.arg1 /* slot */,
538                             (KeepalivePacketData) msg.obj /* packet */);
539                     break;
540                 }
541                 case CMD_REMOVE_KEEPALIVE_PACKET_FILTER: {
542                     onRemoveKeepalivePacketFilter(msg.arg1 /* slot */);
543                     break;
544                 }
545             }
546         }
547     }
548 
549     /**
550      * Register this network agent with ConnectivityService.
551      *
552      * This method can only be called once per network agent.
553      *
554      * @return the Network associated with this network agent (which can also be obtained later
555      *         by calling getNetwork() on this agent).
556      * @throws IllegalStateException thrown by the system server if this network agent is
557      *         already registered.
558      */
559     @NonNull
register()560     public Network register() {
561         if (VDBG) log("Registering NetworkAgent");
562         synchronized (mRegisterLock) {
563             if (mNetwork != null) {
564                 throw new IllegalStateException("Agent already registered");
565             }
566             final ConnectivityManager cm = (ConnectivityManager) mInitialConfiguration.context
567                     .getSystemService(Context.CONNECTIVITY_SERVICE);
568             mNetwork = cm.registerNetworkAgent(new Messenger(mHandler),
569                     new NetworkInfo(mInitialConfiguration.info),
570                     mInitialConfiguration.properties, mInitialConfiguration.capabilities,
571                     mInitialConfiguration.score, mInitialConfiguration.config, providerId);
572             mInitialConfiguration = null; // All this memory can now be GC'd
573         }
574         return mNetwork;
575     }
576 
577     /**
578      * Register this network agent with a testing harness.
579      *
580      * The returned Messenger sends messages to the Handler. This allows a test to send
581      * this object {@code CMD_*} messages as if they came from ConnectivityService, which
582      * is useful for testing the behavior.
583      *
584      * @hide
585      */
registerForTest(final Network network)586     public Messenger registerForTest(final Network network) {
587         log("Registering NetworkAgent for test");
588         synchronized (mRegisterLock) {
589             mNetwork = network;
590             mInitialConfiguration = null;
591         }
592         return new Messenger(mHandler);
593     }
594 
595     /**
596      * Waits for the handler to be idle.
597      * This is useful for testing, and has smaller scope than an accessor to mHandler.
598      * TODO : move the implementation in common library with the tests
599      * @hide
600      */
601     @VisibleForTesting
waitForIdle(final long timeoutMs)602     public boolean waitForIdle(final long timeoutMs) {
603         final ConditionVariable cv = new ConditionVariable(false);
604         mHandler.post(cv::open);
605         return cv.block(timeoutMs);
606     }
607 
608     /**
609      * @return The Network associated with this agent, or null if it's not registered yet.
610      */
611     @Nullable
getNetwork()612     public Network getNetwork() {
613         return mNetwork;
614     }
615 
queueOrSendMessage(int what, Object obj)616     private void queueOrSendMessage(int what, Object obj) {
617         queueOrSendMessage(what, 0, 0, obj);
618     }
619 
queueOrSendMessage(int what, int arg1, int arg2)620     private void queueOrSendMessage(int what, int arg1, int arg2) {
621         queueOrSendMessage(what, arg1, arg2, null);
622     }
623 
queueOrSendMessage(int what, int arg1, int arg2, Object obj)624     private void queueOrSendMessage(int what, int arg1, int arg2, Object obj) {
625         Message msg = Message.obtain();
626         msg.what = what;
627         msg.arg1 = arg1;
628         msg.arg2 = arg2;
629         msg.obj = obj;
630         queueOrSendMessage(msg);
631     }
632 
queueOrSendMessage(Message msg)633     private void queueOrSendMessage(Message msg) {
634         synchronized (mPreConnectedQueue) {
635             if (mAsyncChannel != null) {
636                 mAsyncChannel.sendMessage(msg);
637             } else {
638                 mPreConnectedQueue.add(msg);
639             }
640         }
641     }
642 
643     /**
644      * Must be called by the agent when the network's {@link LinkProperties} change.
645      * @param linkProperties the new LinkProperties.
646      */
sendLinkProperties(@onNull LinkProperties linkProperties)647     public final void sendLinkProperties(@NonNull LinkProperties linkProperties) {
648         Objects.requireNonNull(linkProperties);
649         queueOrSendMessage(EVENT_NETWORK_PROPERTIES_CHANGED, new LinkProperties(linkProperties));
650     }
651 
652     /**
653      * Inform ConnectivityService that this agent has now connected.
654      * Call {@link #unregister} to disconnect.
655      */
markConnected()656     public void markConnected() {
657         if (mIsLegacy) {
658             throw new UnsupportedOperationException(
659                     "Legacy agents can't call markConnected.");
660         }
661         // |reason| cannot be used by the non-legacy agents
662         mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null /* reason */,
663                 mNetworkInfo.getExtraInfo());
664         queueOrSendMessage(EVENT_NETWORK_INFO_CHANGED, mNetworkInfo);
665     }
666 
667     /**
668      * Unregister this network agent.
669      *
670      * This signals the network has disconnected and ends its lifecycle. After this is called,
671      * the network is torn down and this agent can no longer be used.
672      */
unregister()673     public void unregister() {
674         if (mIsLegacy) {
675             throw new UnsupportedOperationException("Legacy agents can't call unregister.");
676         }
677         // When unregistering an agent nobody should use the extrainfo (or reason) any more.
678         mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, null /* reason */,
679                 null /* extraInfo */);
680         queueOrSendMessage(EVENT_NETWORK_INFO_CHANGED, mNetworkInfo);
681     }
682 
683     /**
684      * Change the legacy subtype of this network agent.
685      *
686      * This is only for backward compatibility and should not be used by non-legacy network agents,
687      * or agents that did not use to set a subtype. As such, only TYPE_MOBILE type agents can use
688      * this and others will be thrown an exception if they try.
689      *
690      * @deprecated this is for backward compatibility only.
691      * @param legacySubtype the legacy subtype.
692      * @hide
693      */
694     @Deprecated
setLegacySubtype(final int legacySubtype, @NonNull final String legacySubtypeName)695     public void setLegacySubtype(final int legacySubtype, @NonNull final String legacySubtypeName) {
696         if (mIsLegacy) {
697             throw new UnsupportedOperationException("Legacy agents can't call setLegacySubtype.");
698         }
699         mNetworkInfo.setSubtype(legacySubtype, legacySubtypeName);
700         queueOrSendMessage(EVENT_NETWORK_INFO_CHANGED, mNetworkInfo);
701     }
702 
703     /**
704      * Set the ExtraInfo of this network agent.
705      *
706      * This sets the ExtraInfo field inside the NetworkInfo returned by legacy public API and the
707      * broadcasts about the corresponding Network.
708      * This is only for backward compatibility and should not be used by non-legacy network agents,
709      * who will be thrown an exception if they try. The extra info should only be :
710      * <ul>
711      *   <li>For cellular agents, the APN name.</li>
712      *   <li>For ethernet agents, the interface name.</li>
713      * </ul>
714      *
715      * @deprecated this is for backward compatibility only.
716      * @param extraInfo the ExtraInfo.
717      * @hide
718      */
719     @Deprecated
setLegacyExtraInfo(@ullable final String extraInfo)720     public void setLegacyExtraInfo(@Nullable final String extraInfo) {
721         if (mIsLegacy) {
722             throw new UnsupportedOperationException("Legacy agents can't call setLegacyExtraInfo.");
723         }
724         mNetworkInfo.setExtraInfo(extraInfo);
725         queueOrSendMessage(EVENT_NETWORK_INFO_CHANGED, mNetworkInfo);
726     }
727 
728     /**
729      * Must be called by the agent when it has a new NetworkInfo object.
730      * @hide TODO: expose something better.
731      */
732     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
sendNetworkInfo(NetworkInfo networkInfo)733     public final void sendNetworkInfo(NetworkInfo networkInfo) {
734         if (!mIsLegacy) {
735             throw new UnsupportedOperationException("Only legacy agents can call sendNetworkInfo.");
736         }
737         queueOrSendMessage(EVENT_NETWORK_INFO_CHANGED, new NetworkInfo(networkInfo));
738     }
739 
740     /**
741      * Must be called by the agent when the network's {@link NetworkCapabilities} change.
742      * @param networkCapabilities the new NetworkCapabilities.
743      */
sendNetworkCapabilities(@onNull NetworkCapabilities networkCapabilities)744     public final void sendNetworkCapabilities(@NonNull NetworkCapabilities networkCapabilities) {
745         Objects.requireNonNull(networkCapabilities);
746         mBandwidthUpdatePending.set(false);
747         mLastBwRefreshTime = System.currentTimeMillis();
748         queueOrSendMessage(EVENT_NETWORK_CAPABILITIES_CHANGED,
749                 new NetworkCapabilities(networkCapabilities));
750     }
751 
752     /**
753      * Must be called by the agent to update the score of this network.
754      *
755      * @param score the new score, between 0 and 99.
756      */
sendNetworkScore(@ntRangefrom = 0, to = 99) int score)757     public final void sendNetworkScore(@IntRange(from = 0, to = 99) int score) {
758         if (score < 0) {
759             throw new IllegalArgumentException("Score must be >= 0");
760         }
761         queueOrSendMessage(EVENT_NETWORK_SCORE_CHANGED, score, 0);
762     }
763 
764     /**
765      * Must be called by the agent to indicate this network was manually selected by the user.
766      * This should be called before the NetworkInfo is marked CONNECTED so that this
767      * Network can be given special treatment at that time. If {@code acceptUnvalidated} is
768      * {@code true}, then the system will switch to this network. If it is {@code false} and the
769      * network cannot be validated, the system will ask the user whether to switch to this network.
770      * If the user confirms and selects "don't ask again", then the system will call
771      * {@link #saveAcceptUnvalidated} to persist the user's choice. Thus, if the transport ever
772      * calls this method with {@code acceptUnvalidated} set to {@code false}, it must also implement
773      * {@link #saveAcceptUnvalidated} to respect the user's choice.
774      * @hide should move to NetworkAgentConfig.
775      */
explicitlySelected(boolean acceptUnvalidated)776     public void explicitlySelected(boolean acceptUnvalidated) {
777         explicitlySelected(true /* explicitlySelected */, acceptUnvalidated);
778     }
779 
780     /**
781      * Must be called by the agent to indicate whether the network was manually selected by the
782      * user. This should be called before the network becomes connected, so it can be given
783      * special treatment when it does.
784      *
785      * If {@code explicitlySelected} is {@code true}, and {@code acceptUnvalidated} is {@code true},
786      * then the system will switch to this network. If {@code explicitlySelected} is {@code true}
787      * and {@code acceptUnvalidated} is {@code false}, and the  network cannot be validated, the
788      * system will ask the user whether to switch to this network.  If the user confirms and selects
789      * "don't ask again", then the system will call {@link #saveAcceptUnvalidated} to persist the
790      * user's choice. Thus, if the transport ever calls this method with {@code explicitlySelected}
791      * set to {@code true} and {@code acceptUnvalidated} set to {@code false}, it must also
792      * implement {@link #saveAcceptUnvalidated} to respect the user's choice.
793      *
794      * If  {@code explicitlySelected} is {@code false} and {@code acceptUnvalidated} is
795      * {@code true}, the system will interpret this as the user having accepted partial connectivity
796      * on this network. Thus, the system will switch to the network and consider it validated even
797      * if it only provides partial connectivity, but the network is not otherwise treated specially.
798      * @hide should move to NetworkAgentConfig.
799      */
explicitlySelected(boolean explicitlySelected, boolean acceptUnvalidated)800     public void explicitlySelected(boolean explicitlySelected, boolean acceptUnvalidated) {
801         queueOrSendMessage(EVENT_SET_EXPLICITLY_SELECTED,
802                 explicitlySelected ? 1 : 0,
803                 acceptUnvalidated ? 1 : 0);
804     }
805 
806     /**
807      * Called when ConnectivityService has indicated they no longer want this network.
808      * The parent factory should (previously) have received indication of the change
809      * as well, either canceling NetworkRequests or altering their score such that this
810      * network won't be immediately requested again.
811      */
onNetworkUnwanted()812     public void onNetworkUnwanted() {
813         unwanted();
814     }
815     /** @hide TODO delete once subclasses have moved to onNetworkUnwanted. */
unwanted()816     protected void unwanted() {
817     }
818 
819     /**
820      * Called when ConnectivityService request a bandwidth update. The parent factory
821      * shall try to overwrite this method and produce a bandwidth update if capable.
822      * @hide
823      */
onBandwidthUpdateRequested()824     public void onBandwidthUpdateRequested() {
825         pollLceData();
826     }
827     /** @hide TODO delete once subclasses have moved to onBandwidthUpdateRequested. */
pollLceData()828     protected void pollLceData() {
829     }
830 
831     /**
832      * Called when the system determines the usefulness of this network.
833      *
834      * The system attempts to validate Internet connectivity on networks that provide the
835      * {@link NetworkCapabilities#NET_CAPABILITY_INTERNET} capability.
836      *
837      * Currently there are two possible values:
838      * {@code VALIDATION_STATUS_VALID} if Internet connectivity was validated,
839      * {@code VALIDATION_STATUS_NOT_VALID} if Internet connectivity was not validated.
840      *
841      * This is guaranteed to be called again when the network status changes, but the system
842      * may also call this multiple times even if the status does not change.
843      *
844      * @param status one of {@code VALIDATION_STATUS_VALID} or {@code VALIDATION_STATUS_NOT_VALID}.
845      * @param redirectUri If Internet connectivity is being redirected (e.g., on a captive portal),
846      *        this is the destination the probes are being redirected to, otherwise {@code null}.
847      */
onValidationStatus(@alidationStatus int status, @Nullable Uri redirectUri)848     public void onValidationStatus(@ValidationStatus int status, @Nullable Uri redirectUri) {
849         networkStatus(status, null == redirectUri ? "" : redirectUri.toString());
850     }
851     /** @hide TODO delete once subclasses have moved to onValidationStatus */
networkStatus(int status, String redirectUrl)852     protected void networkStatus(int status, String redirectUrl) {
853     }
854 
855 
856     /**
857      * Called when the user asks to remember the choice to use this network even if unvalidated.
858      * The transport is responsible for remembering the choice, and the next time the user connects
859      * to the network, should explicitlySelected with {@code acceptUnvalidated} set to {@code true}.
860      * This method will only be called if {@link #explicitlySelected} was called with
861      * {@code acceptUnvalidated} set to {@code false}.
862      * @param accept whether the user wants to use the network even if unvalidated.
863      */
onSaveAcceptUnvalidated(boolean accept)864     public void onSaveAcceptUnvalidated(boolean accept) {
865         saveAcceptUnvalidated(accept);
866     }
867     /** @hide TODO delete once subclasses have moved to onSaveAcceptUnvalidated */
saveAcceptUnvalidated(boolean accept)868     protected void saveAcceptUnvalidated(boolean accept) {
869     }
870 
871     /**
872      * Requests that the network hardware send the specified packet at the specified interval.
873      *
874      * @param slot the hardware slot on which to start the keepalive.
875      * @param interval the interval between packets, between 10 and 3600. Note that this API
876      *                 does not support sub-second precision and will round off the request.
877      * @param packet the packet to send.
878      */
879     // seconds is from SocketKeepalive.MIN_INTERVAL_SEC to MAX_INTERVAL_SEC, but these should
880     // not be exposed as constants because they may change in the future (API guideline 4.8)
881     // and should have getters if exposed at all. Getters can't be used in the annotation,
882     // so the values unfortunately need to be copied.
onStartSocketKeepalive(int slot, @NonNull Duration interval, @NonNull KeepalivePacketData packet)883     public void onStartSocketKeepalive(int slot, @NonNull Duration interval,
884             @NonNull KeepalivePacketData packet) {
885         final long intervalSeconds = interval.getSeconds();
886         if (intervalSeconds < SocketKeepalive.MIN_INTERVAL_SEC
887                 || intervalSeconds > SocketKeepalive.MAX_INTERVAL_SEC) {
888             throw new IllegalArgumentException("Interval needs to be comprised between "
889                     + SocketKeepalive.MIN_INTERVAL_SEC + " and " + SocketKeepalive.MAX_INTERVAL_SEC
890                     + " but was " + intervalSeconds);
891         }
892         final Message msg = mHandler.obtainMessage(CMD_START_SOCKET_KEEPALIVE, slot,
893                 (int) intervalSeconds, packet);
894         startSocketKeepalive(msg);
895         msg.recycle();
896     }
897     /** @hide TODO delete once subclasses have moved to onStartSocketKeepalive */
startSocketKeepalive(Message msg)898     protected void startSocketKeepalive(Message msg) {
899         onSocketKeepaliveEvent(msg.arg1, SocketKeepalive.ERROR_UNSUPPORTED);
900     }
901 
902     /**
903      * Requests that the network hardware stop a previously-started keepalive.
904      *
905      * @param slot the hardware slot on which to stop the keepalive.
906      */
onStopSocketKeepalive(int slot)907     public void onStopSocketKeepalive(int slot) {
908         Message msg = mHandler.obtainMessage(CMD_STOP_SOCKET_KEEPALIVE, slot, 0, null);
909         stopSocketKeepalive(msg);
910         msg.recycle();
911     }
912     /** @hide TODO delete once subclasses have moved to onStopSocketKeepalive */
stopSocketKeepalive(Message msg)913     protected void stopSocketKeepalive(Message msg) {
914         onSocketKeepaliveEvent(msg.arg1, SocketKeepalive.ERROR_UNSUPPORTED);
915     }
916 
917     /**
918      * Must be called by the agent when a socket keepalive event occurs.
919      *
920      * @param slot the hardware slot on which the event occurred.
921      * @param event the event that occurred, as one of the SocketKeepalive.ERROR_*
922      *              or SocketKeepalive.SUCCESS constants.
923      */
sendSocketKeepaliveEvent(int slot, @SocketKeepalive.KeepaliveEvent int event)924     public final void sendSocketKeepaliveEvent(int slot,
925             @SocketKeepalive.KeepaliveEvent int event) {
926         queueOrSendMessage(EVENT_SOCKET_KEEPALIVE, slot, event);
927     }
928     /** @hide TODO delete once callers have moved to sendSocketKeepaliveEvent */
onSocketKeepaliveEvent(int slot, int reason)929     public void onSocketKeepaliveEvent(int slot, int reason) {
930         sendSocketKeepaliveEvent(slot, reason);
931     }
932 
933     /**
934      * Called by ConnectivityService to add specific packet filter to network hardware to block
935      * replies (e.g., TCP ACKs) matching the sent keepalive packets. Implementations that support
936      * this feature must override this method.
937      *
938      * @param slot the hardware slot on which the keepalive should be sent.
939      * @param packet the packet that is being sent.
940      */
onAddKeepalivePacketFilter(int slot, @NonNull KeepalivePacketData packet)941     public void onAddKeepalivePacketFilter(int slot, @NonNull KeepalivePacketData packet) {
942         Message msg = mHandler.obtainMessage(CMD_ADD_KEEPALIVE_PACKET_FILTER, slot, 0, packet);
943         addKeepalivePacketFilter(msg);
944         msg.recycle();
945     }
946     /** @hide TODO delete once subclasses have moved to onAddKeepalivePacketFilter */
addKeepalivePacketFilter(Message msg)947     protected void addKeepalivePacketFilter(Message msg) {
948     }
949 
950     /**
951      * Called by ConnectivityService to remove a packet filter installed with
952      * {@link #addKeepalivePacketFilter(Message)}. Implementations that support this feature
953      * must override this method.
954      *
955      * @param slot the hardware slot on which the keepalive is being sent.
956      */
onRemoveKeepalivePacketFilter(int slot)957     public void onRemoveKeepalivePacketFilter(int slot) {
958         Message msg = mHandler.obtainMessage(CMD_REMOVE_KEEPALIVE_PACKET_FILTER, slot, 0, null);
959         removeKeepalivePacketFilter(msg);
960         msg.recycle();
961     }
962     /** @hide TODO delete once subclasses have moved to onRemoveKeepalivePacketFilter */
removeKeepalivePacketFilter(Message msg)963     protected void removeKeepalivePacketFilter(Message msg) {
964     }
965 
966     /**
967      * Called by ConnectivityService to inform this network agent of signal strength thresholds
968      * that when crossed should trigger a system wakeup and a NetworkCapabilities update.
969      *
970      * When the system updates the list of thresholds that should wake up the CPU for a
971      * given agent it will call this method on the agent. The agent that implement this
972      * should implement it in hardware so as to ensure the CPU will be woken up on breach.
973      * Agents are expected to react to a breach by sending an updated NetworkCapabilities
974      * object with the appropriate signal strength to sendNetworkCapabilities.
975      *
976      * The specific units are bearer-dependent. See details on the units and requests in
977      * {@link NetworkCapabilities.Builder#setSignalStrength}.
978      *
979      * @param thresholds the array of thresholds that should trigger wakeups.
980      */
onSignalStrengthThresholdsUpdated(@onNull int[] thresholds)981     public void onSignalStrengthThresholdsUpdated(@NonNull int[] thresholds) {
982         setSignalStrengthThresholds(thresholds);
983     }
984     /** @hide TODO delete once subclasses have moved to onSetSignalStrengthThresholds */
setSignalStrengthThresholds(int[] thresholds)985     protected void setSignalStrengthThresholds(int[] thresholds) {
986     }
987 
988     /**
989      * Called when the user asks to not stay connected to this network because it was found to not
990      * provide Internet access.  Usually followed by call to {@code unwanted}.  The transport is
991      * responsible for making sure the device does not automatically reconnect to the same network
992      * after the {@code unwanted} call.
993      */
onAutomaticReconnectDisabled()994     public void onAutomaticReconnectDisabled() {
995         preventAutomaticReconnect();
996     }
997     /** @hide TODO delete once subclasses have moved to onAutomaticReconnectDisabled */
preventAutomaticReconnect()998     protected void preventAutomaticReconnect() {
999     }
1000 
1001     /** @hide */
log(String s)1002     protected void log(String s) {
1003         Log.d(LOG_TAG, "NetworkAgent: " + s);
1004     }
1005 }
1006