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, 0, config.legacyTypeName, "");
358         ni.setIsAvailable(true);
359         ni.setExtraInfo(config.getLegacyExtraInfo());
360         return ni;
361     }
362 
363     /**
364      * Create a new network agent.
365      * @param context a {@link Context} to get system services from.
366      * @param looper the {@link Looper} on which to invoke the callbacks.
367      * @param logTag the tag for logs
368      * @param nc the initial {@link NetworkCapabilities} of this network. Update with
369      *           sendNetworkCapabilities.
370      * @param lp the initial {@link LinkProperties} of this network. Update with sendLinkProperties.
371      * @param score the initial score of this network. Update with sendNetworkScore.
372      * @param config an immutable {@link NetworkAgentConfig} for this agent.
373      * @param provider the {@link NetworkProvider} managing this agent.
374      */
NetworkAgent(@onNull Context context, @NonNull Looper looper, @NonNull String logTag, @NonNull NetworkCapabilities nc, @NonNull LinkProperties lp, int score, @NonNull NetworkAgentConfig config, @Nullable NetworkProvider provider)375     public NetworkAgent(@NonNull Context context, @NonNull Looper looper, @NonNull String logTag,
376             @NonNull NetworkCapabilities nc, @NonNull LinkProperties lp, int score,
377             @NonNull NetworkAgentConfig config, @Nullable NetworkProvider provider) {
378         this(looper, context, logTag, nc, lp, score, config,
379                 provider == null ? NetworkProvider.ID_NONE : provider.getProviderId(),
380                 getLegacyNetworkInfo(config), false /* legacy */);
381     }
382 
383     private static class InitialConfiguration {
384         public final Context context;
385         public final NetworkCapabilities capabilities;
386         public final LinkProperties properties;
387         public final int score;
388         public final NetworkAgentConfig config;
389         public final NetworkInfo info;
InitialConfiguration(@onNull Context context, @NonNull NetworkCapabilities capabilities, @NonNull LinkProperties properties, int score, @NonNull NetworkAgentConfig config, @NonNull NetworkInfo info)390         InitialConfiguration(@NonNull Context context, @NonNull NetworkCapabilities capabilities,
391                 @NonNull LinkProperties properties, int score, @NonNull NetworkAgentConfig config,
392                 @NonNull NetworkInfo info) {
393             this.context = context;
394             this.capabilities = capabilities;
395             this.properties = properties;
396             this.score = score;
397             this.config = config;
398             this.info = info;
399         }
400     }
401     private volatile InitialConfiguration mInitialConfiguration;
402 
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)403     private NetworkAgent(@NonNull Looper looper, @NonNull Context context, @NonNull String logTag,
404             @NonNull NetworkCapabilities nc, @NonNull LinkProperties lp, int score,
405             @NonNull NetworkAgentConfig config, int providerId, @NonNull NetworkInfo ni,
406             boolean legacy) {
407         mHandler = new NetworkAgentHandler(looper);
408         LOG_TAG = logTag;
409         mIsLegacy = legacy;
410         mNetworkInfo = new NetworkInfo(ni);
411         this.providerId = providerId;
412         if (ni == null || nc == null || lp == null) {
413             throw new IllegalArgumentException();
414         }
415 
416         mInitialConfiguration = new InitialConfiguration(context, new NetworkCapabilities(nc),
417                 new LinkProperties(lp), score, config, ni);
418     }
419 
420     private class NetworkAgentHandler extends Handler {
NetworkAgentHandler(Looper looper)421         NetworkAgentHandler(Looper looper) {
422             super(looper);
423         }
424 
425         @Override
handleMessage(Message msg)426         public void handleMessage(Message msg) {
427             switch (msg.what) {
428                 case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: {
429                     if (mAsyncChannel != null) {
430                         log("Received new connection while already connected!");
431                     } else {
432                         if (VDBG) log("NetworkAgent fully connected");
433                         AsyncChannel ac = new AsyncChannel();
434                         ac.connected(null, this, msg.replyTo);
435                         ac.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
436                                 AsyncChannel.STATUS_SUCCESSFUL);
437                         synchronized (mPreConnectedQueue) {
438                             mAsyncChannel = ac;
439                             for (Message m : mPreConnectedQueue) {
440                                 ac.sendMessage(m);
441                             }
442                             mPreConnectedQueue.clear();
443                         }
444                     }
445                     break;
446                 }
447                 case AsyncChannel.CMD_CHANNEL_DISCONNECT: {
448                     if (VDBG) log("CMD_CHANNEL_DISCONNECT");
449                     if (mAsyncChannel != null) mAsyncChannel.disconnect();
450                     break;
451                 }
452                 case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
453                     if (DBG) log("NetworkAgent channel lost");
454                     // let the client know CS is done with us.
455                     onNetworkUnwanted();
456                     synchronized (mPreConnectedQueue) {
457                         mAsyncChannel = null;
458                     }
459                     break;
460                 }
461                 case CMD_SUSPECT_BAD: {
462                     log("Unhandled Message " + msg);
463                     break;
464                 }
465                 case CMD_REQUEST_BANDWIDTH_UPDATE: {
466                     long currentTimeMs = System.currentTimeMillis();
467                     if (VDBG) {
468                         log("CMD_REQUEST_BANDWIDTH_UPDATE request received.");
469                     }
470                     if (currentTimeMs >= (mLastBwRefreshTime + BW_REFRESH_MIN_WIN_MS)) {
471                         mBandwidthUpdateScheduled = false;
472                         if (!mBandwidthUpdatePending.getAndSet(true)) {
473                             onBandwidthUpdateRequested();
474                         }
475                     } else {
476                         // deliver the request at a later time rather than discard it completely.
477                         if (!mBandwidthUpdateScheduled) {
478                             long waitTime = mLastBwRefreshTime + BW_REFRESH_MIN_WIN_MS
479                                     - currentTimeMs + 1;
480                             mBandwidthUpdateScheduled = sendEmptyMessageDelayed(
481                                     CMD_REQUEST_BANDWIDTH_UPDATE, waitTime);
482                         }
483                     }
484                     break;
485                 }
486                 case CMD_REPORT_NETWORK_STATUS: {
487                     String redirectUrl = ((Bundle) msg.obj).getString(REDIRECT_URL_KEY);
488                     if (VDBG) {
489                         log("CMD_REPORT_NETWORK_STATUS("
490                                 + (msg.arg1 == VALID_NETWORK ? "VALID, " : "INVALID, ")
491                                 + redirectUrl);
492                     }
493                     Uri uri = null;
494                     try {
495                         if (null != redirectUrl) {
496                             uri = Uri.parse(redirectUrl);
497                         }
498                     } catch (Exception e) {
499                         Log.wtf(LOG_TAG, "Surprising URI : " + redirectUrl, e);
500                     }
501                     onValidationStatus(msg.arg1 /* status */, uri);
502                     break;
503                 }
504                 case CMD_SAVE_ACCEPT_UNVALIDATED: {
505                     onSaveAcceptUnvalidated(msg.arg1 != 0);
506                     break;
507                 }
508                 case CMD_START_SOCKET_KEEPALIVE: {
509                     onStartSocketKeepalive(msg.arg1 /* slot */,
510                             Duration.ofSeconds(msg.arg2) /* interval */,
511                             (KeepalivePacketData) msg.obj /* packet */);
512                     break;
513                 }
514                 case CMD_STOP_SOCKET_KEEPALIVE: {
515                     onStopSocketKeepalive(msg.arg1 /* slot */);
516                     break;
517                 }
518 
519                 case CMD_SET_SIGNAL_STRENGTH_THRESHOLDS: {
520                     ArrayList<Integer> thresholds =
521                             ((Bundle) msg.obj).getIntegerArrayList("thresholds");
522                     // TODO: Change signal strength thresholds API to use an ArrayList<Integer>
523                     // rather than convert to int[].
524                     int[] intThresholds = new int[(thresholds != null) ? thresholds.size() : 0];
525                     for (int i = 0; i < intThresholds.length; i++) {
526                         intThresholds[i] = thresholds.get(i);
527                     }
528                     onSignalStrengthThresholdsUpdated(intThresholds);
529                     break;
530                 }
531                 case CMD_PREVENT_AUTOMATIC_RECONNECT: {
532                     onAutomaticReconnectDisabled();
533                     break;
534                 }
535                 case CMD_ADD_KEEPALIVE_PACKET_FILTER: {
536                     onAddKeepalivePacketFilter(msg.arg1 /* slot */,
537                             (KeepalivePacketData) msg.obj /* packet */);
538                     break;
539                 }
540                 case CMD_REMOVE_KEEPALIVE_PACKET_FILTER: {
541                     onRemoveKeepalivePacketFilter(msg.arg1 /* slot */);
542                     break;
543                 }
544             }
545         }
546     }
547 
548     /**
549      * Register this network agent with ConnectivityService.
550      *
551      * This method can only be called once per network agent.
552      *
553      * @return the Network associated with this network agent (which can also be obtained later
554      *         by calling getNetwork() on this agent).
555      * @throws IllegalStateException thrown by the system server if this network agent is
556      *         already registered.
557      */
558     @NonNull
register()559     public Network register() {
560         if (VDBG) log("Registering NetworkAgent");
561         synchronized (mRegisterLock) {
562             if (mNetwork != null) {
563                 throw new IllegalStateException("Agent already registered");
564             }
565             final ConnectivityManager cm = (ConnectivityManager) mInitialConfiguration.context
566                     .getSystemService(Context.CONNECTIVITY_SERVICE);
567             mNetwork = cm.registerNetworkAgent(new Messenger(mHandler),
568                     new NetworkInfo(mInitialConfiguration.info),
569                     mInitialConfiguration.properties, mInitialConfiguration.capabilities,
570                     mInitialConfiguration.score, mInitialConfiguration.config, providerId);
571             mInitialConfiguration = null; // All this memory can now be GC'd
572         }
573         return mNetwork;
574     }
575 
576     /**
577      * Register this network agent with a testing harness.
578      *
579      * The returned Messenger sends messages to the Handler. This allows a test to send
580      * this object {@code CMD_*} messages as if they came from ConnectivityService, which
581      * is useful for testing the behavior.
582      *
583      * @hide
584      */
registerForTest(final Network network)585     public Messenger registerForTest(final Network network) {
586         log("Registering NetworkAgent for test");
587         synchronized (mRegisterLock) {
588             mNetwork = network;
589             mInitialConfiguration = null;
590         }
591         return new Messenger(mHandler);
592     }
593 
594     /**
595      * Waits for the handler to be idle.
596      * This is useful for testing, and has smaller scope than an accessor to mHandler.
597      * TODO : move the implementation in common library with the tests
598      * @hide
599      */
600     @VisibleForTesting
waitForIdle(final long timeoutMs)601     public boolean waitForIdle(final long timeoutMs) {
602         final ConditionVariable cv = new ConditionVariable(false);
603         mHandler.post(cv::open);
604         return cv.block(timeoutMs);
605     }
606 
607     /**
608      * @return The Network associated with this agent, or null if it's not registered yet.
609      */
610     @Nullable
getNetwork()611     public Network getNetwork() {
612         return mNetwork;
613     }
614 
queueOrSendMessage(int what, Object obj)615     private void queueOrSendMessage(int what, Object obj) {
616         queueOrSendMessage(what, 0, 0, obj);
617     }
618 
queueOrSendMessage(int what, int arg1, int arg2)619     private void queueOrSendMessage(int what, int arg1, int arg2) {
620         queueOrSendMessage(what, arg1, arg2, null);
621     }
622 
queueOrSendMessage(int what, int arg1, int arg2, Object obj)623     private void queueOrSendMessage(int what, int arg1, int arg2, Object obj) {
624         Message msg = Message.obtain();
625         msg.what = what;
626         msg.arg1 = arg1;
627         msg.arg2 = arg2;
628         msg.obj = obj;
629         queueOrSendMessage(msg);
630     }
631 
queueOrSendMessage(Message msg)632     private void queueOrSendMessage(Message msg) {
633         synchronized (mPreConnectedQueue) {
634             if (mAsyncChannel != null) {
635                 mAsyncChannel.sendMessage(msg);
636             } else {
637                 mPreConnectedQueue.add(msg);
638             }
639         }
640     }
641 
642     /**
643      * Must be called by the agent when the network's {@link LinkProperties} change.
644      * @param linkProperties the new LinkProperties.
645      */
sendLinkProperties(@onNull LinkProperties linkProperties)646     public final void sendLinkProperties(@NonNull LinkProperties linkProperties) {
647         Objects.requireNonNull(linkProperties);
648         queueOrSendMessage(EVENT_NETWORK_PROPERTIES_CHANGED, new LinkProperties(linkProperties));
649     }
650 
651     /**
652      * Inform ConnectivityService that this agent has now connected.
653      * Call {@link #unregister} to disconnect.
654      */
markConnected()655     public void markConnected() {
656         if (mIsLegacy) {
657             throw new UnsupportedOperationException(
658                     "Legacy agents can't call markConnected.");
659         }
660         // |reason| cannot be used by the non-legacy agents
661         mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null /* reason */,
662                 mNetworkInfo.getExtraInfo());
663         queueOrSendMessage(EVENT_NETWORK_INFO_CHANGED, mNetworkInfo);
664     }
665 
666     /**
667      * Unregister this network agent.
668      *
669      * This signals the network has disconnected and ends its lifecycle. After this is called,
670      * the network is torn down and this agent can no longer be used.
671      */
unregister()672     public void unregister() {
673         if (mIsLegacy) {
674             throw new UnsupportedOperationException("Legacy agents can't call unregister.");
675         }
676         // When unregistering an agent nobody should use the extrainfo (or reason) any more.
677         mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, null /* reason */,
678                 null /* extraInfo */);
679         queueOrSendMessage(EVENT_NETWORK_INFO_CHANGED, mNetworkInfo);
680     }
681 
682     /**
683      * Change the legacy subtype of this network agent.
684      *
685      * This is only for backward compatibility and should not be used by non-legacy network agents,
686      * or agents that did not use to set a subtype. As such, only TYPE_MOBILE type agents can use
687      * this and others will be thrown an exception if they try.
688      *
689      * @deprecated this is for backward compatibility only.
690      * @param legacySubtype the legacy subtype.
691      * @hide
692      */
693     @Deprecated
setLegacySubtype(final int legacySubtype, @NonNull final String legacySubtypeName)694     public void setLegacySubtype(final int legacySubtype, @NonNull final String legacySubtypeName) {
695         if (mIsLegacy) {
696             throw new UnsupportedOperationException("Legacy agents can't call setLegacySubtype.");
697         }
698         mNetworkInfo.setSubtype(legacySubtype, legacySubtypeName);
699         queueOrSendMessage(EVENT_NETWORK_INFO_CHANGED, mNetworkInfo);
700     }
701 
702     /**
703      * Set the ExtraInfo of this network agent.
704      *
705      * This sets the ExtraInfo field inside the NetworkInfo returned by legacy public API and the
706      * broadcasts about the corresponding Network.
707      * This is only for backward compatibility and should not be used by non-legacy network agents,
708      * who will be thrown an exception if they try. The extra info should only be :
709      * <ul>
710      *   <li>For cellular agents, the APN name.</li>
711      *   <li>For ethernet agents, the interface name.</li>
712      * </ul>
713      *
714      * @deprecated this is for backward compatibility only.
715      * @param extraInfo the ExtraInfo.
716      * @hide
717      */
718     @Deprecated
setLegacyExtraInfo(@ullable final String extraInfo)719     public void setLegacyExtraInfo(@Nullable final String extraInfo) {
720         if (mIsLegacy) {
721             throw new UnsupportedOperationException("Legacy agents can't call setLegacyExtraInfo.");
722         }
723         mNetworkInfo.setExtraInfo(extraInfo);
724         queueOrSendMessage(EVENT_NETWORK_INFO_CHANGED, mNetworkInfo);
725     }
726 
727     /**
728      * Must be called by the agent when it has a new NetworkInfo object.
729      * @hide TODO: expose something better.
730      */
731     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
sendNetworkInfo(NetworkInfo networkInfo)732     public final void sendNetworkInfo(NetworkInfo networkInfo) {
733         if (!mIsLegacy) {
734             throw new UnsupportedOperationException("Only legacy agents can call sendNetworkInfo.");
735         }
736         queueOrSendMessage(EVENT_NETWORK_INFO_CHANGED, new NetworkInfo(networkInfo));
737     }
738 
739     /**
740      * Must be called by the agent when the network's {@link NetworkCapabilities} change.
741      * @param networkCapabilities the new NetworkCapabilities.
742      */
sendNetworkCapabilities(@onNull NetworkCapabilities networkCapabilities)743     public final void sendNetworkCapabilities(@NonNull NetworkCapabilities networkCapabilities) {
744         Objects.requireNonNull(networkCapabilities);
745         mBandwidthUpdatePending.set(false);
746         mLastBwRefreshTime = System.currentTimeMillis();
747         queueOrSendMessage(EVENT_NETWORK_CAPABILITIES_CHANGED,
748                 new NetworkCapabilities(networkCapabilities));
749     }
750 
751     /**
752      * Must be called by the agent to update the score of this network.
753      *
754      * @param score the new score, between 0 and 99.
755      */
sendNetworkScore(@ntRangefrom = 0, to = 99) int score)756     public final void sendNetworkScore(@IntRange(from = 0, to = 99) int score) {
757         if (score < 0) {
758             throw new IllegalArgumentException("Score must be >= 0");
759         }
760         queueOrSendMessage(EVENT_NETWORK_SCORE_CHANGED, score, 0);
761     }
762 
763     /**
764      * Must be called by the agent to indicate this network was manually selected by the user.
765      * This should be called before the NetworkInfo is marked CONNECTED so that this
766      * Network can be given special treatment at that time. If {@code acceptUnvalidated} is
767      * {@code true}, then the system will switch to this network. If it is {@code false} and the
768      * network cannot be validated, the system will ask the user whether to switch to this network.
769      * If the user confirms and selects "don't ask again", then the system will call
770      * {@link #saveAcceptUnvalidated} to persist the user's choice. Thus, if the transport ever
771      * calls this method with {@code acceptUnvalidated} set to {@code false}, it must also implement
772      * {@link #saveAcceptUnvalidated} to respect the user's choice.
773      * @hide should move to NetworkAgentConfig.
774      */
explicitlySelected(boolean acceptUnvalidated)775     public void explicitlySelected(boolean acceptUnvalidated) {
776         explicitlySelected(true /* explicitlySelected */, acceptUnvalidated);
777     }
778 
779     /**
780      * Must be called by the agent to indicate whether the network was manually selected by the
781      * user. This should be called before the network becomes connected, so it can be given
782      * special treatment when it does.
783      *
784      * If {@code explicitlySelected} is {@code true}, and {@code acceptUnvalidated} is {@code true},
785      * then the system will switch to this network. If {@code explicitlySelected} is {@code true}
786      * and {@code acceptUnvalidated} is {@code false}, and the  network cannot be validated, the
787      * system will ask the user whether to switch to this network.  If the user confirms and selects
788      * "don't ask again", then the system will call {@link #saveAcceptUnvalidated} to persist the
789      * user's choice. Thus, if the transport ever calls this method with {@code explicitlySelected}
790      * set to {@code true} and {@code acceptUnvalidated} set to {@code false}, it must also
791      * implement {@link #saveAcceptUnvalidated} to respect the user's choice.
792      *
793      * If  {@code explicitlySelected} is {@code false} and {@code acceptUnvalidated} is
794      * {@code true}, the system will interpret this as the user having accepted partial connectivity
795      * on this network. Thus, the system will switch to the network and consider it validated even
796      * if it only provides partial connectivity, but the network is not otherwise treated specially.
797      * @hide should move to NetworkAgentConfig.
798      */
explicitlySelected(boolean explicitlySelected, boolean acceptUnvalidated)799     public void explicitlySelected(boolean explicitlySelected, boolean acceptUnvalidated) {
800         queueOrSendMessage(EVENT_SET_EXPLICITLY_SELECTED,
801                 explicitlySelected ? 1 : 0,
802                 acceptUnvalidated ? 1 : 0);
803     }
804 
805     /**
806      * Called when ConnectivityService has indicated they no longer want this network.
807      * The parent factory should (previously) have received indication of the change
808      * as well, either canceling NetworkRequests or altering their score such that this
809      * network won't be immediately requested again.
810      */
onNetworkUnwanted()811     public void onNetworkUnwanted() {
812         unwanted();
813     }
814     /** @hide TODO delete once subclasses have moved to onNetworkUnwanted. */
unwanted()815     protected void unwanted() {
816     }
817 
818     /**
819      * Called when ConnectivityService request a bandwidth update. The parent factory
820      * shall try to overwrite this method and produce a bandwidth update if capable.
821      * @hide
822      */
onBandwidthUpdateRequested()823     public void onBandwidthUpdateRequested() {
824         pollLceData();
825     }
826     /** @hide TODO delete once subclasses have moved to onBandwidthUpdateRequested. */
pollLceData()827     protected void pollLceData() {
828     }
829 
830     /**
831      * Called when the system determines the usefulness of this network.
832      *
833      * The system attempts to validate Internet connectivity on networks that provide the
834      * {@link NetworkCapabilities#NET_CAPABILITY_INTERNET} capability.
835      *
836      * Currently there are two possible values:
837      * {@code VALIDATION_STATUS_VALID} if Internet connectivity was validated,
838      * {@code VALIDATION_STATUS_NOT_VALID} if Internet connectivity was not validated.
839      *
840      * This is guaranteed to be called again when the network status changes, but the system
841      * may also call this multiple times even if the status does not change.
842      *
843      * @param status one of {@code VALIDATION_STATUS_VALID} or {@code VALIDATION_STATUS_NOT_VALID}.
844      * @param redirectUri If Internet connectivity is being redirected (e.g., on a captive portal),
845      *        this is the destination the probes are being redirected to, otherwise {@code null}.
846      */
onValidationStatus(@alidationStatus int status, @Nullable Uri redirectUri)847     public void onValidationStatus(@ValidationStatus int status, @Nullable Uri redirectUri) {
848         networkStatus(status, null == redirectUri ? "" : redirectUri.toString());
849     }
850     /** @hide TODO delete once subclasses have moved to onValidationStatus */
networkStatus(int status, String redirectUrl)851     protected void networkStatus(int status, String redirectUrl) {
852     }
853 
854 
855     /**
856      * Called when the user asks to remember the choice to use this network even if unvalidated.
857      * The transport is responsible for remembering the choice, and the next time the user connects
858      * to the network, should explicitlySelected with {@code acceptUnvalidated} set to {@code true}.
859      * This method will only be called if {@link #explicitlySelected} was called with
860      * {@code acceptUnvalidated} set to {@code false}.
861      * @param accept whether the user wants to use the network even if unvalidated.
862      */
onSaveAcceptUnvalidated(boolean accept)863     public void onSaveAcceptUnvalidated(boolean accept) {
864         saveAcceptUnvalidated(accept);
865     }
866     /** @hide TODO delete once subclasses have moved to onSaveAcceptUnvalidated */
saveAcceptUnvalidated(boolean accept)867     protected void saveAcceptUnvalidated(boolean accept) {
868     }
869 
870     /**
871      * Requests that the network hardware send the specified packet at the specified interval.
872      *
873      * @param slot the hardware slot on which to start the keepalive.
874      * @param interval the interval between packets, between 10 and 3600. Note that this API
875      *                 does not support sub-second precision and will round off the request.
876      * @param packet the packet to send.
877      */
878     // seconds is from SocketKeepalive.MIN_INTERVAL_SEC to MAX_INTERVAL_SEC, but these should
879     // not be exposed as constants because they may change in the future (API guideline 4.8)
880     // and should have getters if exposed at all. Getters can't be used in the annotation,
881     // so the values unfortunately need to be copied.
onStartSocketKeepalive(int slot, @NonNull Duration interval, @NonNull KeepalivePacketData packet)882     public void onStartSocketKeepalive(int slot, @NonNull Duration interval,
883             @NonNull KeepalivePacketData packet) {
884         final long intervalSeconds = interval.getSeconds();
885         if (intervalSeconds < SocketKeepalive.MIN_INTERVAL_SEC
886                 || intervalSeconds > SocketKeepalive.MAX_INTERVAL_SEC) {
887             throw new IllegalArgumentException("Interval needs to be comprised between "
888                     + SocketKeepalive.MIN_INTERVAL_SEC + " and " + SocketKeepalive.MAX_INTERVAL_SEC
889                     + " but was " + intervalSeconds);
890         }
891         final Message msg = mHandler.obtainMessage(CMD_START_SOCKET_KEEPALIVE, slot,
892                 (int) intervalSeconds, packet);
893         startSocketKeepalive(msg);
894         msg.recycle();
895     }
896     /** @hide TODO delete once subclasses have moved to onStartSocketKeepalive */
startSocketKeepalive(Message msg)897     protected void startSocketKeepalive(Message msg) {
898         onSocketKeepaliveEvent(msg.arg1, SocketKeepalive.ERROR_UNSUPPORTED);
899     }
900 
901     /**
902      * Requests that the network hardware stop a previously-started keepalive.
903      *
904      * @param slot the hardware slot on which to stop the keepalive.
905      */
onStopSocketKeepalive(int slot)906     public void onStopSocketKeepalive(int slot) {
907         Message msg = mHandler.obtainMessage(CMD_STOP_SOCKET_KEEPALIVE, slot, 0, null);
908         stopSocketKeepalive(msg);
909         msg.recycle();
910     }
911     /** @hide TODO delete once subclasses have moved to onStopSocketKeepalive */
stopSocketKeepalive(Message msg)912     protected void stopSocketKeepalive(Message msg) {
913         onSocketKeepaliveEvent(msg.arg1, SocketKeepalive.ERROR_UNSUPPORTED);
914     }
915 
916     /**
917      * Must be called by the agent when a socket keepalive event occurs.
918      *
919      * @param slot the hardware slot on which the event occurred.
920      * @param event the event that occurred, as one of the SocketKeepalive.ERROR_*
921      *              or SocketKeepalive.SUCCESS constants.
922      */
sendSocketKeepaliveEvent(int slot, @SocketKeepalive.KeepaliveEvent int event)923     public final void sendSocketKeepaliveEvent(int slot,
924             @SocketKeepalive.KeepaliveEvent int event) {
925         queueOrSendMessage(EVENT_SOCKET_KEEPALIVE, slot, event);
926     }
927     /** @hide TODO delete once callers have moved to sendSocketKeepaliveEvent */
onSocketKeepaliveEvent(int slot, int reason)928     public void onSocketKeepaliveEvent(int slot, int reason) {
929         sendSocketKeepaliveEvent(slot, reason);
930     }
931 
932     /**
933      * Called by ConnectivityService to add specific packet filter to network hardware to block
934      * replies (e.g., TCP ACKs) matching the sent keepalive packets. Implementations that support
935      * this feature must override this method.
936      *
937      * @param slot the hardware slot on which the keepalive should be sent.
938      * @param packet the packet that is being sent.
939      */
onAddKeepalivePacketFilter(int slot, @NonNull KeepalivePacketData packet)940     public void onAddKeepalivePacketFilter(int slot, @NonNull KeepalivePacketData packet) {
941         Message msg = mHandler.obtainMessage(CMD_ADD_KEEPALIVE_PACKET_FILTER, slot, 0, packet);
942         addKeepalivePacketFilter(msg);
943         msg.recycle();
944     }
945     /** @hide TODO delete once subclasses have moved to onAddKeepalivePacketFilter */
addKeepalivePacketFilter(Message msg)946     protected void addKeepalivePacketFilter(Message msg) {
947     }
948 
949     /**
950      * Called by ConnectivityService to remove a packet filter installed with
951      * {@link #addKeepalivePacketFilter(Message)}. Implementations that support this feature
952      * must override this method.
953      *
954      * @param slot the hardware slot on which the keepalive is being sent.
955      */
onRemoveKeepalivePacketFilter(int slot)956     public void onRemoveKeepalivePacketFilter(int slot) {
957         Message msg = mHandler.obtainMessage(CMD_REMOVE_KEEPALIVE_PACKET_FILTER, slot, 0, null);
958         removeKeepalivePacketFilter(msg);
959         msg.recycle();
960     }
961     /** @hide TODO delete once subclasses have moved to onRemoveKeepalivePacketFilter */
removeKeepalivePacketFilter(Message msg)962     protected void removeKeepalivePacketFilter(Message msg) {
963     }
964 
965     /**
966      * Called by ConnectivityService to inform this network agent of signal strength thresholds
967      * that when crossed should trigger a system wakeup and a NetworkCapabilities update.
968      *
969      * When the system updates the list of thresholds that should wake up the CPU for a
970      * given agent it will call this method on the agent. The agent that implement this
971      * should implement it in hardware so as to ensure the CPU will be woken up on breach.
972      * Agents are expected to react to a breach by sending an updated NetworkCapabilities
973      * object with the appropriate signal strength to sendNetworkCapabilities.
974      *
975      * The specific units are bearer-dependent. See details on the units and requests in
976      * {@link NetworkCapabilities.Builder#setSignalStrength}.
977      *
978      * @param thresholds the array of thresholds that should trigger wakeups.
979      */
onSignalStrengthThresholdsUpdated(@onNull int[] thresholds)980     public void onSignalStrengthThresholdsUpdated(@NonNull int[] thresholds) {
981         setSignalStrengthThresholds(thresholds);
982     }
983     /** @hide TODO delete once subclasses have moved to onSetSignalStrengthThresholds */
setSignalStrengthThresholds(int[] thresholds)984     protected void setSignalStrengthThresholds(int[] thresholds) {
985     }
986 
987     /**
988      * Called when the user asks to not stay connected to this network because it was found to not
989      * provide Internet access.  Usually followed by call to {@code unwanted}.  The transport is
990      * responsible for making sure the device does not automatically reconnect to the same network
991      * after the {@code unwanted} call.
992      */
onAutomaticReconnectDisabled()993     public void onAutomaticReconnectDisabled() {
994         preventAutomaticReconnect();
995     }
996     /** @hide TODO delete once subclasses have moved to onAutomaticReconnectDisabled */
preventAutomaticReconnect()997     protected void preventAutomaticReconnect() {
998     }
999 
1000     /** @hide */
log(String s)1001     protected void log(String s) {
1002         Log.d(LOG_TAG, "NetworkAgent: " + s);
1003     }
1004 }
1005