• 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/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