1 /*
2  * Copyright (C) 2017 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 /* This implements a GPS hardware HAL library for cuttlefish.
18  * A produced shared library is placed in /system/lib/hw/gps.gce.so, and
19  * loaded by hardware/libhardware/hardware.c code which is called from
20  * android_location_GpsLocationProvider.cpp
21  */
22 
23 #include <errno.h>
24 #include <pthread.h>
25 #include <stdint.h>
26 #include <unistd.h>
27 #include <inttypes.h>
28 
29 #include <log/log.h>
30 #include <cutils/sockets.h>
31 #include <hardware/gps.h>
32 
33 #include "guest/hals/gps/gps_thread.h"
34 
35 static GpsState _gps_state;
36 
37 // Cleans up GpsState data structure.
gps_state_cleanup(GpsState * s)38 static void gps_state_cleanup(GpsState* s) {
39   char cmd = CMD_QUIT;
40 
41   write(s->control[0], &cmd, 1);
42   if (s->thread > 0) {
43     pthread_join(s->thread, NULL);
44   }
45 
46   close(s->control[0]);
47   close(s->control[1]);
48   close(s->fd);
49 
50   s->thread = 0;
51   s->control[0] = -1;
52   s->control[1] = -1;
53   s->fd = -1;
54   s->init = 0;
55 }
56 
gce_gps_init(GpsCallbacks * callbacks)57 static int gce_gps_init(GpsCallbacks* callbacks) {
58   D("%s: called", __FUNCTION__);
59   // Stop if the framework does not fulfill its interface contract.
60   // We don't want to return an error and continue to ensure that we
61   // catch framework breaks ASAP and to give a tombstone to track down the
62   // offending code.
63   LOG_ALWAYS_FATAL_IF(!callbacks->location_cb);
64   LOG_ALWAYS_FATAL_IF(!callbacks->status_cb);
65   LOG_ALWAYS_FATAL_IF(!callbacks->sv_status_cb);
66   LOG_ALWAYS_FATAL_IF(!callbacks->nmea_cb);
67   LOG_ALWAYS_FATAL_IF(!callbacks->set_capabilities_cb);
68   LOG_ALWAYS_FATAL_IF(!callbacks->acquire_wakelock_cb);
69   LOG_ALWAYS_FATAL_IF(!callbacks->release_wakelock_cb);
70   LOG_ALWAYS_FATAL_IF(!callbacks->create_thread_cb);
71   LOG_ALWAYS_FATAL_IF(!callbacks->request_utc_time_cb);
72   if (!_gps_state.init) {
73     _gps_state.init = 1;
74     _gps_state.control[0] = -1;
75     _gps_state.control[1] = -1;
76     _gps_state.thread = 0;
77 
78     _gps_state.fd = socket_local_client(
79         "gps_broadcasts", ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
80     if (_gps_state.fd < 0) {
81       ALOGE("no GPS emulation detected.");
82       goto Fail;
83     }
84     D("GPS HAL will receive data from remoter via gps_broadcasts channel.");
85 
86     if (socketpair(AF_LOCAL, SOCK_STREAM, 0, _gps_state.control) < 0) {
87       ALOGE("could not create thread control socket pair: %s", strerror(errno));
88       goto Fail;
89     }
90 
91     _gps_state.callbacks = *callbacks;
92     ALOGE("Starting thread callback=%p", callbacks->location_cb);
93     _gps_state.thread = callbacks->create_thread_cb(
94         "gps_state_thread", gps_state_thread, &_gps_state);
95     if (!_gps_state.thread) {
96       ALOGE("could not create GPS thread: %s", strerror(errno));
97       goto Fail;
98     }
99   }
100 
101   if (_gps_state.fd < 0) return -1;
102   return 0;
103 
104 Fail:
105   gps_state_cleanup(&_gps_state);
106   return -1;
107 }
108 
gce_gps_cleanup()109 static void gce_gps_cleanup() {
110   D("%s: called", __FUNCTION__);
111   if (_gps_state.init) gps_state_cleanup(&_gps_state);
112 }
113 
gce_gps_start()114 static int gce_gps_start() {
115   if (!_gps_state.init) {
116     ALOGE("%s: called with uninitialized gps_state!", __FUNCTION__);
117     return -1;
118   }
119 
120   char cmd = CMD_START;
121   int ret;
122   do {
123     ret = write(_gps_state.control[0], &cmd, 1);
124   } while (ret < 0 && errno == EINTR);
125 
126   if (ret != 1) {
127     D("%s: could not send CMD_START command: ret=%d: %s", __FUNCTION__, ret,
128       strerror(errno));
129     return -1;
130   }
131 
132   return 0;
133 }
134 
gce_gps_stop()135 static int gce_gps_stop() {
136   D("%s: called", __FUNCTION__);
137   if (!_gps_state.init) {
138     ALOGE("%s: called with uninitialized gps_state!", __FUNCTION__);
139     return -1;
140   }
141 
142   char cmd = CMD_STOP;
143   int ret;
144 
145   do {
146     ret = write(_gps_state.control[0], &cmd, 1);
147   } while (ret < 0 && errno == EINTR);
148 
149   if (ret != 1) {
150     ALOGE("%s: could not send CMD_STOP command: ret=%d: %s", __FUNCTION__, ret,
151           strerror(errno));
152     return -1;
153   }
154   return 0;
155 }
156 
gce_gps_inject_time(GpsUtcTime,int64_t,int)157 static int gce_gps_inject_time(GpsUtcTime /*time*/, int64_t /*time_ref*/,
158                                int /*uncertainty*/) {
159   D("%s: called", __FUNCTION__);
160   if (!_gps_state.init) {
161     ALOGE("%s: called with uninitialized gps_state!", __FUNCTION__);
162     return -1;
163   }
164 
165   return 0;
166 }
167 
gce_gps_inject_location(double,double,float)168 static int gce_gps_inject_location(double /*latitude*/, double /*longitude*/,
169                                    float /*accuracy*/) {
170   D("%s: called", __FUNCTION__);
171   if (!_gps_state.init) {
172     ALOGE("%s: called with uninitialized gps_state!", __FUNCTION__);
173     return -1;
174   }
175 
176   return 0;
177 }
178 
gce_gps_delete_aiding_data(GpsAidingData)179 static void gce_gps_delete_aiding_data(GpsAidingData /*flags*/) {
180   D("%s: called", __FUNCTION__);
181   if (!_gps_state.init) {
182     ALOGE("%s: called with uninitialized gps_state!", __FUNCTION__);
183     return;
184   }
185 }
186 
gce_gps_set_position_mode(GpsPositionMode mode,GpsPositionRecurrence recurrence,uint32_t min_interval,uint32_t preferred_accuracy,uint32_t preferred_time)187 static int gce_gps_set_position_mode(GpsPositionMode mode,
188                                      GpsPositionRecurrence recurrence,
189                                      uint32_t min_interval,
190                                      uint32_t preferred_accuracy,
191                                      uint32_t preferred_time) {
192   D("%s: called", __FUNCTION__);
193   if (!_gps_state.init) {
194     ALOGE("%s: called with uninitialized gps_state!", __FUNCTION__);
195     return -1;
196   }
197   ALOGE("%s(mode=%d, recurrence=%d, min_interval=%" PRIu32
198         ", "
199         "preferred_accuracy=%" PRIu32 ", preferred_time=%" PRIu32
200         ") unimplemented",
201         __FUNCTION__, mode, recurrence, min_interval, preferred_accuracy,
202         preferred_time);
203   return 0;
204 }
205 
gce_gps_get_extension(const char * name)206 static const void* gce_gps_get_extension(const char* name) {
207   D("%s: called", __FUNCTION__);
208   // It is normal for this to be called before init.
209   ALOGE("%s(%s): called but not implemented.", __FUNCTION__,
210         name ? name : "NULL");
211   return NULL;
212 }
213 
214 static const GpsInterface gceGpsInterface = {
215     sizeof(GpsInterface),
216     gce_gps_init,
217     gce_gps_start,
218     gce_gps_stop,
219     gce_gps_cleanup,
220     gce_gps_inject_time,
221     gce_gps_inject_location,
222     gce_gps_delete_aiding_data,
223     gce_gps_set_position_mode,
224     gce_gps_get_extension,
225 };
226 
gps_get_gps_interface(struct gps_device_t *)227 const GpsInterface* gps_get_gps_interface(struct gps_device_t* /*dev*/) {
228   return &gceGpsInterface;
229 }
230 
open_gps(const struct hw_module_t * module,char const *,struct hw_device_t ** device)231 static int open_gps(const struct hw_module_t* module, char const* /*name*/,
232                     struct hw_device_t** device) {
233   struct gps_device_t* dev =
234       (struct gps_device_t*)malloc(sizeof(struct gps_device_t));
235   LOG_FATAL_IF(!dev, "%s: malloc returned NULL.", __FUNCTION__);
236   memset(dev, 0, sizeof(*dev));
237 
238   dev->common.tag = HARDWARE_DEVICE_TAG;
239   dev->common.version = 0;
240   dev->common.module = (struct hw_module_t*)module;
241   dev->get_gps_interface = gps_get_gps_interface;
242 
243   *device = (struct hw_device_t*)dev;
244   return 0;
245 }
246 
247 static struct hw_module_methods_t gps_module_methods = {
248     .open = open_gps};
249 
250 struct hw_module_t HAL_MODULE_INFO_SYM = {
251     .tag = HARDWARE_MODULE_TAG,
252     .version_major = 1,
253     .version_minor = 0,
254     .id = GPS_HARDWARE_MODULE_ID,
255     .name = "GCE GPS Module",
256     .author = "The Android Open Source Project",
257     .methods = & gps_module_methods,
258 };
259