1 /*
2  * Copyright (C) 2022 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.nearby.integration.untrusted
18 
19 import android.content.Context
20 import android.nearby.BroadcastCallback
21 import android.nearby.BroadcastRequest
22 import android.nearby.NearbyDevice
23 import android.nearby.NearbyManager
24 import android.nearby.PresenceBroadcastRequest
25 import android.nearby.PresenceCredential
26 import android.nearby.PrivateCredential
27 import android.nearby.ScanCallback
28 import android.nearby.ScanRequest
29 import androidx.test.core.app.ApplicationProvider
30 import androidx.test.ext.junit.runners.AndroidJUnit4
31 import androidx.test.uiautomator.LogcatWaitMixin
32 import com.google.common.truth.Truth.assertThat
33 import java.time.Duration
34 import java.util.Calendar
35 import org.junit.Assert.assertThrows
36 import org.junit.Before
37 import org.junit.Test
38 import org.junit.runner.RunWith
39 
40 @RunWith(AndroidJUnit4::class)
41 class NearbyManagerTest {
42     private lateinit var appContext: Context
43 
44     @Before
setUpnull45     fun setUp() {
46         appContext = ApplicationProvider.getApplicationContext<Context>()
47     }
48 
49     /** Verify untrusted app can get Nearby service. */
50     @Test
testContextGetNearbyService_fromUnTrustedApp_returnsNotNullnull51     fun testContextGetNearbyService_fromUnTrustedApp_returnsNotNull() {
52         assertThat(appContext.getSystemService(Context.NEARBY_SERVICE)).isNotNull()
53     }
54 
55     /**
56      * Verify untrusted app can't start scan because it needs BLUETOOTH_PRIVILEGED
57      * permission which is not for use by third-party applications.
58      */
59     @Test
testNearbyManagerStartScan_fromUnTrustedApp_throwsExceptionnull60     fun testNearbyManagerStartScan_fromUnTrustedApp_throwsException() {
61         val nearbyManager = appContext.getSystemService(Context.NEARBY_SERVICE) as NearbyManager
62         val scanRequest = ScanRequest.Builder()
63             .setScanMode(ScanRequest.SCAN_MODE_LOW_LATENCY)
64             .setScanType(ScanRequest.SCAN_TYPE_FAST_PAIR)
65             .setBleEnabled(true)
66             .build()
67         val scanCallback = object : ScanCallback {
68             override fun onDiscovered(device: NearbyDevice) {}
69 
70             override fun onUpdated(device: NearbyDevice) {}
71 
72             override fun onLost(device: NearbyDevice) {}
73         }
74 
75         assertThrows(SecurityException::class.java) {
76             nearbyManager.startScan(scanRequest, /* executor */ { it.run() }, scanCallback)
77         }
78     }
79 
80     /** Verify untrusted app can't stop scan because it never successfully registers a callback. */
81     @Test
testNearbyManagerStopScan_fromUnTrustedApp_logsErrornull82     fun testNearbyManagerStopScan_fromUnTrustedApp_logsError() {
83         val nearbyManager = appContext.getSystemService(Context.NEARBY_SERVICE) as NearbyManager
84         val scanCallback = object : ScanCallback {
85             override fun onDiscovered(device: NearbyDevice) {}
86 
87             override fun onUpdated(device: NearbyDevice) {}
88 
89             override fun onLost(device: NearbyDevice) {}
90         }
91         val startTime = Calendar.getInstance().time
92 
93         nearbyManager.stopScan(scanCallback)
94 
95         assertThat(
96             LogcatWaitMixin().waitForSpecificLog(
97                 "Cannot stop scan with this callback because it is never registered.",
98                 startTime,
99                 WAIT_INVALID_OPERATIONS_LOGS_TIMEOUT
100             )
101         ).isTrue()
102     }
103 
104     /**
105      * Verify untrusted app can't start broadcast because it needs BLUETOOTH_PRIVILEGED
106      * permission which is not for use by third-party applications.
107      */
108     @Test
testNearbyManagerStartBroadcast_fromUnTrustedApp_throwsExceptionnull109     fun testNearbyManagerStartBroadcast_fromUnTrustedApp_throwsException() {
110         val nearbyManager = appContext.getSystemService(Context.NEARBY_SERVICE) as NearbyManager
111         val salt = byteArrayOf(1, 2)
112         val secreteId = byteArrayOf(1, 2, 3, 4)
113         val metadataEncryptionKey = ByteArray(14)
114         val authenticityKey = byteArrayOf(0, 1, 1, 1)
115         val deviceName = "test_device"
116         val mediums = listOf(BroadcastRequest.MEDIUM_BLE)
117         val credential =
118             PrivateCredential.Builder(secreteId, authenticityKey, metadataEncryptionKey, deviceName)
119                 .setIdentityType(PresenceCredential.IDENTITY_TYPE_PRIVATE)
120                 .build()
121         val broadcastRequest: BroadcastRequest =
122             PresenceBroadcastRequest.Builder(mediums, salt, credential)
123                 .addAction(123)
124                 .build()
125         val broadcastCallback = BroadcastCallback { }
126 
127         assertThrows(SecurityException::class.java) {
128             nearbyManager.startBroadcast(
129                 broadcastRequest, /* executor */ { it.run() }, broadcastCallback
130             )
131         }
132     }
133 
134     /**
135      * Verify untrusted app can't stop broadcast because it never successfully registers a callback.
136      */
137     @Test
testNearbyManagerStopBroadcast_fromUnTrustedApp_logsErrornull138     fun testNearbyManagerStopBroadcast_fromUnTrustedApp_logsError() {
139         val nearbyManager = appContext.getSystemService(Context.NEARBY_SERVICE) as NearbyManager
140         val broadcastCallback = BroadcastCallback { }
141         val startTime = Calendar.getInstance().time
142 
143         nearbyManager.stopBroadcast(broadcastCallback)
144 
145         assertThat(
146             LogcatWaitMixin().waitForSpecificLog(
147                 "Cannot stop broadcast with this callback because it is never registered.",
148                 startTime,
149                 WAIT_INVALID_OPERATIONS_LOGS_TIMEOUT
150             )
151         ).isTrue()
152     }
153 
154     /**
155      * Verify untrusted app can't set powered off finding ephemeral IDs because it needs
156      * BLUETOOTH_PRIVILEGED permission which is not for use by third-party applications.
157      */
158     @Test
testNearbyManagerSetPoweredOffFindingEphemeralIds_fromUnTrustedApp_throwsExceptionnull159     fun testNearbyManagerSetPoweredOffFindingEphemeralIds_fromUnTrustedApp_throwsException() {
160         val nearbyManager = appContext.getSystemService(Context.NEARBY_SERVICE) as NearbyManager
161         val eid = ByteArray(20)
162 
163         assertThrows(SecurityException::class.java) {
164             nearbyManager.setPoweredOffFindingEphemeralIds(listOf(eid))
165         }
166     }
167 
168     /**
169      * Verify untrusted app can't set powered off finding mode because it needs BLUETOOTH_PRIVILEGED
170      * permission which is not for use by third-party applications.
171      */
172     @Test
testNearbyManagerSetPoweredOffFindingMode_fromUnTrustedApp_throwsExceptionnull173     fun testNearbyManagerSetPoweredOffFindingMode_fromUnTrustedApp_throwsException() {
174         val nearbyManager = appContext.getSystemService(Context.NEARBY_SERVICE) as NearbyManager
175 
176         assertThrows(SecurityException::class.java) {
177             nearbyManager.setPoweredOffFindingMode(NearbyManager.POWERED_OFF_FINDING_MODE_ENABLED)
178         }
179     }
180 
181     /**
182      * Verify untrusted app can't get powered off finding mode because it needs BLUETOOTH_PRIVILEGED
183      * permission which is not for use by third-party applications.
184      */
185     @Test
testNearbyManagerGetPoweredOffFindingMode_fromUnTrustedApp_throwsExceptionnull186     fun testNearbyManagerGetPoweredOffFindingMode_fromUnTrustedApp_throwsException() {
187         val nearbyManager = appContext.getSystemService(Context.NEARBY_SERVICE) as NearbyManager
188 
189         assertThrows(SecurityException::class.java) {
190             nearbyManager.getPoweredOffFindingMode()
191         }
192     }
193 
194     companion object {
195         private val WAIT_INVALID_OPERATIONS_LOGS_TIMEOUT = Duration.ofSeconds(5)
196     }
197 }
198