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