1 /*
2  * Copyright (C) 2023 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 #include "chre_host/hal_client.h"
17 #include "chre_host/hal_error.h"
18 
19 #include <unordered_set>
20 
21 #include "gmock/gmock.h"
22 #include "gtest/gtest.h"
23 
24 #include <aidl/android/hardware/contexthub/IContextHub.h>
25 
26 namespace android::chre {
27 
28 namespace {
29 using ::aidl::android::hardware::contexthub::ContextHubMessage;
30 using ::aidl::android::hardware::contexthub::HostEndpointInfo;
31 using ::aidl::android::hardware::contexthub::IContextHub;
32 using ::aidl::android::hardware::contexthub::IContextHubCallbackDefault;
33 using ::aidl::android::hardware::contexthub::IContextHubDefault;
34 
35 using ::ndk::ScopedAStatus;
36 
37 using ::testing::_;
38 using ::testing::ByMove;
39 using ::testing::Field;
40 using ::testing::IsEmpty;
41 using ::testing::Return;
42 using ::testing::UnorderedElementsAre;
43 
44 using HostEndpointId = char16_t;
45 constexpr HostEndpointId kEndpointId = 0x10;
46 
47 class HalClientForTest : public HalClient {
48  public:
HalClientForTest(const std::shared_ptr<IContextHub> & contextHub,const std::vector<HostEndpointId> & connectedEndpoints,const std::shared_ptr<IContextHubCallback> & callback=ndk::SharedRefBase::make<IContextHubCallbackDefault> ())49   HalClientForTest(const std::shared_ptr<IContextHub> &contextHub,
50                    const std::vector<HostEndpointId> &connectedEndpoints,
51                    const std::shared_ptr<IContextHubCallback> &callback =
52                        ndk::SharedRefBase::make<IContextHubCallbackDefault>())
53       : HalClient(callback) {
54     mContextHub = contextHub;
55     for (const HostEndpointId &endpointId : connectedEndpoints) {
56       mConnectedEndpoints[endpointId] = {.hostEndpointId = endpointId};
57     }
58   }
59 
getConnectedEndpointIds()60   std::unordered_set<HostEndpointId> getConnectedEndpointIds() {
61     std::unordered_set<HostEndpointId> result{};
62     for (const auto &[endpointId, unusedEndpointInfo] : mConnectedEndpoints) {
63       result.insert(endpointId);
64     }
65     return result;
66   }
67 
getClientCallback()68   HalClientCallback *getClientCallback() {
69     return mCallback.get();
70   }
71 };
72 
73 class MockContextHub : public IContextHubDefault {
74  public:
75   MOCK_METHOD(ScopedAStatus, onHostEndpointConnected,
76               (const HostEndpointInfo &info), (override));
77   MOCK_METHOD(ScopedAStatus, onHostEndpointDisconnected,
78               (HostEndpointId endpointId), (override));
79   MOCK_METHOD(ScopedAStatus, queryNanoapps, (int32_t icontextHubId),
80               (override));
81   MOCK_METHOD(ScopedAStatus, sendMessageToHub,
82               (int32_t contextHubId, const ContextHubMessage &message),
83               (override));
84 };
85 
86 }  // namespace
87 
TEST(HalClientTest,EndpointConnectionBasic)88 TEST(HalClientTest, EndpointConnectionBasic) {
89   auto mockContextHub = ndk::SharedRefBase::make<MockContextHub>();
90   const HostEndpointInfo kInfo = {
91       .hostEndpointId = kEndpointId,
92       .type = HostEndpointInfo::Type::NATIVE,
93       .packageName = "HalClientTest",
94       .attributionTag{},
95   };
96 
97   auto halClient = std::make_unique<HalClientForTest>(
98       mockContextHub, std::vector<HostEndpointId>{});
99   EXPECT_THAT(halClient->getConnectedEndpointIds(), IsEmpty());
100 
101   EXPECT_CALL(*mockContextHub,
102               onHostEndpointConnected(
103                   Field(&HostEndpointInfo::hostEndpointId, kEndpointId)))
104       .WillOnce(Return(ScopedAStatus::ok()));
105 
106   halClient->connectEndpoint(kInfo);
107   EXPECT_THAT(halClient->getConnectedEndpointIds(),
108               UnorderedElementsAre(kEndpointId));
109 }
110 
TEST(HalClientTest,EndpointConnectionMultipleRequests)111 TEST(HalClientTest, EndpointConnectionMultipleRequests) {
112   auto mockContextHub = ndk::SharedRefBase::make<MockContextHub>();
113   const HostEndpointInfo kInfo = {
114       .hostEndpointId = kEndpointId,
115       .type = HostEndpointInfo::Type::NATIVE,
116       .packageName = "HalClientTest",
117       .attributionTag{},
118   };
119 
120   auto halClient = std::make_unique<HalClientForTest>(
121       mockContextHub, std::vector<HostEndpointId>{});
122   EXPECT_THAT(halClient->getConnectedEndpointIds(), IsEmpty());
123 
124   // multiple requests are tolerated
125   EXPECT_CALL(*mockContextHub,
126               onHostEndpointConnected(
127                   Field(&HostEndpointInfo::hostEndpointId, kEndpointId)))
128       .WillOnce(Return(ScopedAStatus::ok()))
129       .WillOnce(Return(ScopedAStatus::ok()));
130 
131   halClient->connectEndpoint(kInfo);
132   halClient->connectEndpoint(kInfo);
133 
134   EXPECT_THAT(halClient->getConnectedEndpointIds(),
135               UnorderedElementsAre(kEndpointId));
136 }
137 
TEST(HalClientTest,EndpointDisconnectionBasic)138 TEST(HalClientTest, EndpointDisconnectionBasic) {
139   auto mockContextHub = ndk::SharedRefBase::make<MockContextHub>();
140   auto halClient = std::make_unique<HalClientForTest>(
141       mockContextHub, std::vector<HostEndpointId>{kEndpointId});
142 
143   EXPECT_THAT(halClient->getConnectedEndpointIds(),
144               UnorderedElementsAre(kEndpointId));
145 
146   EXPECT_CALL(*mockContextHub, onHostEndpointDisconnected(kEndpointId))
147       .WillOnce(Return(ScopedAStatus::ok()));
148   halClient->disconnectEndpoint(kEndpointId);
149 
150   EXPECT_THAT(halClient->getConnectedEndpointIds(), IsEmpty());
151 }
152 
TEST(HalClientTest,EndpointDisconnectionMultipleRequest)153 TEST(HalClientTest, EndpointDisconnectionMultipleRequest) {
154   auto mockContextHub = ndk::SharedRefBase::make<MockContextHub>();
155   auto halClient = std::make_unique<HalClientForTest>(
156       mockContextHub, std::vector<HostEndpointId>{kEndpointId});
157   EXPECT_THAT(halClient->getConnectedEndpointIds(),
158               UnorderedElementsAre(kEndpointId));
159 
160   EXPECT_CALL(*mockContextHub, onHostEndpointDisconnected(kEndpointId))
161       .WillOnce(Return(ScopedAStatus::ok()))
162       .WillOnce(Return(ScopedAStatus::ok()));
163 
164   halClient->disconnectEndpoint(kEndpointId);
165   halClient->disconnectEndpoint(kEndpointId);
166 
167   EXPECT_THAT(halClient->getConnectedEndpointIds(), IsEmpty());
168 }
169 
TEST(HalClientTest,SendMessageBasic)170 TEST(HalClientTest, SendMessageBasic) {
171   auto mockContextHub = ndk::SharedRefBase::make<MockContextHub>();
172   const ContextHubMessage contextHubMessage = {
173       .nanoappId = 0xbeef,
174       .hostEndPoint = kEndpointId,
175       .messageBody = {},
176       .permissions = {},
177   };
178   auto halClient = std::make_unique<HalClientForTest>(
179       mockContextHub, std::vector<HostEndpointId>{kEndpointId});
180 
181   EXPECT_CALL(*mockContextHub, sendMessageToHub(_, _))
182       .WillOnce(Return(ScopedAStatus::ok()));
183   halClient->sendMessage(contextHubMessage);
184 }
185 
TEST(HalClientTest,QueryNanoapp)186 TEST(HalClientTest, QueryNanoapp) {
187   auto mockContextHub = ndk::SharedRefBase::make<MockContextHub>();
188   auto halClient = std::make_unique<HalClientForTest>(
189       mockContextHub, std::vector<HostEndpointId>{});
190 
191   EXPECT_CALL(*mockContextHub, queryNanoapps(HalClient::kDefaultContextHubId));
192   halClient->queryNanoapps();
193 }
194 
TEST(HalClientTest,HandleChreRestart)195 TEST(HalClientTest, HandleChreRestart) {
196   auto mockContextHub = ndk::SharedRefBase::make<MockContextHub>();
197 
198   auto halClient = std::make_unique<HalClientForTest>(
199       mockContextHub,
200       std::vector<HostEndpointId>{kEndpointId, kEndpointId + 1});
201 
202   EXPECT_CALL(*mockContextHub, onHostEndpointConnected(
203                                    Field(&HostEndpointInfo::hostEndpointId, _)))
204       .WillOnce(Return(ScopedAStatus::ok()))
205       .WillOnce(Return(ScopedAStatus::ok()));
206 
207   halClient->getClientCallback()->handleContextHubAsyncEvent(
208       AsyncEventType::RESTARTED);
209   EXPECT_THAT(halClient->getConnectedEndpointIds(),
210               UnorderedElementsAre(kEndpointId, kEndpointId + 1));
211 }
212 
TEST(HalClientTest,IsConnected)213 TEST(HalClientTest, IsConnected) {
214   auto mockContextHub = ndk::SharedRefBase::make<MockContextHub>();
215 
216   auto halClient = std::make_unique<HalClientForTest>(
217       mockContextHub,
218       std::vector<HostEndpointId>{kEndpointId, kEndpointId + 1});
219 
220   EXPECT_THAT(halClient->isConnected(), true);
221 }
222 }  // namespace android::chre
223