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