Add location tracking device

Supports tracking your location in the background, which has the nice
side effect of keeping everything in app running in the background.

#249
This commit is contained in:
Theodore Dubois
2019-10-20 20:48:29 -07:00
parent 30d2b4dda3
commit 16b11d479b
9 changed files with 482 additions and 9 deletions
+7 -1
View File
@@ -9,7 +9,8 @@
#include <arpa/inet.h>
#include <netdb.h>
#import "AppDelegate.h"
#import "Pasteboard.h"
#import "PasteboardDevice.h"
#import "LocationDevice.h"
#import "TerminalViewController.h"
#import "UserPreferences.h"
#include "kernel/init.h"
@@ -116,6 +117,11 @@ static void ios_handle_die(const char *msg) {
return err;
}
generic_mknod("/dev/clipboard", S_IFCHR|0666, dev_make(DYN_DEV_MAJOR, DEV_CLIPBOARD_MINOR));
err = dyn_dev_register(&location_dev, DEV_CHAR, DYN_DEV_MAJOR, DEV_LOCATION_MINOR);
if (err != 0)
return err;
generic_mknod("/dev/location", S_IFCHR|0666, dev_make(DYN_DEV_MAJOR, DEV_LOCATION_MINOR));
do_mount(&procfs, "proc", "/proc", 0);
do_mount(&devptsfs, "devpts", "/dev/pts", 0);
+10 -2
View File
@@ -20,12 +20,16 @@
<string>60</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UIBackgroundModes</key>
<array>
<string>location</string>
</array>
<key>UIFileSharingEnabled</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIFileSharingEnabled</key>
<true/>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
@@ -46,5 +50,9 @@
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>A program running in iSH wants to track your location in the background.</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>A program running in iSH wants to track your location.</string>
</dict>
</plist>
+8
View File
@@ -0,0 +1,8 @@
//
// LocationDevice.h
// iSH
//
// Created by Theodore Dubois on 10/20/19.
//
extern struct dev_ops location_dev;
+174
View File
@@ -0,0 +1,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,
};
+2
View File
@@ -0,0 +1,2 @@
// Pasteboard is implementation of /dev/clipboard device
extern struct dev_ops clipboard_dev;
+252
View File
@@ -0,0 +1,252 @@
#include <string.h>
#import <UIKit/UIKit.h>
#include "fs/poll.h"
#include "fs/dyndev.h"
#include "kernel/errno.h"
#include "debug.h"
/**
* buffer is dynamically sized buffer of size buffer_cap
* All writes go to it, and buffer_len is length of data held in buffer
*/
// Prepare for fd separation
#define fd_priv(fd) fd->clipboard
typedef struct fd clip_fd;
#define INITIAL_BUFFER_CAP 1024
// 8MB: https://stackoverflow.com/a/3523175
#define MAXIMAL_BUFFER_CAP 8*1024*1024
// If pasteboard contents were changed since file was opened,
// all read operations on in return error
static int check_read_generation(clip_fd *fd) {
UIPasteboard *pb = UIPasteboard.generalPasteboard;
uint64_t pb_gen = (uint64_t) pb.changeCount;
uint64_t fd_gen = fd_priv(fd).generation;
if (fd_gen == 0 || fd->offset == 0) {
fd_priv(fd).generation = pb_gen;
} else if (fd_gen != pb_gen) {
return -1;
}
return 0;
}
static const char *get_data(clip_fd *fd, size_t *len) {
if (fd_priv(fd).buffer != NULL) {
*len = fd_priv(fd).buffer_len;
return fd_priv(fd).buffer;
}
if (check_read_generation(fd) != 0) {
return NULL;
}
NSString __autoreleasing *contents = UIPasteboard.generalPasteboard.string;
*len = contents.length;
return contents.UTF8String;
}
static int realloc_to_fit(clip_fd* fd, size_t fit_len) {
// (Re)allocate buffer if there's not enough space to fit fit_len
if (fit_len <= fd_priv(fd).buffer_cap) {
return 0;
}
if (fit_len > MAXIMAL_BUFFER_CAP) {
return 1;
}
size_t size = fd_priv(fd).buffer_cap * 2;
if (size == 0) {
size = INITIAL_BUFFER_CAP;
}
while (size < fit_len) size *= 2;
void *new_buf = realloc(fd_priv(fd).buffer, size);
if (new_buf == NULL) {
return 1;
}
fd_priv(fd).buffer = new_buf;
fd_priv(fd).buffer_cap = size;
return 0;
}
// buffer => UIPasteboard
static int clipboard_write_sync(clip_fd *fd) {
if (fd_priv(fd).buffer == NULL) {
return 0;
}
void *data = fd_priv(fd).buffer;
size_t len = fd_priv(fd).buffer_len;
// FIXME(stek29): This logs "Returning local object of class NSString"
// and I have no idea why (or how to fix it)
UIPasteboard.generalPasteboard.string = [[NSString alloc]
initWithBytes:data
length:len
encoding:NSUTF8StringEncoding];
// Reset generation since we've just updated UIPasteboard
// note: offset doesn't change
fd_priv(fd).generation = 0;
return 0;
}
// UIPasteboard => buffer, return len
static ssize_t clipboard_read_sync(clip_fd *fd) {
if (fd_priv(fd).buffer != NULL) {
free(fd_priv(fd).buffer);
fd_priv(fd).buffer = NULL;
fd_priv(fd).buffer_cap = 0;
fd_priv(fd).buffer_len = 0;
}
@autoreleasepool {
size_t len;
const void *data = get_data(fd, &len);
// Make sure size is still INITIAL_BUFFER_CAP based
if (realloc_to_fit(fd, len)) {
return _ENOMEM;
}
memcpy(fd_priv(fd).buffer, data, len);
fd_priv(fd).buffer_len = len;
return len;
}
}
static int clipboard_poll(clip_fd *fd) {
return POLL_READ | POLL_WRITE;
}
static ssize_t clipboard_read(clip_fd *fd, void *buf, size_t bufsize) {
@autoreleasepool {
size_t length = 0;
const char *data = get_data(fd, &length);
if (data == NULL) {
return _EIO;
}
size_t remaining = length - fd->offset;
if ((size_t) fd->offset > length)
remaining = 0;
size_t n = bufsize;
if (n > remaining)
n = remaining;
if (n != 0) {
memcpy(buf, data + fd->offset, n);
fd->offset += n;
}
return n;
}
}
static ssize_t clipboard_write(clip_fd *fd, const void *buf, size_t bufsize) {
size_t new_len = fd->offset + bufsize;
size_t old_len = fd_priv(fd).buffer_len;
if (old_len > new_len) {
new_len = old_len;
}
if (realloc_to_fit(fd, new_len)) {
return _ENOMEM;
}
// fill the hole between new offset and old len
if (old_len < fd->offset) {
memset(fd_priv(fd).buffer + old_len, '\0', fd->offset - old_len);
}
memcpy(fd_priv(fd).buffer + fd->offset, buf, bufsize);
fd->offset += bufsize;
fd_priv(fd).buffer_len = new_len;
return bufsize;
}
static off_t_ clipboard_lseek(clip_fd *fd, off_t_ off, int whence) {
off_t_ old_off = fd->offset;
size_t length = 0;
if (whence != LSEEK_SET || off != 0) {
@autoreleasepool {
if (get_data(fd, &length) == NULL) {
return _EIO;
}
}
}
switch (whence) {
case LSEEK_SET:
fd->offset = off;
break;
case LSEEK_CUR:
fd->offset += off;
break;
case LSEEK_END:
fd->offset = length + off;
break;
default:
return _EINVAL;
}
if (fd->offset < 0) {
fd->offset = old_off;
return _EINVAL;
}
return fd->offset;
}
static int clipboard_close(clip_fd *fd) {
clipboard_write_sync(fd);
if (fd_priv(fd).buffer != NULL) {
free(fd_priv(fd).buffer);
}
return 0;
}
static int clipboard_open(int major, int minor, clip_fd *fd) {
// Zero fd_priv data
memset(&fd_priv(fd), 0, sizeof(fd_priv(fd)));
// If O_TRUNC is not set, initialize buffer with current pasteboard contents
if (!(fd->flags & O_TRUNC_)) {
ssize_t len = clipboard_read_sync(fd);
if (len < 0) {
return (int) len;
}
if (fd->flags & O_APPEND_) {
fd->offset = (size_t) len;
}
}
return 0;
}
struct dev_ops clipboard_dev = {
.open = clipboard_open,
.fd.read = clipboard_read,
.fd.write = clipboard_write,
.fd.lseek = clipboard_lseek,
.fd.poll = clipboard_poll,
.fd.close = clipboard_close,
.fd.fsync = clipboard_write_sync,
};
+2
View File
@@ -38,5 +38,7 @@
// /dev/clipboard
#define DEV_CLIPBOARD_MINOR 0
// /dev/gps
#define DEV_LOCATION_MINOR 1
#endif
+3
View File
@@ -72,6 +72,9 @@ struct fd {
// length of actual data stored in the buffer
size_t buffer_len;
} clipboard;
// can fit anything in here
void *data;
};
// fs data
union {
+24 -6
View File
@@ -7,12 +7,14 @@
objects = {
/* Begin PBXBuildFile section */
650B337422EA235C00B4C03E /* Pasteboard.m in Sources */ = {isa = PBXBuildFile; fileRef = 650B337322EA235C00B4C03E /* Pasteboard.m */; };
650B337422EA235C00B4C03E /* PasteboardDevice.m in Sources */ = {isa = PBXBuildFile; fileRef = 650B337322EA235C00B4C03E /* PasteboardDevice.m */; };
8632A7BF219A59FB00F02325 /* UserPreferences.m in Sources */ = {isa = PBXBuildFile; fileRef = 8632A7BE219A59FB00F02325 /* UserPreferences.m */; };
9A28E4EA219A8B670073D200 /* AboutAppearanceViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9A28E4E9219A8B670073D200 /* AboutAppearanceViewController.m */; };
BB0FC5921F980A6C00803272 /* Terminal.m in Sources */ = {isa = PBXBuildFile; fileRef = BB0FC5911F980A6B00803272 /* Terminal.m */; };
BB13F7EA200ADCED003D1C4D /* libish.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BB13F7DC200AD81D003D1C4D /* libish.a */; };
BB1D9D93234A8FE100F364E8 /* AboutNavigationController.m in Sources */ = {isa = PBXBuildFile; fileRef = BB1D9D92234A8FE100F364E8 /* AboutNavigationController.m */; };
BB235534235D488500139E00 /* LocationDevice.m in Sources */ = {isa = PBXBuildFile; fileRef = BB235533235D488400139E00 /* LocationDevice.m */; };
BB235537235D49B300139E00 /* CoreLocation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BB235536235D49B300139E00 /* CoreLocation.framework */; };
BB23F58D231E1D1400585522 /* ScrollbarView.m in Sources */ = {isa = PBXBuildFile; fileRef = BB23F58C231E1D1400585522 /* ScrollbarView.m */; };
BB2B4DAC231D94C300CB578B /* hterm_all.js in Resources */ = {isa = PBXBuildFile; fileRef = BB2B4DAB231D94C300CB578B /* hterm_all.js */; };
BB2B4DAD231D998300CB578B /* term.js in Resources */ = {isa = PBXBuildFile; fileRef = BB4A539C1FAA490C00A72ACE /* term.js */; };
@@ -100,8 +102,8 @@
650B336822E9F0B500B4C03E /* root.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = root.c; sourceTree = "<group>"; };
650B336C22EA052400B4C03E /* dyndev.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = dyndev.c; sourceTree = "<group>"; };
650B336D22EA052400B4C03E /* dyndev.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dyndev.h; sourceTree = "<group>"; };
650B337222EA235C00B4C03E /* Pasteboard.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Pasteboard.h; sourceTree = "<group>"; };
650B337322EA235C00B4C03E /* Pasteboard.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Pasteboard.m; sourceTree = "<group>"; };
650B337222EA235C00B4C03E /* PasteboardDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PasteboardDevice.h; sourceTree = "<group>"; };
650B337322EA235C00B4C03E /* PasteboardDevice.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PasteboardDevice.m; sourceTree = "<group>"; };
650B337522EA728B00B4C03E /* devices.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = devices.h; sourceTree = "<group>"; };
8632A7BD219A59FB00F02325 /* UserPreferences.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = UserPreferences.h; sourceTree = "<group>"; };
8632A7BE219A59FB00F02325 /* UserPreferences.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = UserPreferences.m; sourceTree = "<group>"; };
@@ -115,6 +117,9 @@
BB13F7DC200AD81D003D1C4D /* libish.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libish.a; sourceTree = BUILT_PRODUCTS_DIR; };
BB1D9D91234A8FE100F364E8 /* AboutNavigationController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AboutNavigationController.h; sourceTree = "<group>"; };
BB1D9D92234A8FE100F364E8 /* AboutNavigationController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AboutNavigationController.m; sourceTree = "<group>"; };
BB235533235D488400139E00 /* LocationDevice.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LocationDevice.m; sourceTree = "<group>"; };
BB235535235D489400139E00 /* LocationDevice.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LocationDevice.h; sourceTree = "<group>"; };
BB235536235D49B300139E00 /* CoreLocation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreLocation.framework; path = System/Library/Frameworks/CoreLocation.framework; sourceTree = SDKROOT; };
BB23F58B231E1D1400585522 /* ScrollbarView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ScrollbarView.h; sourceTree = "<group>"; };
BB23F58C231E1D1400585522 /* ScrollbarView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ScrollbarView.m; sourceTree = "<group>"; };
BB2B4DAB231D94C300CB578B /* hterm_all.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; name = hterm_all.js; path = libapps/hterm/dist/js/hterm_all.js; sourceTree = "<group>"; };
@@ -294,6 +299,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
BB235537235D49B300139E00 /* CoreLocation.framework in Frameworks */,
BB6DB261216435340047A611 /* libiconv.tbd in Frameworks */,
BBFB55662158644C00DFE6DE /* libresolv.tbd in Frameworks */,
BB13F7EA200ADCED003D1C4D /* libish.a in Frameworks */,
@@ -329,6 +335,17 @@
name = Scripts;
sourceTree = "<group>";
};
BB235532235D472F00139E00 /* Devices */ = {
isa = PBXGroup;
children = (
650B337222EA235C00B4C03E /* PasteboardDevice.h */,
650B337322EA235C00B4C03E /* PasteboardDevice.m */,
BB235535235D489400139E00 /* LocationDevice.h */,
BB235533235D488400139E00 /* LocationDevice.m */,
);
name = Devices;
sourceTree = "<group>";
};
BB2D71072354244700A10D1E /* platform */ = {
isa = PBXGroup;
children = (
@@ -407,8 +424,7 @@
BB23F58B231E1D1400585522 /* ScrollbarView.h */,
BB23F58C231E1D1400585522 /* ScrollbarView.m */,
BB4A53991FAA40FD00A72ACE /* terminal */,
650B337222EA235C00B4C03E /* Pasteboard.h */,
650B337322EA235C00B4C03E /* Pasteboard.m */,
BB235532235D472F00139E00 /* Devices */,
BB792B591F96D90D00FFB7A4 /* Main.storyboard */,
BBFB557321586C7600DFE6DE /* About */,
BBFB557221586C6600DFE6DE /* Utilities */,
@@ -425,6 +441,7 @@
BB792B7D1F96E32B00FFB7A4 /* Frameworks */ = {
isa = PBXGroup;
children = (
BB235536235D49B300139E00 /* CoreLocation.framework */,
BB6DB260216435330047A611 /* libiconv.tbd */,
BBFB55652158644C00DFE6DE /* libresolv.tbd */,
);
@@ -949,6 +966,7 @@
files = (
9A28E4EA219A8B670073D200 /* AboutAppearanceViewController.m in Sources */,
BB1D9D93234A8FE100F364E8 /* AboutNavigationController.m in Sources */,
BB235534235D488500139E00 /* LocationDevice.m in Sources */,
BB792B581F96D90D00FFB7A4 /* TerminalViewController.m in Sources */,
BB78AB2B1FAD22440013E782 /* TerminalView.m in Sources */,
BB23F58D231E1D1400585522 /* ScrollbarView.m in Sources */,
@@ -961,7 +979,7 @@
BB60F55221573FCA003A4E52 /* BarButton.m in Sources */,
BBFB557C215878C600DFE6DE /* UIApplication+OpenURL.m in Sources */,
BBFB5579215876CD00DFE6DE /* AboutViewController.m in Sources */,
650B337422EA235C00B4C03E /* Pasteboard.m in Sources */,
650B337422EA235C00B4C03E /* PasteboardDevice.m in Sources */,
BBFB557121586C4800DFE6DE /* UIViewController+Back.m in Sources */,
BBFB558021587B6800DFE6DE /* ArrowBarButton.m in Sources */,
BB792B551F96D90D00FFB7A4 /* AppDelegate.m in Sources */,