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