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.testutils
18 
19 import android.net.DnsResolver
20 import android.net.InetAddresses
21 import android.os.Looper
22 import android.os.Handler
23 import com.android.internal.annotations.GuardedBy
24 import java.net.InetAddress
25 import java.util.concurrent.Executor
26 import org.mockito.invocation.InvocationOnMock
27 import org.mockito.Mockito.any
28 import org.mockito.Mockito.anyInt
29 import org.mockito.Mockito.doAnswer
30 
31 const val TYPE_UNSPECIFIED = -1
32 // TODO: Integrate with NetworkMonitorTest.
33 class FakeDns(val mockResolver: DnsResolver) {
34     class DnsEntry(val hostname: String, val type: Int, val addresses: List<InetAddress>) {
matchnull35         fun match(host: String, type: Int) = hostname.equals(host) && type == type
36     }
37 
38     @GuardedBy("answers")
39     val answers = ArrayList<DnsEntry>()
40 
41     fun getAnswer(hostname: String, type: Int): DnsEntry? = synchronized(answers) {
42         return answers.firstOrNull { it.match(hostname, type) }
43     }
44 
<lambda>null45     fun setAnswer(hostname: String, answer: Array<String>, type: Int) = synchronized(answers) {
46         val ans = DnsEntry(hostname, type, generateAnswer(answer))
47         // Replace or remove the existing one.
48         when (val index = answers.indexOfFirst { it.match(hostname, type) }) {
49             -1 -> answers.add(ans)
50             else -> answers[index] = ans
51         }
52     }
53 
generateAnswernull54     private fun generateAnswer(answer: Array<String>) =
55             answer.filterNotNull().map { InetAddresses.parseNumericAddress(it) }
56 
startMockingnull57     fun startMocking() {
58         // Mock DnsResolver.query() w/o type
59         doAnswer {
60             mockAnswer(it, 1, -1, 3, 5)
61         }.`when`(mockResolver).query(any() /* network */, any() /* domain */, anyInt() /* flags */,
62                 any() /* executor */, any() /* cancellationSignal */, any() /*callback*/)
63         // Mock DnsResolver.query() w/ type
64         doAnswer {
65             mockAnswer(it, 1, 2, 4, 6)
66         }.`when`(mockResolver).query(any() /* network */, any() /* domain */, anyInt() /* nsType */,
67                 anyInt() /* flags */, any() /* executor */, any() /* cancellationSignal */,
68         any() /*callback*/)
69     }
70 
mockAnswernull71     private fun mockAnswer(
72         it: InvocationOnMock,
73         posHos: Int,
74         posType: Int,
75         posExecutor: Int,
76         posCallback: Int
77     ) {
78         val hostname = it.arguments[posHos] as String
79         val executor = it.arguments[posExecutor] as Executor
80         val callback = it.arguments[posCallback] as DnsResolver.Callback<List<InetAddress>>
81         var type = if (posType != -1) it.arguments[posType] as Int else TYPE_UNSPECIFIED
82         val answer = getAnswer(hostname, type)
83 
84         if (!answer?.addresses.isNullOrEmpty()) {
85             Handler(Looper.getMainLooper()).post({ executor.execute({
86                     callback.onAnswer(answer?.addresses, 0); }) })
87         }
88     }
89 
90     /** Clears all entries. */
<lambda>null91     fun clearAll() = synchronized(answers) {
92         answers.clear()
93     }
94 }
95