• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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 #include "chpp/clients/discovery.h"
18 
19 #include <inttypes.h>
20 #include <stddef.h>
21 #include <stdint.h>
22 #include <string.h>
23 
24 #include "chpp/app.h"
25 #include "chpp/common/discovery.h"
26 #include "chpp/log.h"
27 #include "chpp/macros.h"
28 #include "chpp/memory.h"
29 #include "chpp/transport.h"
30 
31 /************************************************
32  *  Prototypes
33  ***********************************************/
34 
35 static inline bool chppIsClientCompatibleWithService(
36     const struct ChppClientDescriptor *client,
37     const struct ChppServiceDescriptor *service);
38 static uint8_t chppFindMatchingClientIndex(
39     struct ChppAppState *appState, const struct ChppServiceDescriptor *service);
40 static void chppProcessDiscoverAllResponse(
41     struct ChppAppState *appState, const struct ChppDiscoveryResponse *response,
42     size_t responseLen);
43 static ChppNotifierFunction *chppGetClientMatchNotifierFunction(
44     struct ChppAppState *appState, uint8_t index);
45 
46 /************************************************
47  *  Private Functions
48  ***********************************************/
49 
50 /**
51  * Determines if a client is compatible with a service.
52  *
53  * Compatibility requirements are:
54  * 1. UUIDs must match
55  * 2. Major version numbers must match
56  *
57  * @param client ChppClientDescriptor of client.
58  * @param service ChppServiceDescriptor of service.
59  *
60  * @param return True if compatible.
61  */
chppIsClientCompatibleWithService(const struct ChppClientDescriptor * client,const struct ChppServiceDescriptor * service)62 static inline bool chppIsClientCompatibleWithService(
63     const struct ChppClientDescriptor *client,
64     const struct ChppServiceDescriptor *service) {
65   return memcmp(client->uuid, service->uuid, CHPP_SERVICE_UUID_LEN) == 0 &&
66          client->version.major == service->version.major;
67 }
68 
69 /**
70  * Matches a registered client to a (discovered) service.
71  *
72  * @param appState Application layer state.
73  * @param service ChppServiceDescriptor of service.
74  *
75  * @param return Index of client matching the service, or CHPP_CLIENT_INDEX_NONE
76  * if there is none.
77  */
chppFindMatchingClientIndex(struct ChppAppState * appState,const struct ChppServiceDescriptor * service)78 static uint8_t chppFindMatchingClientIndex(
79     struct ChppAppState *appState,
80     const struct ChppServiceDescriptor *service) {
81   uint8_t result = CHPP_CLIENT_INDEX_NONE;
82 
83   const struct ChppClient **clients = appState->registeredClients;
84 
85   for (uint8_t i = 0; i < appState->registeredClientCount; i++) {
86     if (chppIsClientCompatibleWithService(&clients[i]->descriptor, service)) {
87       result = i;
88       break;
89     }
90   }
91 
92   return result;
93 }
94 
95 /**
96  * Processes the Discover All Services response
97  * (CHPP_DISCOVERY_COMMAND_DISCOVER_ALL).
98  *
99  * @param appState Application layer state.
100  * @param response The response from the discovery service.
101  * @param responseLen Length of the in bytes.
102  */
chppProcessDiscoverAllResponse(struct ChppAppState * appState,const struct ChppDiscoveryResponse * response,size_t responseLen)103 static void chppProcessDiscoverAllResponse(
104     struct ChppAppState *appState, const struct ChppDiscoveryResponse *response,
105     size_t responseLen) {
106   if (appState->isDiscoveryComplete) {
107     CHPP_LOGE("Dupe discovery resp");
108     return;
109   }
110 
111   size_t servicesLen = responseLen - sizeof(struct ChppAppHeader);
112   uint8_t serviceCount =
113       (uint8_t)(servicesLen / sizeof(struct ChppServiceDescriptor));
114 
115   CHPP_DEBUG_ASSERT_LOG(
116       servicesLen == serviceCount * sizeof(struct ChppServiceDescriptor),
117       "Discovery desc len=%" PRIuSIZE " != count=%" PRIu8 " * size=%" PRIuSIZE,
118       servicesLen, serviceCount, sizeof(struct ChppServiceDescriptor));
119 
120   CHPP_DEBUG_ASSERT_LOG(serviceCount <= CHPP_MAX_DISCOVERED_SERVICES,
121                         "Service count=%" PRIu8 " > max=%d", serviceCount,
122                         CHPP_MAX_DISCOVERED_SERVICES);
123 
124   CHPP_LOGI("Discovered %" PRIu8 " services", serviceCount);
125 
126   uint8_t matchedClients = 0;
127   for (uint8_t i = 0; i < MIN(serviceCount, CHPP_MAX_DISCOVERED_SERVICES);
128        i++) {
129     const struct ChppServiceDescriptor *service = &response->services[i];
130 
131     // Update lookup table
132     uint8_t clientIndex = chppFindMatchingClientIndex(appState, service);
133     appState->clientIndexOfServiceIndex[i] = clientIndex;
134 
135     char uuidText[CHPP_SERVICE_UUID_STRING_LEN];
136     chppUuidToStr(service->uuid, uuidText);
137 
138     if (clientIndex == CHPP_CLIENT_INDEX_NONE) {
139       CHPP_LOGE(
140           "No client for service #%d"
141           " name=%s, UUID=%s, v=%" PRIu8 ".%" PRIu8 ".%" PRIu16,
142           CHPP_SERVICE_HANDLE_OF_INDEX(i), service->name, uuidText,
143           service->version.major, service->version.minor,
144           service->version.patch);
145       continue;
146     }
147 
148     const struct ChppClient *client = appState->registeredClients[clientIndex];
149 
150     CHPP_LOGD("Client # %" PRIu8
151               " matched to service on handle %d"
152               " with name=%s, UUID=%s. "
153               "client version=%" PRIu8 ".%" PRIu8 ".%" PRIu16
154               ", service version=%" PRIu8 ".%" PRIu8 ".%" PRIu16,
155               clientIndex, CHPP_SERVICE_HANDLE_OF_INDEX(i), service->name,
156               uuidText, client->descriptor.version.major,
157               client->descriptor.version.minor,
158               client->descriptor.version.patch, service->version.major,
159               service->version.minor, service->version.patch);
160 
161     // Initialize client
162     if (!client->initFunctionPtr(
163             appState->registeredClientContexts[clientIndex],
164             CHPP_SERVICE_HANDLE_OF_INDEX(i), service->version)) {
165       CHPP_LOGE("Client v=%" PRIu8 ".%" PRIu8 ".%" PRIu16
166                 " rejected init. Service v=%" PRIu8 ".%" PRIu8 ".%" PRIu16,
167                 client->descriptor.version.major,
168                 client->descriptor.version.minor,
169                 client->descriptor.version.patch, service->version.major,
170                 service->version.minor, service->version.patch);
171       continue;
172     }
173 
174     matchedClients++;
175   }
176 
177   CHPP_LOGD("Matched %" PRIu8 " out of %" PRIu8 " clients and %" PRIu8
178             " services",
179             matchedClients, appState->registeredClientCount, serviceCount);
180 
181   // Notify any clients waiting on discovery completion
182   chppMutexLock(&appState->discoveryMutex);
183   appState->isDiscoveryComplete = true;
184   appState->matchedClientCount = matchedClients;
185   appState->discoveredServiceCount = serviceCount;
186   chppConditionVariableSignal(&appState->discoveryCv);
187   chppMutexUnlock(&appState->discoveryMutex);
188 
189   // Notify clients of match
190   for (uint8_t i = 0; i < appState->discoveredServiceCount; i++) {
191     uint8_t clientIndex = appState->clientIndexOfServiceIndex[i];
192     if (clientIndex != CHPP_CLIENT_INDEX_NONE) {
193       // Discovered service has a matched client
194       ChppNotifierFunction *matchNotifierFunction =
195           chppGetClientMatchNotifierFunction(appState, clientIndex);
196 
197       CHPP_LOGD("Client #%" PRIu8 " (H#%d) match notifier found=%d",
198                 clientIndex, CHPP_SERVICE_HANDLE_OF_INDEX(i),
199                 (matchNotifierFunction != NULL));
200 
201       if (matchNotifierFunction != NULL) {
202         matchNotifierFunction(appState->registeredClientContexts[clientIndex]);
203       }
204     }
205   }
206 }
207 
208 /**
209  * Returns the match notification function pointer of a particular negotiated
210  * client. The function pointer will be set to null by clients that do not need
211  * or support a match notification.
212  *
213  * @param appState Application layer state.
214  * @param index Index of the registered client.
215  *
216  * @return Pointer to the match notification function.
217  */
chppGetClientMatchNotifierFunction(struct ChppAppState * appState,uint8_t index)218 static ChppNotifierFunction *chppGetClientMatchNotifierFunction(
219     struct ChppAppState *appState, uint8_t index) {
220   return appState->registeredClients[index]->matchNotifierFunctionPtr;
221 }
222 
223 /************************************************
224  *  Public Functions
225  ***********************************************/
226 
chppDiscoveryInit(struct ChppAppState * appState)227 void chppDiscoveryInit(struct ChppAppState *appState) {
228   CHPP_ASSERT_LOG(!appState->isDiscoveryClientInitialized,
229                   "Discovery client already initialized");
230 
231   CHPP_LOGD("Initializing CHPP discovery client");
232 
233   if (!appState->isDiscoveryClientInitialized) {
234     chppMutexInit(&appState->discoveryMutex);
235     chppConditionVariableInit(&appState->discoveryCv);
236     appState->isDiscoveryClientInitialized = true;
237   }
238 
239   appState->matchedClientCount = 0;
240   appState->isDiscoveryComplete = false;
241   appState->isDiscoveryClientInitialized = true;
242 }
243 
chppDiscoveryDeinit(struct ChppAppState * appState)244 void chppDiscoveryDeinit(struct ChppAppState *appState) {
245   CHPP_ASSERT_LOG(appState->isDiscoveryClientInitialized,
246                   "Discovery client already deinitialized");
247 
248   CHPP_LOGD("Deinitializing CHPP discovery client");
249   appState->isDiscoveryClientInitialized = false;
250 }
251 
chppWaitForDiscoveryComplete(struct ChppAppState * appState,uint64_t timeoutMs)252 bool chppWaitForDiscoveryComplete(struct ChppAppState *appState,
253                                   uint64_t timeoutMs) {
254   bool success = false;
255 
256   if (!appState->isDiscoveryClientInitialized) {
257     timeoutMs = 0;
258   } else {
259     success = true;
260 
261     chppMutexLock(&appState->discoveryMutex);
262     if (timeoutMs == 0) {
263       success = appState->isDiscoveryComplete;
264     } else {
265       while (success && !appState->isDiscoveryComplete) {
266         success = chppConditionVariableTimedWait(
267             &appState->discoveryCv, &appState->discoveryMutex,
268             timeoutMs * CHPP_NSEC_PER_MSEC);
269       }
270     }
271     chppMutexUnlock(&appState->discoveryMutex);
272   }
273 
274   if (!success) {
275     CHPP_LOGE("Discovery incomplete after %" PRIu64 " ms", timeoutMs);
276   }
277   return success;
278 }
279 
chppDispatchDiscoveryServiceResponse(struct ChppAppState * appState,const uint8_t * buf,size_t len)280 bool chppDispatchDiscoveryServiceResponse(struct ChppAppState *appState,
281                                           const uint8_t *buf, size_t len) {
282   const struct ChppAppHeader *rxHeader = (const struct ChppAppHeader *)buf;
283   bool success = true;
284 
285   switch (rxHeader->command) {
286     case CHPP_DISCOVERY_COMMAND_DISCOVER_ALL: {
287       chppProcessDiscoverAllResponse(
288           appState, (const struct ChppDiscoveryResponse *)buf, len);
289       break;
290     }
291     default: {
292       success = false;
293       break;
294     }
295   }
296   return success;
297 }
298 
chppInitiateDiscovery(struct ChppAppState * appState)299 void chppInitiateDiscovery(struct ChppAppState *appState) {
300   if (appState->isDiscoveryComplete) {
301     CHPP_LOGE("Duplicate discovery init");
302     return;
303   }
304 
305   for (uint8_t i = 0; i < CHPP_MAX_DISCOVERED_SERVICES; i++) {
306     appState->clientIndexOfServiceIndex[i] = CHPP_CLIENT_INDEX_NONE;
307   }
308 
309   struct ChppAppHeader *request = chppMalloc(sizeof(struct ChppAppHeader));
310   request->handle = CHPP_HANDLE_DISCOVERY;
311   request->type = CHPP_MESSAGE_TYPE_CLIENT_REQUEST;
312   request->transaction = 0;
313   request->error = CHPP_APP_ERROR_NONE;
314   request->command = CHPP_DISCOVERY_COMMAND_DISCOVER_ALL;
315 
316   chppEnqueueTxDatagramOrFail(appState->transportContext, request,
317                               sizeof(*request));
318 }
319 
chppAreAllClientsMatched(struct ChppAppState * appState)320 bool chppAreAllClientsMatched(struct ChppAppState *appState) {
321   bool success = false;
322   chppMutexLock(&appState->discoveryMutex);
323   success = (appState->isDiscoveryComplete) &&
324             (appState->registeredClientCount == appState->matchedClientCount);
325   chppMutexUnlock(&appState->discoveryMutex);
326   return success;
327 }
328