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/wwan.h"
18
19 #include <inttypes.h>
20 #include <stdbool.h>
21 #include <stddef.h>
22 #include <stdint.h>
23 #include <string.h>
24
25 #include "chpp/app.h"
26 #include "chpp/clients/discovery.h"
27 #ifdef CHPP_CLIENT_ENABLED_TIMESYNC
28 #include "chpp/clients/timesync.h"
29 #endif
30 #include "chpp/common/standard_uuids.h"
31 #include "chpp/common/wwan.h"
32 #include "chpp/common/wwan_types.h"
33 #include "chpp/log.h"
34 #include "chpp/macros.h"
35 #include "chpp/memory.h"
36 #include "chre/pal/wwan.h"
37
38 #ifndef CHPP_WWAN_DISCOVERY_TIMEOUT_MS
39 #define CHPP_WWAN_DISCOVERY_TIMEOUT_MS CHPP_DISCOVERY_DEFAULT_TIMEOUT_MS
40 #endif
41
42 #ifndef CHPP_WWAN_MAX_TIMESYNC_AGE_NS
43 #define CHPP_WWAN_MAX_TIMESYNC_AGE_NS CHPP_TIMESYNC_DEFAULT_MAX_AGE_NS
44 #endif
45
46 /************************************************
47 * Prototypes
48 ***********************************************/
49
50 static enum ChppAppErrorCode chppDispatchWwanResponse(void *clientContext,
51 uint8_t *buf, size_t len);
52 static bool chppWwanClientInit(void *clientContext, uint8_t handle,
53 struct ChppVersion serviceVersion);
54 static void chppWwanClientDeinit(void *clientContext);
55 static void chppWwanClientNotifyReset(void *clientContext);
56 static void chppWwanClientNotifyMatch(void *clientContext);
57
58 /************************************************
59 * Private Definitions
60 ***********************************************/
61
62 /**
63 * Structure to maintain state for the WWAN client and its Request/Response
64 * (RR) functionality.
65 */
66 struct ChppWwanClientState {
67 struct ChppEndpointState client; // CHPP client state
68 const struct chrePalWwanApi *api; // WWAN PAL API
69
70 struct ChppOutgoingRequestState
71 outReqStates[CHPP_WWAN_CLIENT_REQUEST_MAX + 1];
72
73 uint32_t capabilities; // Cached GetCapabilities result
74 bool capabilitiesValid; // Flag to indicate if the capabilities result
75 // is valid
76 };
77
78 // Note: This global definition of gWwanClientContext supports only one
79 // instance of the CHPP WWAN client at a time.
80 struct ChppWwanClientState gWwanClientContext;
81 static const struct chrePalSystemApi *gSystemApi;
82 static const struct chrePalWwanCallbacks *gCallbacks;
83
84 /**
85 * Configuration parameters for this client
86 */
87 static const struct ChppClient kWwanClientConfig = {
88 .descriptor.uuid = CHPP_UUID_WWAN_STANDARD,
89
90 // Version
91 .descriptor.version.major = 1,
92 .descriptor.version.minor = 0,
93 .descriptor.version.patch = 0,
94
95 // Notifies client if CHPP is reset
96 .resetNotifierFunctionPtr = &chppWwanClientNotifyReset,
97
98 // Notifies client if they are matched to a service
99 .matchNotifierFunctionPtr = &chppWwanClientNotifyMatch,
100
101 // Service response dispatch function pointer
102 .responseDispatchFunctionPtr = &chppDispatchWwanResponse,
103
104 // Service notification dispatch function pointer
105 .notificationDispatchFunctionPtr = NULL, // Not supported
106
107 // Service response dispatch function pointer
108 .initFunctionPtr = &chppWwanClientInit,
109
110 // Service notification dispatch function pointer
111 .deinitFunctionPtr = &chppWwanClientDeinit,
112
113 // Number of request-response states in the outReqStates array.
114 .outReqCount = ARRAY_SIZE(gWwanClientContext.outReqStates),
115
116 // Min length is the entire header
117 .minLength = sizeof(struct ChppAppHeader),
118 };
119
120 /************************************************
121 * Prototypes
122 ***********************************************/
123
124 static bool chppWwanClientOpen(const struct chrePalSystemApi *systemApi,
125 const struct chrePalWwanCallbacks *callbacks);
126 static void chppWwanClientClose(void);
127 static uint32_t chppWwanClientGetCapabilities(void);
128 static bool chppWwanClientGetCellInfoAsync(void);
129 static void chppWwanClientReleaseCellInfoResult(
130 struct chreWwanCellInfoResult *result);
131
132 static void chppWwanCloseResult(struct ChppWwanClientState *clientContext,
133 uint8_t *buf, size_t len);
134 static void chppWwanGetCapabilitiesResult(
135 struct ChppWwanClientState *clientContext, uint8_t *buf, size_t len);
136 static void chppWwanGetCellInfoAsyncResult(
137 struct ChppWwanClientState *clientContext, uint8_t *buf, size_t len);
138
139 /************************************************
140 * Private Functions
141 ***********************************************/
142
143 /**
144 * Dispatches a service response from the transport layer that is determined to
145 * be for the WWAN client.
146 *
147 * This function is called from the app layer using its function pointer given
148 * during client registration.
149 *
150 * @param clientContext Maintains status for each client instance.
151 * @param buf Input data. Cannot be null.
152 * @param len Length of input data in bytes.
153 *
154 * @return Indicates the result of this function call.
155 */
chppDispatchWwanResponse(void * clientContext,uint8_t * buf,size_t len)156 static enum ChppAppErrorCode chppDispatchWwanResponse(void *clientContext,
157 uint8_t *buf,
158 size_t len) {
159 struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf;
160 struct ChppWwanClientState *wwanClientContext =
161 (struct ChppWwanClientState *)clientContext;
162 enum ChppAppErrorCode error = CHPP_APP_ERROR_NONE;
163
164 if (rxHeader->command > CHPP_WWAN_CLIENT_REQUEST_MAX) {
165 error = CHPP_APP_ERROR_INVALID_COMMAND;
166
167 } else if (!chppTimestampIncomingResponse(
168 wwanClientContext->client.appContext,
169 &wwanClientContext->outReqStates[rxHeader->command],
170 rxHeader)) {
171 error = CHPP_APP_ERROR_UNEXPECTED_RESPONSE;
172
173 } else {
174 switch (rxHeader->command) {
175 case CHPP_WWAN_OPEN: {
176 chppClientProcessOpenResponse(&wwanClientContext->client, buf, len);
177 break;
178 }
179
180 case CHPP_WWAN_CLOSE: {
181 chppWwanCloseResult(wwanClientContext, buf, len);
182 break;
183 }
184
185 case CHPP_WWAN_GET_CAPABILITIES: {
186 chppWwanGetCapabilitiesResult(wwanClientContext, buf, len);
187 break;
188 }
189
190 case CHPP_WWAN_GET_CELLINFO_ASYNC: {
191 chppWwanGetCellInfoAsyncResult(wwanClientContext, buf, len);
192 break;
193 }
194
195 default: {
196 error = CHPP_APP_ERROR_INVALID_COMMAND;
197 break;
198 }
199 }
200 }
201
202 return error;
203 }
204
205 /**
206 * Initializes the client and provides its handle number and the version of the
207 * matched service when/if it the client is matched with a service during
208 * discovery.
209 *
210 * @param clientContext Maintains status for each client instance.
211 * @param handle Handle number for this client.
212 * @param serviceVersion Version of the matched service.
213 *
214 * @return True if client is compatible and successfully initialized.
215 */
chppWwanClientInit(void * clientContext,uint8_t handle,struct ChppVersion serviceVersion)216 static bool chppWwanClientInit(void *clientContext, uint8_t handle,
217 struct ChppVersion serviceVersion) {
218 UNUSED_VAR(serviceVersion);
219
220 struct ChppWwanClientState *wwanClientContext =
221 (struct ChppWwanClientState *)clientContext;
222 chppClientInit(&wwanClientContext->client, handle);
223
224 return true;
225 }
226
227 /**
228 * Deinitializes the client.
229 *
230 * @param clientContext Maintains status for each client instance.
231 */
chppWwanClientDeinit(void * clientContext)232 static void chppWwanClientDeinit(void *clientContext) {
233 struct ChppWwanClientState *wwanClientContext =
234 (struct ChppWwanClientState *)clientContext;
235 chppClientDeinit(&wwanClientContext->client);
236 }
237
238 /**
239 * Notifies the client of an incoming reset.
240 *
241 * @param clientContext Maintains status for each client instance.
242 */
chppWwanClientNotifyReset(void * clientContext)243 static void chppWwanClientNotifyReset(void *clientContext) {
244 struct ChppWwanClientState *wwanClientContext =
245 (struct ChppWwanClientState *)clientContext;
246
247 chppClientCloseOpenRequests(&wwanClientContext->client, &kWwanClientConfig,
248 false /* clearOnly */);
249
250 CHPP_LOGI("WWAN client reopening from state=%" PRIu8,
251 wwanClientContext->client.openState);
252 chppClientSendOpenRequest(&wwanClientContext->client,
253 &wwanClientContext->outReqStates[CHPP_WWAN_OPEN],
254 CHPP_WWAN_OPEN,
255 /*blocking=*/false);
256 }
257
258 /**
259 * Notifies the client of being matched to a service.
260 *
261 * @param clientContext Maintains status for each client instance.
262 */
chppWwanClientNotifyMatch(void * clientContext)263 static void chppWwanClientNotifyMatch(void *clientContext) {
264 struct ChppWwanClientState *wwanClientContext =
265 (struct ChppWwanClientState *)clientContext;
266
267 if (wwanClientContext->client.pseudoOpen) {
268 CHPP_LOGD("Pseudo-open WWAN client opening");
269 chppClientSendOpenRequest(&wwanClientContext->client,
270 &wwanClientContext->outReqStates[CHPP_WWAN_OPEN],
271 CHPP_WWAN_OPEN,
272 /*blocking=*/false);
273 }
274 }
275
276 /**
277 * Handles the service response for the close client request.
278 *
279 * This function is called from chppDispatchWwanResponse().
280 *
281 * @param clientContext Maintains status for each client instance.
282 * @param buf Input data. Cannot be null.
283 * @param len Length of input data in bytes.
284 */
chppWwanCloseResult(struct ChppWwanClientState * clientContext,uint8_t * buf,size_t len)285 static void chppWwanCloseResult(struct ChppWwanClientState *clientContext,
286 uint8_t *buf, size_t len) {
287 // TODO
288 UNUSED_VAR(clientContext);
289 UNUSED_VAR(buf);
290 UNUSED_VAR(len);
291 }
292
293 /**
294 * Handles the service response for the get capabilities client request.
295 *
296 * This function is called from chppDispatchWwanResponse().
297 *
298 * @param clientContext Maintains status for each client instance.
299 * @param buf Input data. Cannot be null.
300 * @param len Length of input data in bytes.
301 */
chppWwanGetCapabilitiesResult(struct ChppWwanClientState * clientContext,uint8_t * buf,size_t len)302 static void chppWwanGetCapabilitiesResult(
303 struct ChppWwanClientState *clientContext, uint8_t *buf, size_t len) {
304 if (len < sizeof(struct ChppWwanGetCapabilitiesResponse)) {
305 CHPP_LOGE("Bad WWAN capabilities len=%" PRIuSIZE, len);
306
307 } else {
308 struct ChppWwanGetCapabilitiesParameters *result =
309 &((struct ChppWwanGetCapabilitiesResponse *)buf)->params;
310
311 CHPP_LOGD("chppWwanGetCapabilitiesResult received capabilities=0x%" PRIx32,
312 result->capabilities);
313
314 // TODO(b/229758746): Disable assertion on WWAN side until it's fixed
315 // CHPP_ASSERT((result->capabilities & CHPP_WWAN_DEFAULT_CAPABILITIES) ==
316 // CHPP_WWAN_DEFAULT_CAPABILITIES);
317 if (result->capabilities != CHPP_WWAN_DEFAULT_CAPABILITIES) {
318 CHPP_LOGE("WWAN capabilities 0x%" PRIx32 " != 0x%" PRIx32,
319 result->capabilities, CHPP_WWAN_DEFAULT_CAPABILITIES);
320 }
321
322 clientContext->capabilitiesValid = true;
323 clientContext->capabilities = result->capabilities;
324 }
325 }
326
327 /**
328 * Handles the service response for the asynchronous get cell info client
329 * request.
330 *
331 * This function is called from chppDispatchWwanResponse().
332 *
333 * @param clientContext Maintains status for each client instance.
334 * @param buf Input data. Cannot be null.
335 * @param len Length of input data in bytes.
336 */
chppWwanGetCellInfoAsyncResult(struct ChppWwanClientState * clientContext,uint8_t * buf,size_t len)337 static void chppWwanGetCellInfoAsyncResult(
338 struct ChppWwanClientState *clientContext, uint8_t *buf, size_t len) {
339 UNUSED_VAR(clientContext);
340 CHPP_LOGD("chppWwanGetCellInfoAsyncResult received data len=%" PRIuSIZE, len);
341
342 struct chreWwanCellInfoResult *chre = NULL;
343 uint8_t errorCode = CHRE_ERROR;
344
345 if (len == sizeof(struct ChppAppHeader)) {
346 errorCode = chppAppShortResponseErrorHandler(buf, len, "GetCellInfo");
347
348 } else {
349 buf += sizeof(struct ChppAppHeader);
350 len -= sizeof(struct ChppAppHeader);
351 chre =
352 chppWwanCellInfoResultToChre((struct ChppWwanCellInfoResult *)buf, len);
353
354 if (chre == NULL) {
355 CHPP_LOGE("Cell info conversion failed len=%" PRIuSIZE, len);
356 }
357 }
358
359 if (chre == NULL) {
360 chre = chppMalloc(sizeof(struct chreWwanCellInfoResult));
361 if (chre == NULL) {
362 CHPP_LOG_OOM();
363 } else {
364 chre->version = CHRE_WWAN_CELL_INFO_RESULT_VERSION;
365 chre->errorCode = errorCode;
366 chre->cellInfoCount = 0;
367 chre->reserved = 0;
368 chre->cookie = 0;
369 chre->cells = NULL;
370 }
371
372 } else {
373 #ifdef CHPP_CLIENT_ENABLED_TIMESYNC
374 int64_t offset = chppTimesyncGetOffset(gWwanClientContext.client.appContext,
375 CHPP_WWAN_MAX_TIMESYNC_AGE_NS);
376 for (uint8_t i = 0; i < chre->cellInfoCount; i++) {
377 uint64_t *timeStamp =
378 (uint64_t *)(CHPP_CONST_CAST_POINTER(&chre->cells[i].timeStamp));
379 *timeStamp -= (uint64_t)offset;
380 }
381 #endif
382 }
383
384 if (chre != NULL) {
385 gCallbacks->cellInfoResultCallback(chre);
386 }
387 }
388
389 /**
390 * Initializes the WWAN client upon an open request from CHRE and responds with
391 * the result.
392 *
393 * @param systemApi CHRE system function pointers.
394 * @param callbacks CHRE entry points.
395 *
396 * @return True if successful. False otherwise.
397 */
chppWwanClientOpen(const struct chrePalSystemApi * systemApi,const struct chrePalWwanCallbacks * callbacks)398 static bool chppWwanClientOpen(const struct chrePalSystemApi *systemApi,
399 const struct chrePalWwanCallbacks *callbacks) {
400 CHPP_DEBUG_NOT_NULL(systemApi);
401 CHPP_DEBUG_NOT_NULL(callbacks);
402
403 bool result = false;
404 gSystemApi = systemApi;
405 gCallbacks = callbacks;
406
407 CHPP_LOGD("WWAN client opening");
408 if (gWwanClientContext.client.appContext == NULL) {
409 CHPP_LOGE("WWAN client app is null");
410 } else {
411 // Wait for discovery to complete for "open" call to succeed
412 if (chppWaitForDiscoveryComplete(gWwanClientContext.client.appContext,
413 CHPP_WWAN_DISCOVERY_TIMEOUT_MS)) {
414 result = chppClientSendOpenRequest(
415 &gWwanClientContext.client,
416 &gWwanClientContext.outReqStates[CHPP_WWAN_OPEN], CHPP_WWAN_OPEN,
417 /*blocking=*/true);
418 }
419
420 // Since CHPP_WWAN_DEFAULT_CAPABILITIES is mandatory, we can always
421 // pseudo-open and return true. Otherwise, these should have been gated.
422 chppClientPseudoOpen(&gWwanClientContext.client);
423 result = true;
424 }
425
426 return result;
427 }
428
429 /**
430 * Deinitializes the WWAN client.
431 */
chppWwanClientClose(void)432 static void chppWwanClientClose(void) {
433 // Remote
434 struct ChppAppHeader *request = chppAllocClientRequestCommand(
435 &gWwanClientContext.client, CHPP_WWAN_CLOSE);
436
437 if (request == NULL) {
438 CHPP_LOG_OOM();
439 } else if (chppClientSendTimestampedRequestAndWait(
440 &gWwanClientContext.client,
441 &gWwanClientContext.outReqStates[CHPP_WWAN_CLOSE], request,
442 sizeof(*request))) {
443 gWwanClientContext.client.openState = CHPP_OPEN_STATE_CLOSED;
444 gWwanClientContext.capabilities = CHRE_WWAN_CAPABILITIES_NONE;
445 gWwanClientContext.capabilitiesValid = false;
446 chppClientCloseOpenRequests(&gWwanClientContext.client, &kWwanClientConfig,
447 true /* clearOnly */);
448 }
449 }
450
451 /**
452 * Retrieves a set of flags indicating the WWAN features supported by the
453 * current implementation.
454 *
455 * @return Capabilities flags.
456 */
chppWwanClientGetCapabilities(void)457 static uint32_t chppWwanClientGetCapabilities(void) {
458 uint32_t capabilities = CHPP_WWAN_DEFAULT_CAPABILITIES;
459
460 if (gWwanClientContext.capabilitiesValid) {
461 // Result already cached
462 capabilities = gWwanClientContext.capabilities;
463
464 } else {
465 struct ChppAppHeader *request = chppAllocClientRequestCommand(
466 &gWwanClientContext.client, CHPP_WWAN_GET_CAPABILITIES);
467
468 if (request == NULL) {
469 CHPP_LOG_OOM();
470 } else {
471 if (chppClientSendTimestampedRequestAndWait(
472 &gWwanClientContext.client,
473 &gWwanClientContext.outReqStates[CHPP_WWAN_GET_CAPABILITIES],
474 request, sizeof(*request))) {
475 // Success. gWwanClientContext.capabilities is now populated
476 if (gWwanClientContext.capabilitiesValid) {
477 capabilities = gWwanClientContext.capabilities;
478 }
479 }
480 }
481 }
482
483 return capabilities;
484 }
485
486 /**
487 * Query information about the current serving cell and its neighbors. This does
488 * not perform a network scan, but should return state from the current network
489 * registration data stored in the cellular modem.
490 *
491 * @return True indicates the request was sent off to the service.
492 */
chppWwanClientGetCellInfoAsync(void)493 static bool chppWwanClientGetCellInfoAsync(void) {
494 bool result = false;
495
496 struct ChppAppHeader *request = chppAllocClientRequestCommand(
497 &gWwanClientContext.client, CHPP_WWAN_GET_CELLINFO_ASYNC);
498
499 if (request == NULL) {
500 CHPP_LOG_OOM();
501 } else {
502 result = chppClientSendTimestampedRequestOrFail(
503 &gWwanClientContext.client,
504 &gWwanClientContext.outReqStates[CHPP_WWAN_GET_CELLINFO_ASYNC], request,
505 sizeof(*request), CHPP_REQUEST_TIMEOUT_DEFAULT);
506 }
507
508 return result;
509 }
510
511 /**
512 * Releases the memory held for the GetCellInfoAsync result.
513 */
chppWwanClientReleaseCellInfoResult(struct chreWwanCellInfoResult * result)514 static void chppWwanClientReleaseCellInfoResult(
515 struct chreWwanCellInfoResult *result) {
516 if (result->cellInfoCount > 0) {
517 void *cells = CHPP_CONST_CAST_POINTER(result->cells);
518 CHPP_FREE_AND_NULLIFY(cells);
519 }
520
521 CHPP_FREE_AND_NULLIFY(result);
522 }
523
524 /************************************************
525 * Public Functions
526 ***********************************************/
527
chppRegisterWwanClient(struct ChppAppState * appContext)528 void chppRegisterWwanClient(struct ChppAppState *appContext) {
529 memset(&gWwanClientContext, 0, sizeof(gWwanClientContext));
530 chppRegisterClient(appContext, (void *)&gWwanClientContext,
531 &gWwanClientContext.client,
532 gWwanClientContext.outReqStates, &kWwanClientConfig);
533 }
534
chppDeregisterWwanClient(struct ChppAppState * appContext)535 void chppDeregisterWwanClient(struct ChppAppState *appContext) {
536 // TODO
537
538 UNUSED_VAR(appContext);
539 }
540
getChppWwanClientState(void)541 struct ChppEndpointState *getChppWwanClientState(void) {
542 return &gWwanClientContext.client;
543 }
544
545 #ifdef CHPP_CLIENT_ENABLED_WWAN
546
547 #ifdef CHPP_CLIENT_ENABLED_CHRE_WWAN
chrePalWwanGetApi(uint32_t requestedApiVersion)548 const struct chrePalWwanApi *chrePalWwanGetApi(uint32_t requestedApiVersion) {
549 #else
550 const struct chrePalWwanApi *chppPalWwanGetApi(uint32_t requestedApiVersion) {
551 #endif
552
553 static const struct chrePalWwanApi api = {
554 .moduleVersion = CHPP_PAL_WWAN_API_VERSION,
555 .open = chppWwanClientOpen,
556 .close = chppWwanClientClose,
557 .getCapabilities = chppWwanClientGetCapabilities,
558 .requestCellInfo = chppWwanClientGetCellInfoAsync,
559 .releaseCellInfoResult = chppWwanClientReleaseCellInfoResult,
560 };
561
562 CHPP_STATIC_ASSERT(
563 CHRE_PAL_WWAN_API_CURRENT_VERSION == CHPP_PAL_WWAN_API_VERSION,
564 "A newer CHRE PAL API version is available. Please update.");
565
566 if (!CHRE_PAL_VERSIONS_ARE_COMPATIBLE(api.moduleVersion,
567 requestedApiVersion)) {
568 return NULL;
569 } else {
570 return &api;
571 }
572 }
573
574 #endif
575