1 /*
2  * Copyright (C) 2019 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 com.android.server;
18 
19 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
20 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
21 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
22 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
23 import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
24 import static android.net.NetworkCapabilities.TRANSPORT_VPN;
25 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
26 import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE;
27 
28 import static com.android.server.ConnectivityServiceTestUtils.transportToLegacyType;
29 
30 import static junit.framework.Assert.assertTrue;
31 
32 import static org.junit.Assert.assertEquals;
33 import static org.junit.Assert.fail;
34 
35 import android.annotation.NonNull;
36 import android.content.Context;
37 import android.net.ConnectivityManager;
38 import android.net.LinkProperties;
39 import android.net.Network;
40 import android.net.NetworkAgent;
41 import android.net.NetworkAgentConfig;
42 import android.net.NetworkCapabilities;
43 import android.net.NetworkProvider;
44 import android.net.NetworkScore;
45 import android.net.NetworkSpecifier;
46 import android.net.QosFilter;
47 import android.net.SocketKeepalive;
48 import android.os.ConditionVariable;
49 import android.os.HandlerThread;
50 import android.os.Message;
51 import android.util.Log;
52 import android.util.Range;
53 
54 import com.android.net.module.util.ArrayTrackRecord;
55 import com.android.testutils.HandlerUtils;
56 import com.android.testutils.TestableNetworkCallback;
57 
58 import java.util.List;
59 import java.util.Objects;
60 import java.util.Set;
61 import java.util.concurrent.atomic.AtomicBoolean;
62 
63 public class NetworkAgentWrapper implements TestableNetworkCallback.HasNetwork {
64     private final NetworkCapabilities mNetworkCapabilities;
65     private final HandlerThread mHandlerThread;
66     private final Context mContext;
67     private final String mLogTag;
68     private final NetworkAgentConfig mNetworkAgentConfig;
69 
70     private final ConditionVariable mDisconnected = new ConditionVariable();
71     private final ConditionVariable mPreventReconnectReceived = new ConditionVariable();
72     private final AtomicBoolean mConnected = new AtomicBoolean(false);
73     private NetworkScore mScore;
74     private NetworkAgent mNetworkAgent;
75     private int mStartKeepaliveError = SocketKeepalive.ERROR_UNSUPPORTED;
76     private int mStopKeepaliveError = SocketKeepalive.NO_KEEPALIVE;
77     // Controls how test network agent is going to wait before responding to keepalive
78     // start/stop. Useful when simulate KeepaliveTracker is waiting for response from modem.
79     private long mKeepaliveResponseDelay = 0L;
80     private Integer mExpectedKeepaliveSlot = null;
81     private final ArrayTrackRecord<CallbackType>.ReadHead mCallbackHistory =
82             new ArrayTrackRecord<CallbackType>().newReadHead();
83 
NetworkAgentWrapper(int transport, LinkProperties linkProperties, NetworkCapabilities ncTemplate, Context context)84     public NetworkAgentWrapper(int transport, LinkProperties linkProperties,
85             NetworkCapabilities ncTemplate, Context context) throws Exception {
86         final int type = transportToLegacyType(transport);
87         final String typeName = ConnectivityManager.getNetworkTypeName(type);
88         mNetworkCapabilities = (ncTemplate != null) ? ncTemplate : new NetworkCapabilities();
89         mNetworkCapabilities.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
90         mNetworkCapabilities.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
91         mNetworkCapabilities.addTransportType(transport);
92         switch (transport) {
93             case TRANSPORT_ETHERNET:
94                 mScore = new NetworkScore.Builder().setLegacyInt(70).build();
95                 break;
96             case TRANSPORT_WIFI:
97                 mScore = new NetworkScore.Builder().setLegacyInt(60).build();
98                 break;
99             case TRANSPORT_CELLULAR:
100                 mScore = new NetworkScore.Builder().setLegacyInt(50).build();
101                 break;
102             case TRANSPORT_WIFI_AWARE:
103                 mScore = new NetworkScore.Builder().setLegacyInt(20).build();
104                 break;
105             case TRANSPORT_VPN:
106                 mNetworkCapabilities.removeCapability(NET_CAPABILITY_NOT_VPN);
107                 // VPNs deduce the SUSPENDED capability from their underlying networks and there
108                 // is no public API to let VPN services set it.
109                 mNetworkCapabilities.removeCapability(NET_CAPABILITY_NOT_SUSPENDED);
110                 mScore = new NetworkScore.Builder().setLegacyInt(101).build();
111                 break;
112             default:
113                 throw new UnsupportedOperationException("unimplemented network type");
114         }
115         mContext = context;
116         mLogTag = "Mock-" + typeName;
117         mHandlerThread = new HandlerThread(mLogTag);
118         mHandlerThread.start();
119 
120         // extraInfo is set to "" by default in NetworkAgentConfig.
121         final String extraInfo = (transport == TRANSPORT_CELLULAR) ? "internet.apn" : "";
122         mNetworkAgentConfig = new NetworkAgentConfig.Builder()
123                 .setLegacyType(type)
124                 .setLegacyTypeName(typeName)
125                 .setLegacyExtraInfo(extraInfo)
126                 .build();
127         mNetworkAgent = makeNetworkAgent(linkProperties, mNetworkAgentConfig);
128     }
129 
makeNetworkAgent(LinkProperties linkProperties, final NetworkAgentConfig nac)130     protected InstrumentedNetworkAgent makeNetworkAgent(LinkProperties linkProperties,
131             final NetworkAgentConfig nac) throws Exception {
132         return new InstrumentedNetworkAgent(this, linkProperties, nac);
133     }
134 
135     public static class InstrumentedNetworkAgent extends NetworkAgent {
136         private final NetworkAgentWrapper mWrapper;
137         private static final String PROVIDER_NAME = "InstrumentedNetworkAgentProvider";
138 
InstrumentedNetworkAgent(NetworkAgentWrapper wrapper, LinkProperties lp, NetworkAgentConfig nac)139         public InstrumentedNetworkAgent(NetworkAgentWrapper wrapper, LinkProperties lp,
140                 NetworkAgentConfig nac) {
141             super(wrapper.mContext, wrapper.mHandlerThread.getLooper(), wrapper.mLogTag,
142                     wrapper.mNetworkCapabilities, lp, wrapper.mScore, nac,
143                     new NetworkProvider(wrapper.mContext, wrapper.mHandlerThread.getLooper(),
144                             PROVIDER_NAME));
145             mWrapper = wrapper;
146             register();
147         }
148 
149         @Override
unwanted()150         public void unwanted() {
151             mWrapper.mDisconnected.open();
152         }
153 
154         @Override
startSocketKeepalive(Message msg)155         public void startSocketKeepalive(Message msg) {
156             int slot = msg.arg1;
157             if (mWrapper.mExpectedKeepaliveSlot != null) {
158                 assertEquals((int) mWrapper.mExpectedKeepaliveSlot, slot);
159             }
160             mWrapper.mHandlerThread.getThreadHandler().postDelayed(
161                     () -> onSocketKeepaliveEvent(slot, mWrapper.mStartKeepaliveError),
162                     mWrapper.mKeepaliveResponseDelay);
163         }
164 
165         @Override
stopSocketKeepalive(Message msg)166         public void stopSocketKeepalive(Message msg) {
167             final int slot = msg.arg1;
168             mWrapper.mHandlerThread.getThreadHandler().postDelayed(
169                     () -> onSocketKeepaliveEvent(slot, mWrapper.mStopKeepaliveError),
170                     mWrapper.mKeepaliveResponseDelay);
171         }
172 
173         @Override
onQosCallbackRegistered(final int qosCallbackId, final @NonNull QosFilter filter)174         public void onQosCallbackRegistered(final int qosCallbackId,
175                 final @NonNull QosFilter filter) {
176             Log.i(mWrapper.mLogTag, "onQosCallbackRegistered");
177             mWrapper.mCallbackHistory.add(
178                     new CallbackType.OnQosCallbackRegister(qosCallbackId, filter));
179         }
180 
181         @Override
onQosCallbackUnregistered(final int qosCallbackId)182         public void onQosCallbackUnregistered(final int qosCallbackId) {
183             Log.i(mWrapper.mLogTag, "onQosCallbackUnregistered");
184             mWrapper.mCallbackHistory.add(new CallbackType.OnQosCallbackUnregister(qosCallbackId));
185         }
186 
187         @Override
preventAutomaticReconnect()188         protected void preventAutomaticReconnect() {
189             mWrapper.mPreventReconnectReceived.open();
190         }
191 
192         @Override
addKeepalivePacketFilter(Message msg)193         protected void addKeepalivePacketFilter(Message msg) {
194             Log.i(mWrapper.mLogTag, "Add keepalive packet filter.");
195         }
196 
197         @Override
removeKeepalivePacketFilter(Message msg)198         protected void removeKeepalivePacketFilter(Message msg) {
199             Log.i(mWrapper.mLogTag, "Remove keepalive packet filter.");
200         }
201     }
202 
setScore(@onNull final NetworkScore score)203     public void setScore(@NonNull final NetworkScore score) {
204         mScore = score;
205         mNetworkAgent.sendNetworkScore(score);
206     }
207 
adjustScore(int change)208     public void adjustScore(int change) {
209         final int newLegacyScore = mScore.getLegacyInt() + change;
210         final NetworkScore.Builder builder = new NetworkScore.Builder()
211                 .setLegacyInt(newLegacyScore);
212         if (mNetworkCapabilities.hasTransport(TRANSPORT_WIFI) && newLegacyScore < 50) {
213             builder.setExiting(true);
214         }
215         mScore = builder.build();
216         mNetworkAgent.sendNetworkScore(mScore);
217     }
218 
getScore()219     public NetworkScore getScore() {
220         return mScore;
221     }
222 
explicitlySelected(boolean explicitlySelected, boolean acceptUnvalidated)223     public void explicitlySelected(boolean explicitlySelected, boolean acceptUnvalidated) {
224         mNetworkAgent.explicitlySelected(explicitlySelected, acceptUnvalidated);
225     }
226 
addCapability(int capability)227     public void addCapability(int capability) {
228         mNetworkCapabilities.addCapability(capability);
229         mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
230     }
231 
removeCapability(int capability)232     public void removeCapability(int capability) {
233         mNetworkCapabilities.removeCapability(capability);
234         mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
235     }
236 
setUids(Set<Range<Integer>> uids)237     public void setUids(Set<Range<Integer>> uids) {
238         mNetworkCapabilities.setUids(uids);
239         mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
240     }
241 
setSignalStrength(int signalStrength)242     public void setSignalStrength(int signalStrength) {
243         mNetworkCapabilities.setSignalStrength(signalStrength);
244         mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
245     }
246 
setNetworkSpecifier(NetworkSpecifier networkSpecifier)247     public void setNetworkSpecifier(NetworkSpecifier networkSpecifier) {
248         mNetworkCapabilities.setNetworkSpecifier(networkSpecifier);
249         mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
250     }
251 
setNetworkCapabilities(NetworkCapabilities nc, boolean sendToConnectivityService)252     public void setNetworkCapabilities(NetworkCapabilities nc, boolean sendToConnectivityService) {
253         mNetworkCapabilities.set(nc);
254         if (sendToConnectivityService) {
255             mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
256         }
257     }
258 
setUnderlyingNetworks(List<Network> underlyingNetworks)259     public void setUnderlyingNetworks(List<Network> underlyingNetworks) {
260         mNetworkAgent.setUnderlyingNetworks(underlyingNetworks);
261     }
262 
setOwnerUid(int uid)263     public void setOwnerUid(int uid) {
264         mNetworkCapabilities.setOwnerUid(uid);
265         mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
266     }
267 
connect()268     public void connect() {
269         if (!mConnected.compareAndSet(false /* expect */, true /* update */)) {
270             // compareAndSet returns false when the value couldn't be updated because it did not
271             // match the expected value.
272             fail("Test NetworkAgents can only be connected once");
273         }
274         mNetworkAgent.markConnected();
275     }
276 
suspend()277     public void suspend() {
278         removeCapability(NET_CAPABILITY_NOT_SUSPENDED);
279     }
280 
resume()281     public void resume() {
282         addCapability(NET_CAPABILITY_NOT_SUSPENDED);
283     }
284 
disconnect()285     public void disconnect() {
286         mNetworkAgent.unregister();
287     }
288 
289     @Override
getNetwork()290     public Network getNetwork() {
291         return mNetworkAgent.getNetwork();
292     }
293 
expectPreventReconnectReceived(long timeoutMs)294     public void expectPreventReconnectReceived(long timeoutMs) {
295         assertTrue(mPreventReconnectReceived.block(timeoutMs));
296     }
297 
expectDisconnected(long timeoutMs)298     public void expectDisconnected(long timeoutMs) {
299         assertTrue(mDisconnected.block(timeoutMs));
300     }
301 
sendLinkProperties(LinkProperties lp)302     public void sendLinkProperties(LinkProperties lp) {
303         mNetworkAgent.sendLinkProperties(lp);
304     }
305 
setStartKeepaliveEvent(int reason)306     public void setStartKeepaliveEvent(int reason) {
307         mStartKeepaliveError = reason;
308     }
309 
setStopKeepaliveEvent(int reason)310     public void setStopKeepaliveEvent(int reason) {
311         mStopKeepaliveError = reason;
312     }
313 
setKeepaliveResponseDelay(long delay)314     public void setKeepaliveResponseDelay(long delay) {
315         mKeepaliveResponseDelay = delay;
316     }
317 
setExpectedKeepaliveSlot(Integer slot)318     public void setExpectedKeepaliveSlot(Integer slot) {
319         mExpectedKeepaliveSlot = slot;
320     }
321 
getNetworkAgent()322     public NetworkAgent getNetworkAgent() {
323         return mNetworkAgent;
324     }
325 
getNetworkCapabilities()326     public NetworkCapabilities getNetworkCapabilities() {
327         return mNetworkCapabilities;
328     }
329 
getLegacyType()330     public int getLegacyType() {
331         return mNetworkAgentConfig.getLegacyType();
332     }
333 
getExtraInfo()334     public String getExtraInfo() {
335         return mNetworkAgentConfig.getLegacyExtraInfo();
336     }
337 
getCallbackHistory()338     public @NonNull ArrayTrackRecord<CallbackType>.ReadHead getCallbackHistory() {
339         return mCallbackHistory;
340     }
341 
waitForIdle(long timeoutMs)342     public void waitForIdle(long timeoutMs) {
343         HandlerUtils.waitForIdle(mHandlerThread, timeoutMs);
344     }
345 
346     abstract static class CallbackType {
347         final int mQosCallbackId;
348 
CallbackType(final int qosCallbackId)349         protected CallbackType(final int qosCallbackId) {
350             mQosCallbackId = qosCallbackId;
351         }
352 
353         static class OnQosCallbackRegister extends CallbackType {
354             final QosFilter mFilter;
OnQosCallbackRegister(final int qosCallbackId, final QosFilter filter)355             OnQosCallbackRegister(final int qosCallbackId, final QosFilter filter) {
356                 super(qosCallbackId);
357                 mFilter = filter;
358             }
359 
360             @Override
equals(final Object o)361             public boolean equals(final Object o) {
362                 if (this == o) return true;
363                 if (o == null || getClass() != o.getClass()) return false;
364                 final OnQosCallbackRegister that = (OnQosCallbackRegister) o;
365                 return mQosCallbackId == that.mQosCallbackId
366                         && Objects.equals(mFilter, that.mFilter);
367             }
368 
369             @Override
hashCode()370             public int hashCode() {
371                 return Objects.hash(mQosCallbackId, mFilter);
372             }
373         }
374 
375         static class OnQosCallbackUnregister extends CallbackType {
OnQosCallbackUnregister(final int qosCallbackId)376             OnQosCallbackUnregister(final int qosCallbackId) {
377                 super(qosCallbackId);
378             }
379 
380             @Override
equals(final Object o)381             public boolean equals(final Object o) {
382                 if (this == o) return true;
383                 if (o == null || getClass() != o.getClass()) return false;
384                 final OnQosCallbackUnregister that = (OnQosCallbackUnregister) o;
385                 return mQosCallbackId == that.mQosCallbackId;
386             }
387 
388             @Override
hashCode()389             public int hashCode() {
390                 return Objects.hash(mQosCallbackId);
391             }
392         }
393     }
394 
isBypassableVpn()395     public boolean isBypassableVpn() {
396         return mNetworkAgentConfig.isBypassableVpn();
397     }
398 }
399