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