• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * 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.net.integrationtests
18 
19 import android.app.Service
20 import android.content.Intent
21 import androidx.annotation.GuardedBy
22 import com.android.testutils.quitExecutorServices
23 import com.android.testutils.quitThreads
24 import java.net.URL
25 import java.util.Collections
26 import java.util.concurrent.ConcurrentHashMap
27 import java.util.concurrent.ConcurrentLinkedQueue
28 import java.util.concurrent.ExecutorService
29 import kotlin.collections.ArrayList
30 import kotlin.test.fail
31 
32 /**
33  * An instrumentation interface for the NetworkStack that allows controlling behavior to
34  * facilitate integration tests.
35  */
36 class NetworkStackInstrumentationService : Service() {
37     override fun onBind(intent: Intent) = InstrumentationConnector.asBinder()
38 
39     object InstrumentationConnector : INetworkStackInstrumentation.Stub() {
40         private val httpResponses = ConcurrentHashMap<String, ConcurrentLinkedQueue<HttpResponse>>()
41                 .run {
42                     withDefault { key -> getOrPut(key) { ConcurrentLinkedQueue() } }
43                 }
44         private val httpRequestUrls = Collections.synchronizedList(mutableListOf<String>())
45 
46         @GuardedBy("networkMonitorThreads")
47         private val networkMonitorThreads = mutableListOf<Thread>()
48         @GuardedBy("networkMonitorExecutorServices")
49         private val networkMonitorExecutorServices = mutableListOf<ExecutorService>()
50 
51         /**
52          * Called when an HTTP request is being processed by NetworkMonitor. Returns the response
53          * that should be simulated.
54          */
55         fun processRequest(url: URL): HttpResponse {
56             val strUrl = url.toString()
57             httpRequestUrls.add(strUrl)
58             return httpResponses[strUrl]?.poll()
59                     ?: fail("No mocked response for request: $strUrl. " +
60                             "Mocked URL keys are: ${httpResponses.keys}")
61         }
62 
63         /**
64          * Called when NetworkMonitor creates a new Thread.
65          */
66         fun onNetworkMonitorThreadCreated(thread: Thread) {
67             synchronized(networkMonitorThreads) {
68                 networkMonitorThreads.add(thread)
69             }
70         }
71 
72         /**
73          * Called when NetworkMonitor creates a new ExecutorService.
74          */
75         fun onNetworkMonitorExecutorServiceCreated(executorService: ExecutorService) {
76             synchronized(networkMonitorExecutorServices) {
77                 networkMonitorExecutorServices.add(executorService)
78             }
79         }
80 
81         /**
82          * Clear all state of this connector. This is intended for use between two tests, so all
83          * state should be reset as if the connector was just created.
84          */
85         override fun clearAllState() {
86             quitThreads(
87                 maxRetryCount = 3,
88                 interrupt = true) {
89                 synchronized(networkMonitorThreads) {
90                     networkMonitorThreads.toList().also { networkMonitorThreads.clear() }
91                 }
92             }
93             quitExecutorServices(
94                 maxRetryCount = 3,
95                 // NetworkMonitor is expected to have interrupted its executors when probing
96                 // finishes, otherwise it's a thread pool leak that should be caught, so they should
97                 // not need to be interrupted (the test only needs to wait for them to finish).
98                 interrupt = false) {
99                 synchronized(networkMonitorExecutorServices) {
100                     networkMonitorExecutorServices.toList().also {
101                         networkMonitorExecutorServices.clear()
102                     }
103                 }
104             }
105             httpResponses.clear()
106             httpRequestUrls.clear()
107         }
108 
109         /**
110          * Add a response to a future HTTP request.
111          *
112          * <p>For any subsequent HTTP/HTTPS query, the first response with a matching URL will be
113          * used to mock the query response.
114          *
115          * <p>All requests that are expected to be sent must have a mock response: if an unexpected
116          * request is seen, the test will fail.
117          */
118         override fun addHttpResponse(response: HttpResponse) {
119             httpResponses.getValue(checkNotNull(response.requestUrl)).add(response)
120         }
121 
122         /**
123          * Get the ordered list of request URLs that have been sent by NetworkMonitor, and were
124          * answered based on mock responses.
125          */
126         override fun getRequestUrls(): List<String> {
127             return ArrayList(httpRequestUrls)
128         }
129     }
130 }
131