• 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 #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     // Client timeout function pointer
114     .timeoutFunctionPtr = NULL,  // Not used
115 
116     // Number of request-response states in the outReqStates array.
117     .outReqCount = ARRAY_SIZE(gWwanClientContext.outReqStates),
118 
119     // Min length is the entire header
120     .minLength = sizeof(struct ChppAppHeader),
121 };
122 
123 /************************************************
124  *  Prototypes
125  ***********************************************/
126 
127 static bool chppWwanClientOpen(const struct chrePalSystemApi *systemApi,
128                                const struct chrePalWwanCallbacks *callbacks);
129 static void chppWwanClientClose(void);
130 static uint32_t chppWwanClientGetCapabilities(void);
131 static bool chppWwanClientGetCellInfoAsync(void);
132 static void chppWwanClientReleaseCellInfoResult(
133     struct chreWwanCellInfoResult *result);
134 
135 static void chppWwanCloseResult(struct ChppWwanClientState *clientContext,
136                                 uint8_t *buf, size_t len);
137 static void chppWwanGetCapabilitiesResult(
138     struct ChppWwanClientState *clientContext, uint8_t *buf, size_t len);
139 static void chppWwanGetCellInfoAsyncResult(
140     struct ChppWwanClientState *clientContext, uint8_t *buf, size_t len);
141 
142 /************************************************
143  *  Private Functions
144  ***********************************************/
145 
146 /**
147  * Dispatches a service response from the transport layer that is determined to
148  * be for the WWAN client.
149  *
150  * This function is called from the app layer using its function pointer given
151  * during client registration.
152  *
153  * @param clientContext Maintains status for each client instance.
154  * @param buf Input data. Cannot be null.
155  * @param len Length of input data in bytes.
156  *
157  * @return Indicates the result of this function call.
158  */
chppDispatchWwanResponse(void * clientContext,uint8_t * buf,size_t len)159 static enum ChppAppErrorCode chppDispatchWwanResponse(void *clientContext,
160                                                       uint8_t *buf,
161                                                       size_t len) {
162   struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf;
163   struct ChppWwanClientState *wwanClientContext =
164       (struct ChppWwanClientState *)clientContext;
165   enum ChppAppErrorCode error = CHPP_APP_ERROR_NONE;
166 
167   if (rxHeader->command > CHPP_WWAN_CLIENT_REQUEST_MAX) {
168     error = CHPP_APP_ERROR_INVALID_COMMAND;
169 
170   } else if (!chppTimestampIncomingResponse(
171                  wwanClientContext->client.appContext,
172                  &wwanClientContext->outReqStates[rxHeader->command],
173                  rxHeader)) {
174     error = CHPP_APP_ERROR_UNEXPECTED_RESPONSE;
175 
176   } else {
177     switch (rxHeader->command) {
178       case CHPP_WWAN_OPEN: {
179         chppClientProcessOpenResponse(&wwanClientContext->client, buf, len);
180         break;
181       }
182 
183       case CHPP_WWAN_CLOSE: {
184         chppWwanCloseResult(wwanClientContext, buf, len);
185         break;
186       }
187 
188       case CHPP_WWAN_GET_CAPABILITIES: {
189         chppWwanGetCapabilitiesResult(wwanClientContext, buf, len);
190         break;
191       }
192 
193       case CHPP_WWAN_GET_CELLINFO_ASYNC: {
194         chppWwanGetCellInfoAsyncResult(wwanClientContext, buf, len);
195         break;
196       }
197 
198       default: {
199         error = CHPP_APP_ERROR_INVALID_COMMAND;
200         break;
201       }
202     }
203   }
204 
205   return error;
206 }
207 
208 /**
209  * Initializes the client and provides its handle number and the version of the
210  * matched service when/if it the client is matched with a service during
211  * discovery.
212  *
213  * @param clientContext Maintains status for each client instance.
214  * @param handle Handle number for this client.
215  * @param serviceVersion Version of the matched service.
216  *
217  * @return True if client is compatible and successfully initialized.
218  */
chppWwanClientInit(void * clientContext,uint8_t handle,struct ChppVersion serviceVersion)219 static bool chppWwanClientInit(void *clientContext, uint8_t handle,
220                                struct ChppVersion serviceVersion) {
221   UNUSED_VAR(serviceVersion);
222 
223   struct ChppWwanClientState *wwanClientContext =
224       (struct ChppWwanClientState *)clientContext;
225   chppClientInit(&wwanClientContext->client, handle);
226 
227   return true;
228 }
229 
230 /**
231  * Deinitializes the client.
232  *
233  * @param clientContext Maintains status for each client instance.
234  */
chppWwanClientDeinit(void * clientContext)235 static void chppWwanClientDeinit(void *clientContext) {
236   struct ChppWwanClientState *wwanClientContext =
237       (struct ChppWwanClientState *)clientContext;
238   chppClientDeinit(&wwanClientContext->client);
239 }
240 
241 /**
242  * Notifies the client of an incoming reset.
243  *
244  * @param clientContext Maintains status for each client instance.
245  */
chppWwanClientNotifyReset(void * clientContext)246 static void chppWwanClientNotifyReset(void *clientContext) {
247   struct ChppWwanClientState *wwanClientContext =
248       (struct ChppWwanClientState *)clientContext;
249 
250   chppClientCloseOpenRequests(&wwanClientContext->client, &kWwanClientConfig,
251                               false /* clearOnly */);
252 
253   CHPP_LOGI("WWAN client reopening from state=%" PRIu8,
254             wwanClientContext->client.openState);
255   chppClientSendOpenRequest(&wwanClientContext->client,
256                             &wwanClientContext->outReqStates[CHPP_WWAN_OPEN],
257                             CHPP_WWAN_OPEN,
258                             /*blocking=*/false);
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->outReqStates[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_NOT_NULL(systemApi);
404   CHPP_DEBUG_NOT_NULL(callbacks);
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.outReqStates[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 (chppClientSendTimestampedRequestAndWait(
443                  &gWwanClientContext.client,
444                  &gWwanClientContext.outReqStates[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 (chppClientSendTimestampedRequestAndWait(
475               &gWwanClientContext.client,
476               &gWwanClientContext.outReqStates[CHPP_WWAN_GET_CAPABILITIES],
477               request, 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 = chppClientSendTimestampedRequestOrFail(
506         &gWwanClientContext.client,
507         &gWwanClientContext.outReqStates[CHPP_WWAN_GET_CELLINFO_ASYNC], request,
508         sizeof(*request), CHPP_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,
535                      gWwanClientContext.outReqStates, &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 ChppEndpointState *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