forked from ish-app/ish
-
Notifications
You must be signed in to change notification settings - Fork 11
/
LocationDevice.m
174 lines (144 loc) · 4.45 KB
/
LocationDevice.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
//
// LocationDevice.m
// iSH
//
// Created by Theodore Dubois on 10/20/19.
//
#import <Foundation/Foundation.h>
#import <CoreLocation/CoreLocation.h>
#include "kernel/fs.h"
#include "fs/dev.h"
#include "util/sync.h"
@interface LocationTracker : NSObject <CLLocationManagerDelegate>
+ (LocationTracker *)instance;
@property CLLocationManager *locationManager;
@property (nonatomic) CLLocation *latest;
@property lock_t lock;
@property cond_t updateCond;
- (int)waitForUpdate;
@end
BOOL CLIsAuthorized(CLAuthorizationStatus status) {
return status == kCLAuthorizationStatusAuthorizedWhenInUse || status == kCLAuthorizationStatusAuthorizedAlways;
}
@implementation LocationTracker
+ (LocationTracker *)instance {
static __weak LocationTracker *tracker;
if (tracker == nil) {
__block LocationTracker *newTracker;
dispatch_sync(dispatch_get_main_queue(), ^{
if (tracker == nil) {
newTracker = [LocationTracker new];
tracker = newTracker;
}
});
return newTracker;
}
return tracker;
}
- (instancetype)init {
if (self = [super init]) {
self.locationManager = [CLLocationManager new];
self.locationManager.delegate = self;
self.locationManager.allowsBackgroundLocationUpdates = YES;
if (CLIsAuthorized([CLLocationManager authorizationStatus])) {
[self.locationManager startUpdatingLocation];
[self.locationManager requestLocation];
} else {
[self.locationManager requestAlwaysAuthorization];
}
lock_init(&_lock);
cond_init(&_updateCond);
}
return self;
}
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations {
lock(&_lock);
self.latest = locations.lastObject;
notify(&_updateCond);
unlock(&_lock);
}
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {
NSLog(@"location failed %@", error);
}
- (void)dealloc {
[self.locationManager stopUpdatingLocation];
cond_destroy(&_updateCond);
}
- (int)waitForUpdate {
lock(&_lock);
CLLocation *oldLatest = self.latest;
int err = 0;
while (self.latest == oldLatest) {
err = wait_for(&_updateCond, &_lock, NULL);
if (err < 0)
break;
}
unlock(&_lock);
return err;
}
- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status {
if (status == kCLAuthorizationStatusAuthorizedAlways || status == kCLAuthorizationStatusAuthorizedWhenInUse) {
NSLog(@"got auth, starting updates");
[manager startUpdatingLocation];
}
}
@end
@interface LocationFile : NSObject {
NSData *buffer;
size_t bufferOffset;
}
@property LocationTracker *tracker;
- (ssize_t)readIntoBuffer:(void *)buf size:(size_t)size;
@end
@implementation LocationFile
- (instancetype)init {
if (self = [super init]) {
self.tracker = [LocationTracker instance];
}
return self;
}
- (int)waitForUpdate {
if (buffer != nil)
return 0;
int err = [self.tracker waitForUpdate];
if (err < 0)
return err;
CLLocation *location = self.tracker.latest;
NSString *output = [NSString stringWithFormat:@"%+f,%+f\n", location.coordinate.latitude, location.coordinate.longitude];
buffer = [output dataUsingEncoding:NSUTF8StringEncoding];
bufferOffset = 0;
return 0;
}
- (ssize_t)readIntoBuffer:(void *)buf size:(size_t)size {
@synchronized (self) {
int err = [self waitForUpdate];
if (err < 0)
return err;
size_t remaining = buffer.length - bufferOffset;
if (size > remaining)
size = remaining;
[buffer getBytes:buf range:NSMakeRange(bufferOffset, size)];
bufferOffset += size;
if (bufferOffset == buffer.length)
buffer = nil;
return size;
}
}
@end
static int location_open(int major, int minor, struct fd *fd) {
fd->data = (void *) CFBridgingRetain([LocationFile new]);
return 0;
}
static int location_close(struct fd *fd) {
CFBridgingRelease(fd->data);
return 0;
}
static ssize_t location_read(struct fd *fd, void *buf, size_t size) {
LocationFile *file = (__bridge LocationFile *) fd->data;
return [file readIntoBuffer:buf size:size];
}
const struct dev_ops location_dev = {
.open = location_open,
.fd.close = location_close,
.fd.read = location_read,
};