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/services/wwan.h"
18 
19 #include <inttypes.h>
20 #include <stdbool.h>
21 #include <stddef.h>
22 #include <stdint.h>
23 
24 #include "chpp/common/standard_uuids.h"
25 #include "chpp/common/wwan.h"
26 #include "chpp/common/wwan_types.h"
27 #include "chpp/log.h"
28 #include "chpp/macros.h"
29 #include "chpp/memory.h"
30 #include "chpp/services.h"
31 #include "chre/pal/wwan.h"
32 
33 /************************************************
34  *  Prototypes
35  ***********************************************/
36 
37 static enum ChppAppErrorCode chppDispatchWwanRequest(void *serviceContext,
38                                                      uint8_t *buf, size_t len);
39 static void chppWwanServiceNotifyReset(void *serviceContext);
40 
41 /************************************************
42  *  Private Definitions
43  ***********************************************/
44 
45 /**
46  * Configuration parameters for this service
47  */
48 static const struct ChppService kWwanServiceConfig = {
49     .descriptor.uuid = CHPP_UUID_WWAN_STANDARD,
50 
51     // Human-readable name
52     .descriptor.name = "WWAN",
53 
54     // Version
55     .descriptor.version.major = 1,
56     .descriptor.version.minor = 0,
57     .descriptor.version.patch = 0,
58 
59     // Notifies service if CHPP is reset
60     .resetNotifierFunctionPtr = &chppWwanServiceNotifyReset,
61 
62     // Client request dispatch function pointer
63     .requestDispatchFunctionPtr = &chppDispatchWwanRequest,
64 
65     // Client notification dispatch function pointer
66     .notificationDispatchFunctionPtr = NULL,  // Not supported
67 
68     // Min length is the entire header
69     .minLength = sizeof(struct ChppAppHeader),
70 };
71 
72 /**
73  * Structure to maintain state for the WWAN service and its Request/Response
74  * (RR) functionality.
75  */
76 struct ChppWwanServiceState {
77   struct ChppServiceState service;   // WWAN service state
78   const struct chrePalWwanApi *api;  // WWAN PAL API
79 
80   struct ChppRequestResponseState open;              // Service init state
81   struct ChppRequestResponseState close;             // Service deinit state
82   struct ChppRequestResponseState getCapabilities;   // Get Capabilities state
83   struct ChppRequestResponseState getCellInfoAsync;  // Get CellInfo Async state
84 };
85 
86 // Note: This global definition of gWwanServiceContext supports only one
87 // instance of the CHPP WWAN service at a time. This limitation is primarily due
88 // to the PAL API.
89 // It would be possible to generate different API and callback pointers to
90 // support multiple instances of the service or modify the PAL API to pass a
91 // void* for context, but this is not necessary in the current version of CHPP.
92 // In such case, wwanServiceContext would be allocated dynamically as part of
93 // chppRegisterWwanService(), e.g.
94 //   struct ChppWwanServiceState *wwanServiceContext = chppMalloc(...);
95 // instead of globally here.
96 struct ChppWwanServiceState gWwanServiceContext;
97 
98 /************************************************
99  *  Prototypes
100  ***********************************************/
101 
102 static enum ChppAppErrorCode chppWwanServiceOpen(
103     struct ChppWwanServiceState *wwanServiceContext,
104     struct ChppAppHeader *requestHeader);
105 static enum ChppAppErrorCode chppWwanServiceClose(
106     struct ChppWwanServiceState *wwanServiceContext,
107     struct ChppAppHeader *requestHeader);
108 static enum ChppAppErrorCode chppWwanServiceGetCapabilities(
109     struct ChppWwanServiceState *wwanServiceContext,
110     struct ChppAppHeader *requestHeader);
111 static enum ChppAppErrorCode chppWwanServiceGetCellInfoAsync(
112     struct ChppWwanServiceState *wwanServiceContext,
113     struct ChppAppHeader *requestHeader);
114 
115 static void chppWwanServiceCellInfoResultCallback(
116     struct chreWwanCellInfoResult *result);
117 
118 /************************************************
119  *  Private Functions
120  ***********************************************/
121 
122 /**
123  * Dispatches a client request from the transport layer that is determined to be
124  * for the WWAN service. If the result of the dispatch is an error, this
125  * function responds to the client with the same error.
126  *
127  * This function is called from the app layer using its function pointer given
128  * during service registration.
129  *
130  * @param serviceContext Maintains status for each service instance.
131  * @param buf Input data. Cannot be null.
132  * @param len Length of input data in bytes.
133  *
134  * @return Indicates the result of this function call.
135  */
chppDispatchWwanRequest(void * serviceContext,uint8_t * buf,size_t len)136 static enum ChppAppErrorCode chppDispatchWwanRequest(void *serviceContext,
137                                                      uint8_t *buf, size_t len) {
138   struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf;
139   struct ChppWwanServiceState *wwanServiceContext =
140       (struct ChppWwanServiceState *)serviceContext;
141   struct ChppRequestResponseState *rRState = NULL;
142   enum ChppAppErrorCode error = CHPP_APP_ERROR_NONE;
143   bool dispatched = true;
144 
145   UNUSED_VAR(len);
146 
147   switch (rxHeader->command) {
148     case CHPP_WWAN_OPEN: {
149       rRState = &wwanServiceContext->open;
150       chppServiceTimestampRequest(rRState, rxHeader);
151       error = chppWwanServiceOpen(wwanServiceContext, rxHeader);
152       break;
153     }
154 
155     case CHPP_WWAN_CLOSE: {
156       rRState = &wwanServiceContext->close;
157       chppServiceTimestampRequest(rRState, rxHeader);
158       error = chppWwanServiceClose(wwanServiceContext, rxHeader);
159       break;
160     }
161 
162     case CHPP_WWAN_GET_CAPABILITIES: {
163       rRState = &wwanServiceContext->getCapabilities;
164       chppServiceTimestampRequest(rRState, rxHeader);
165       error = chppWwanServiceGetCapabilities(wwanServiceContext, rxHeader);
166       break;
167     }
168 
169     case CHPP_WWAN_GET_CELLINFO_ASYNC: {
170       rRState = &wwanServiceContext->getCellInfoAsync;
171       chppServiceTimestampRequest(rRState, rxHeader);
172       error = chppWwanServiceGetCellInfoAsync(wwanServiceContext, rxHeader);
173       break;
174     }
175 
176     default: {
177       dispatched = false;
178       error = CHPP_APP_ERROR_INVALID_COMMAND;
179       break;
180     }
181   }
182 
183   if (dispatched == true && error != CHPP_APP_ERROR_NONE) {
184     // Request was dispatched but an error was returned. Close out
185     // chppServiceTimestampRequest()
186     chppServiceTimestampResponse(rRState);
187   }
188 
189   return error;
190 }
191 
192 /**
193  * Initializes the WWAN service upon an open request from the client and
194  * responds to the client with the result.
195  *
196  * @param serviceContext Maintains status for each service instance.
197  * @param requestHeader App layer header of the request.
198  *
199  * @return Indicates the result of this function call.
200  */
chppWwanServiceOpen(struct ChppWwanServiceState * wwanServiceContext,struct ChppAppHeader * requestHeader)201 static enum ChppAppErrorCode chppWwanServiceOpen(
202     struct ChppWwanServiceState *wwanServiceContext,
203     struct ChppAppHeader *requestHeader) {
204   static const struct chrePalWwanCallbacks palCallbacks = {
205       .cellInfoResultCallback = chppWwanServiceCellInfoResultCallback,
206   };
207 
208   enum ChppAppErrorCode error = CHPP_APP_ERROR_NONE;
209 
210   if (wwanServiceContext->service.openState == CHPP_OPEN_STATE_OPENED) {
211     CHPP_LOGE("WWAN service already open");
212     CHPP_DEBUG_ASSERT(false);
213     error = CHPP_APP_ERROR_INVALID_COMMAND;
214 
215   } else if (!wwanServiceContext->api->open(
216                  wwanServiceContext->service.appContext->systemApi,
217                  &palCallbacks)) {
218     CHPP_LOGE("WWAN PAL open failed");
219     CHPP_DEBUG_ASSERT(false);
220     error = CHPP_APP_ERROR_BEYOND_CHPP;
221 
222   } else {
223     CHPP_LOGI("WWAN service opened");
224     wwanServiceContext->service.openState = CHPP_OPEN_STATE_OPENED;
225 
226     struct ChppAppHeader *response =
227         chppAllocServiceResponseFixed(requestHeader, struct ChppAppHeader);
228     size_t responseLen = sizeof(*response);
229 
230     if (response == NULL) {
231       CHPP_LOG_OOM();
232       error = CHPP_APP_ERROR_OOM;
233     } else {
234       chppSendTimestampedResponseOrFail(&wwanServiceContext->service,
235                                         &wwanServiceContext->open, response,
236                                         responseLen);
237     }
238   }
239 
240   return error;
241 }
242 
243 /**
244  * Deinitializes the WWAN service.
245  *
246  * @param serviceContext Maintains status for each service instance.
247  * @param requestHeader App layer header of the request.
248  *
249  * @return Indicates the result of this function call.
250  */
chppWwanServiceClose(struct ChppWwanServiceState * wwanServiceContext,struct ChppAppHeader * requestHeader)251 static enum ChppAppErrorCode chppWwanServiceClose(
252     struct ChppWwanServiceState *wwanServiceContext,
253     struct ChppAppHeader *requestHeader) {
254   enum ChppAppErrorCode error = CHPP_APP_ERROR_NONE;
255 
256   wwanServiceContext->api->close();
257   wwanServiceContext->service.openState = CHPP_OPEN_STATE_CLOSED;
258 
259   CHPP_LOGI("WWAN service closed");
260 
261   struct ChppAppHeader *response =
262       chppAllocServiceResponseFixed(requestHeader, struct ChppAppHeader);
263   size_t responseLen = sizeof(*response);
264 
265   if (response == NULL) {
266     CHPP_LOG_OOM();
267     error = CHPP_APP_ERROR_OOM;
268   } else {
269     chppSendTimestampedResponseOrFail(&wwanServiceContext->service,
270                                       &wwanServiceContext->close, response,
271                                       responseLen);
272   }
273 
274   return error;
275 }
276 
277 /**
278  * Notifies the service of an incoming reset.
279  *
280  * @param serviceContext Maintains status for each service instance.
281  */
chppWwanServiceNotifyReset(void * serviceContext)282 static void chppWwanServiceNotifyReset(void *serviceContext) {
283   struct ChppWwanServiceState *wwanServiceContext =
284       (struct ChppWwanServiceState *)serviceContext;
285 
286   if (wwanServiceContext->service.openState != CHPP_OPEN_STATE_OPENED) {
287     CHPP_LOGW("WWAN service reset but wasn't open");
288   } else {
289     CHPP_LOGI("WWAN service reset. Closing");
290     wwanServiceContext->service.openState = CHPP_OPEN_STATE_CLOSED;
291     wwanServiceContext->api->close();
292   }
293 }
294 
295 /**
296  * Retrieves a set of flags indicating the WWAN features supported by the
297  * current implementation.
298  *
299  * @param serviceContext Maintains status for each service instance.
300  * @param requestHeader App layer header of the request.
301  *
302  * @return Indicates the result of this function call.
303  */
chppWwanServiceGetCapabilities(struct ChppWwanServiceState * wwanServiceContext,struct ChppAppHeader * requestHeader)304 static enum ChppAppErrorCode chppWwanServiceGetCapabilities(
305     struct ChppWwanServiceState *wwanServiceContext,
306     struct ChppAppHeader *requestHeader) {
307   enum ChppAppErrorCode error = CHPP_APP_ERROR_NONE;
308 
309   struct ChppWwanGetCapabilitiesResponse *response =
310       chppAllocServiceResponseFixed(requestHeader,
311                                     struct ChppWwanGetCapabilitiesResponse);
312   size_t responseLen = sizeof(*response);
313 
314   if (response == NULL) {
315     CHPP_LOG_OOM();
316     error = CHPP_APP_ERROR_OOM;
317   } else {
318     response->params.capabilities = wwanServiceContext->api->getCapabilities();
319 
320     CHPP_LOGD("chppWwanServiceGetCapabilities returning 0x%" PRIx32
321               ", %" PRIuSIZE " bytes",
322               response->params.capabilities, responseLen);
323     chppSendTimestampedResponseOrFail(&wwanServiceContext->service,
324                                       &wwanServiceContext->getCapabilities,
325                                       response, responseLen);
326   }
327 
328   return error;
329 }
330 
331 /**
332  * Query information about the current serving cell and its neighbors in
333  * response to a client request. This does not perform a network scan, but
334  * should return state from the current network registration data stored in the
335  * cellular modem.
336  *
337  * This function returns an error code synchronously. The requested cellular
338  * information shall be returned asynchronously to the client via the
339  * chppPlatformWwanCellInfoResultEvent() service response.
340  *
341  * @param serviceContext Maintains status for each service instance.
342  * @param requestHeader App layer header of the request.
343  *
344  * @return Indicates the result of this function call.
345  */
chppWwanServiceGetCellInfoAsync(struct ChppWwanServiceState * wwanServiceContext,struct ChppAppHeader * requestHeader)346 static enum ChppAppErrorCode chppWwanServiceGetCellInfoAsync(
347     struct ChppWwanServiceState *wwanServiceContext,
348     struct ChppAppHeader *requestHeader) {
349   UNUSED_VAR(requestHeader);
350 
351   enum ChppAppErrorCode error = CHPP_APP_ERROR_NONE;
352 
353   if (!wwanServiceContext->api->requestCellInfo()) {
354     CHPP_LOGE(
355         "WWAN requestCellInfo PAL API failed. Unable to register for callback");
356     error = CHPP_APP_ERROR_UNSPECIFIED;
357   }
358 
359   return error;
360 }
361 
362 /**
363  * PAL callback to provide the result of a prior Request Cell Info
364  * (cellInfoResultCallback).
365  *
366  * @param result Scan results.
367  */
chppWwanServiceCellInfoResultCallback(struct chreWwanCellInfoResult * result)368 static void chppWwanServiceCellInfoResultCallback(
369     struct chreWwanCellInfoResult *result) {
370   // Recover state
371   struct ChppRequestResponseState *rRState =
372       &gWwanServiceContext.getCellInfoAsync;
373   struct ChppWwanServiceState *wwanServiceContext =
374       container_of(rRState, struct ChppWwanServiceState, getCellInfoAsync);
375 
376   // Craft response per parser script
377   struct ChppWwanCellInfoResultWithHeader *response = NULL;
378   size_t responseLen = 0;
379   if (!chppWwanCellInfoResultFromChre(result, &response, &responseLen)) {
380     CHPP_LOGE("CellInfo conversion failed (OOM?) ID=%" PRIu8,
381               rRState->transaction);
382 
383     response = chppMalloc(sizeof(struct ChppAppHeader));
384     if (response == NULL) {
385       CHPP_LOG_OOM();
386     } else {
387       responseLen = sizeof(struct ChppAppHeader);
388     }
389   }
390 
391   if (response != NULL) {
392     response->header.handle = wwanServiceContext->service.handle;
393     response->header.type = CHPP_MESSAGE_TYPE_SERVICE_RESPONSE;
394     response->header.transaction = rRState->transaction;
395     response->header.error = (responseLen > sizeof(struct ChppAppHeader))
396                                  ? CHPP_APP_ERROR_NONE
397                                  : CHPP_APP_ERROR_CONVERSION_FAILED;
398     response->header.command = CHPP_WWAN_GET_CELLINFO_ASYNC;
399 
400     chppSendTimestampedResponseOrFail(&wwanServiceContext->service, rRState,
401                                       response, responseLen);
402   }
403 
404   gWwanServiceContext.api->releaseCellInfoResult(result);
405 }
406 
407 /************************************************
408  *  Public Functions
409  ***********************************************/
410 
chppRegisterWwanService(struct ChppAppState * appContext)411 void chppRegisterWwanService(struct ChppAppState *appContext) {
412   gWwanServiceContext.api = chrePalWwanGetApi(CHPP_PAL_WWAN_API_VERSION);
413 
414   if (gWwanServiceContext.api == NULL) {
415     CHPP_LOGE(
416         "WWAN PAL API version not compatible with CHPP. Cannot register WWAN "
417         "service");
418     CHPP_DEBUG_ASSERT(false);
419 
420   } else {
421     gWwanServiceContext.service.appContext = appContext;
422     gWwanServiceContext.service.handle = chppRegisterService(
423         appContext, (void *)&gWwanServiceContext, &kWwanServiceConfig);
424     CHPP_DEBUG_ASSERT(gWwanServiceContext.service.handle);
425   }
426 }
427 
chppDeregisterWwanService(struct ChppAppState * appContext)428 void chppDeregisterWwanService(struct ChppAppState *appContext) {
429   // TODO
430 
431   UNUSED_VAR(appContext);
432 }
433