Merge branch 'master' into deva/semver
diff --git a/README.md b/README.md
index 1376e4f..e712b0e 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@
 
 * Mac OS X. Tested on 10.11 El Capitan, 10.12 Sierra, iOS 9.0 and iOS 10.0
 * You need to have a valid iOS Development certificate installed.
-* Xcode 7 or greater should be installed (**NOT** Command Line Tools!)
+* Xcode 7 or greater should be installed (**NOT** just Command Line Tools!)
 
 ## Roadmap
 
@@ -65,6 +65,7 @@
         -c, --detect                 only detect if the device is connected
         -b, --bundle <bundle.app>    the path to the app bundle to be installed
         -a, --args <args>            command line arguments to pass to the app when launching it
+        -s, --envs <envs>            environment variables, space separated key-value pairs, to pass to the app when launching it
         -t, --timeout <timeout>      number of seconds to wait for a device to be connected
         -u, --unbuffered             don't buffer stdout
         -n, --nostart                do not start the app when debugging
@@ -76,9 +77,9 @@
         -r, --uninstall              uninstall the app before install (do not use with -m; app cache and data are cleared) 
         -9, --uninstall_only         uninstall the app ONLY. Use only with -1 <bundle_id> 
         -1, --bundle_id <bundle id>  specify bundle id for list and upload
-        -l, --list                   list files
+        -l, --list[=<dir>]           list all app files or the specified directory
         -o, --upload <file>          upload file
-        -w, --download               download app tree
+        -w, --download[=<path>]      download app tree or the specified file/directory
         -2, --to <target pathname>   use together with up/download file/tree. specify target
         -D, --mkdir <dir>            make directory on device
         -R, --rm <path>              remove file or directory on device (directories must be empty)
@@ -86,6 +87,7 @@
         -e, --exists                 check if the app with given bundle_id is installed or not 
         -B, --list_bundle_id         list bundle_id 
         -W, --no-wifi                ignore wifi devices
+        -O, --output <file>          write stdout and stderr to this file
         --detect_deadlocks <sec>     start printing backtraces for all threads periodically after specific amount of seconds
 
 ## Examples
@@ -95,6 +97,9 @@
     // deploy and debug your app to a connected device
     ios-deploy --debug --bundle my.app
 
+    // deploy, debug and pass environment variables to a connected device
+    ios-deploy --debug --envs DYLD_PRINT_STATISTICS=1 --bundle my.app
+
     // deploy and debug your app to a connected device, skipping any wi-fi connection (use USB)
     ios-deploy --debug --bundle my.app --no-wifi
 
diff --git a/RELEASING.md b/RELEASING.md
new file mode 100644
index 0000000..f322b13
--- /dev/null
+++ b/RELEASING.md
@@ -0,0 +1,20 @@
+## 1. Increment a version
+
+```
+export PKG_VER=YOUR_VERSION_HERE
+./increment_version.sh $PKG_VER
+git commit -m "Incremented version to $PKG_VER" package.json src/ios-deploy/version.h
+```
+
+## 2. Tag a version
+
+```
+git tag $PKG_VER
+```
+
+## 3. Push version and tag
+
+```
+git push origin master
+git push origin $PKG_VER
+```
diff --git a/demo/Makefile b/demo/Makefile
index 319e9f8..43fae0c 100644
--- a/demo/Makefile
+++ b/demo/Makefile
@@ -1,7 +1,5 @@
-IOS_SDK_VERSION = 9.1
-
-IOS_CC = gcc -ObjC
-IOS_SDK = $(shell xcode-select --print-path)/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS$(IOS_SDK_VERSION).sdk
+IOS_CC = clang -ObjC
+IOS_SDK = $(shell xcrun --sdk iphoneos --show-sdk-path)
 
 all: clean demo.app
 
diff --git a/increment_version.sh b/increment_version.sh
new file mode 100755
index 0000000..70c1cf6
--- /dev/null
+++ b/increment_version.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+if [ -z "$1" ] 
+then
+  echo "Usage: $0 [version_to_set]"
+  exit 1
+fi
+
+echo "\"$1\"" > src/ios-deploy/version.h
+npm version --no-git-tag-version $1
diff --git a/ios-deploy.xcodeproj/project.pbxproj b/ios-deploy.xcodeproj/project.pbxproj
index 834978f..e549251 100644
--- a/ios-deploy.xcodeproj/project.pbxproj
+++ b/ios-deploy.xcodeproj/project.pbxproj
@@ -180,7 +180,6 @@
 				C0CD3D981F59D20100F954DB /* ShellScript */,
 				7E70898B1B587BF3004D23AA /* Frameworks */,
 				7EDCC3CC1C45DC89002F9851 /* Sources */,
-				C0CD3D9B1F59DA8300F954DB /* Run Script */,
 			);
 			buildRules = (
 			);
@@ -251,7 +250,7 @@
 			};
 			buildConfigurationList = 7E7089891B587BF3004D23AA /* Build configuration list for PBXProject "ios-deploy" */;
 			compatibilityVersion = "Xcode 3.2";
-			developmentRegion = English;
+			developmentRegion = en;
 			hasScannedForEncodings = 0;
 			knownRegions = (
 				en,
@@ -330,21 +329,7 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			shellPath = /bin/sh;
-			shellScript = "mkdir -p \"${PROJECT_DIR}/_Frameworks\"\nrsync -r /System/Library/PrivateFrameworks/MobileDevice.framework \"${PROJECT_DIR}/_Frameworks\" --exclude=XPCServices --links\n";
-		};
-		C0CD3D9B1F59DA8300F954DB /* Run Script */ = {
-			isa = PBXShellScriptBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-			);
-			inputPaths = (
-			);
-			name = "Run Script";
-			outputPaths = (
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-			shellPath = /bin/sh;
-			shellScript = "rm -rf \"${PROJECT_DIR}/_Frameworks\"\n";
+			shellScript = "mkdir -p \"${PROJECT_DIR}/_Frameworks\"\n\n# copy over MobileDevice.framework from its location\nMD_FRAMEWORK_LOCATION=/System/Library/PrivateFrameworks/MobileDevice.framework\nif [ -d \"$MD_FRAMEWORK_LOCATION\" ]; then\n    rsync -r $MD_FRAMEWORK_LOCATION \"${PROJECT_DIR}/_Frameworks\" --exclude=XPCServices --links\nfi\n\n# check if this new location exists, if it does, copy it instead\nNEW_MD_FRAMEWORK_LOCATION=/Library/Apple/System/Library/PrivateFrameworks/MobileDevice.framework\nif [ -d \"$NEW_MD_FRAMEWORK_LOCATION\" ]; then\nrsync -r $NEW_MD_FRAMEWORK_LOCATION \"${PROJECT_DIR}/_Frameworks\" --exclude=XPCServices --links\nfi\n\n";
 		};
 /* End PBXShellScriptBuildPhase section */
 
diff --git a/package.json b/package.json
index c937352..70a7c96 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "ios-deploy",
-  "version": "1.9.4",
+  "version": "1.10.0-beta.1",
   "os": [
     "darwin"
   ],
@@ -26,7 +26,8 @@
     "build-test": "npm run pycompile && xcodebuild -target ios-deploy-lib && xcodebuild test -scheme ios-deploy-tests",
     "eslint": "eslint src/scripts/*.js",
     "test": "npm run eslint && npm run build-test",
-    "pycompile": "python -m py_compile src/scripts/*.py"
+    "pycompile": "python -m py_compile src/scripts/*.py",
+    "postversion": "echo \\\"$npm_package_version\\\" > src/ios-deploy/version.h"
   },
   "keywords": [
     "ios-deploy",
diff --git a/src/ios-deploy/MobileDevice.h b/src/ios-deploy/MobileDevice.h
index 89b7d5a..2646bcd 100644
--- a/src/ios-deploy/MobileDevice.h
+++ b/src/ios-deploy/MobileDevice.h
@@ -66,6 +66,7 @@
 typedef unsigned int afc_error_t;
 typedef unsigned int usbmux_error_t;
 typedef unsigned int service_conn_t;
+typedef service_conn_t * ServiceConnRef;
 
 struct am_recovery_device;
 
@@ -142,6 +143,8 @@
     unsigned int context;           /* 40 */
 } __attribute__ ((packed)) afc_connection;
 
+typedef struct afc_connection * AFCConnectionRef;
+
 typedef struct afc_directory {
     unsigned char unknown[0];   /* size unknown */
 } __attribute__ ((packed)) afc_directory;
@@ -258,10 +261,10 @@
  */
 
 mach_error_t AMDeviceStartService(struct am_device *device, CFStringRef 
-    service_name, service_conn_t *handle, unsigned int *
+    service_name, ServiceConnRef handle, unsigned int *
     unknown);
 
-mach_error_t AMDeviceStartHouseArrestService(struct am_device *device, CFStringRef identifier, void *unknown, service_conn_t *handle, unsigned int *what);
+mach_error_t AMDeviceStartHouseArrestService(struct am_device *device, CFStringRef identifier, void *unknown, ServiceConnRef handle, unsigned int *what);
 
 /* Stops a session. You should do this before accessing services.
  *
@@ -281,10 +284,10 @@
  */
 
 afc_error_t AFCConnectionOpen(service_conn_t handle, unsigned int io_timeout,
-    struct afc_connection **conn);
+    AFCConnectionRef *conn);
 
 /* Pass in a pointer to an afc_device_info structure. It will be filled. */
-afc_error_t AFCDeviceInfoOpen(afc_connection *conn, struct
+afc_error_t AFCDeviceInfoOpen(AFCConnectionRef conn, struct
     afc_dictionary **info);
 
 /* Turns debug mode on if the environment variable AFCDEBUG is set to a numeric
@@ -300,7 +303,7 @@
  *      MDERR_OK                if successful
  */
 
-afc_error_t AFCDirectoryOpen(afc_connection *conn, const char *path,
+afc_error_t AFCDirectoryOpen(AFCConnectionRef conn, const char *path,
                              struct afc_directory **dir);
 
 /* Acquires the next entry in a directory previously opened with
@@ -313,30 +316,30 @@
  *      MDERR_OK                if successful, even if no entries remain
  */
 
-afc_error_t AFCDirectoryRead(afc_connection *conn/*unsigned int unused*/, struct afc_directory *dir,
+afc_error_t AFCDirectoryRead(AFCConnectionRef conn/*unsigned int unused*/, struct afc_directory *dir,
     char **dirent);
 
-afc_error_t AFCDirectoryClose(afc_connection *conn, struct afc_directory *dir);
-afc_error_t AFCDirectoryCreate(afc_connection *conn, const char *dirname);
-afc_error_t AFCRemovePath(afc_connection *conn, const char *dirname);
-afc_error_t AFCRenamePath(afc_connection *conn, const char *from, const char *to);
-afc_error_t AFCLinkPath(afc_connection *conn, long long int linktype, const char *target, const char *linkname);
+afc_error_t AFCDirectoryClose(AFCConnectionRef conn, struct afc_directory *dir);
+afc_error_t AFCDirectoryCreate(AFCConnectionRef conn, const char *dirname);
+afc_error_t AFCRemovePath(AFCConnectionRef conn, const char *dirname);
+afc_error_t AFCRenamePath(AFCConnectionRef conn, const char *from, const char *to);
+afc_error_t AFCLinkPath(AFCConnectionRef conn, long long int linktype, const char *target, const char *linkname);
 
 /* Returns the context field of the given AFC connection. */
-unsigned int AFCConnectionGetContext(afc_connection *conn);
+unsigned int AFCConnectionGetContext(AFCConnectionRef conn);
 
 /* Returns the fs_block_size field of the given AFC connection. */
-unsigned int AFCConnectionGetFSBlockSize(afc_connection *conn);
+unsigned int AFCConnectionGetFSBlockSize(AFCConnectionRef conn);
 
 /* Returns the io_timeout field of the given AFC connection. In iTunes this is
  * 0. */
-unsigned int AFCConnectionGetIOTimeout(afc_connection *conn);
+unsigned int AFCConnectionGetIOTimeout(AFCConnectionRef conn);
 
 /* Returns the sock_block_size field of the given AFC connection. */
-unsigned int AFCConnectionGetSocketBlockSize(afc_connection *conn);
+unsigned int AFCConnectionGetSocketBlockSize(AFCConnectionRef conn);
 
 /* Closes the given AFC connection. */
-afc_error_t AFCConnectionClose(afc_connection *conn);
+afc_error_t AFCConnectionClose(AFCConnectionRef conn);
 
 /* Registers for device notifications related to the restore process. unknown0
  * is zero when iTunes calls this. In iTunes,
@@ -392,19 +395,19 @@
  * ------------------------------------------------------------------------- */
 
 /* mode 2 = read, mode 3 = write */
-afc_error_t AFCFileRefOpen(afc_connection *conn, const char *path,
+afc_error_t AFCFileRefOpen(AFCConnectionRef conn, const char *path,
     unsigned long long mode, afc_file_ref *ref);
-afc_error_t AFCFileRefSeek(afc_connection *conn, afc_file_ref ref,
+afc_error_t AFCFileRefSeek(AFCConnectionRef conn, afc_file_ref ref,
     unsigned long long offset1, unsigned long long offset2);
-afc_error_t AFCFileRefRead(afc_connection *conn, afc_file_ref ref,
+afc_error_t AFCFileRefRead(AFCConnectionRef conn, afc_file_ref ref,
     void *buf, size_t *len);
-afc_error_t AFCFileRefSetFileSize(afc_connection *conn, afc_file_ref ref,
+afc_error_t AFCFileRefSetFileSize(AFCConnectionRef conn, afc_file_ref ref,
     unsigned long long offset);
-afc_error_t AFCFileRefWrite(afc_connection *conn, afc_file_ref ref,
+afc_error_t AFCFileRefWrite(AFCConnectionRef conn, afc_file_ref ref,
     const void *buf, size_t len);
-afc_error_t AFCFileRefClose(afc_connection *conn, afc_file_ref ref);
+afc_error_t AFCFileRefClose(AFCConnectionRef conn, afc_file_ref ref);
 
-afc_error_t AFCFileInfoOpen(afc_connection *conn, const char *path, struct
+afc_error_t AFCFileInfoOpen(AFCConnectionRef conn, const char *path, struct
     afc_dictionary **info);
 afc_error_t AFCKeyValueRead(struct afc_dictionary *dict, char **key, char **
     val);
@@ -429,7 +432,7 @@
 mach_error_t AMDeviceDisconnect(struct am_device *device);
 mach_error_t AMDeviceRetain(struct am_device *device);
 mach_error_t AMDeviceRelease(struct am_device *device);
-CFStringRef AMDeviceCopyValue(struct am_device *device, unsigned int, CFStringRef cfstring);
+CFStringRef AMDeviceCopyValue(struct am_device *device, void*, CFStringRef cfstring);
 CFStringRef AMDeviceCopyDeviceIdentifier(struct am_device *device);
 
 typedef void (*notify_callback)(CFStringRef notification, void *data);
diff --git a/src/ios-deploy/device_db.h b/src/ios-deploy/device_db.h
index 489cf36..3758138 100644
--- a/src/ios-deploy/device_db.h
+++ b/src/ios-deploy/device_db.h
@@ -31,6 +31,7 @@
                           ADD_DEVICE("N78AP",  "iPod Touch 5G",              "iphoneos", "armv7"),
                           ADD_DEVICE("N78AAP", "iPod Touch 5G",              "iphoneos", "armv7"),
                           ADD_DEVICE("N102AP", "iPod Touch 6G",              "iphoneos", "arm64"),
+                          ADD_DEVICE("N112AP", "iPod Touch 7G",              "iphoneos", "arm64"),
 
                           // iPad
 
@@ -45,6 +46,7 @@
                           ADD_DEVICE("P101AP", "iPad 4",                     "iphoneos", "armv7s"),
                           ADD_DEVICE("P102AP", "iPad 4 (GSM)",               "iphoneos", "armv7s"),
                           ADD_DEVICE("P103AP", "iPad 4 (CDMA)",              "iphoneos", "armv7s"),
+                          ADD_DEVICE("J71bAP", "iPad 6",                     "iphoneos", "arm64"),
                           ADD_DEVICE("J71AP",  "iPad Air",                   "iphoneos", "arm64"),
                           ADD_DEVICE("J72AP",  "iPad Air (GSM)",             "iphoneos", "arm64"),
                           ADD_DEVICE("J73AP",  "iPad Air (CDMA)",            "iphoneos", "arm64"),
@@ -55,6 +57,10 @@
                           ADD_DEVICE("J71tAP", "iPad (2017)",                "iphoneos", "arm64"),
                           ADD_DEVICE("J72sAP", "iPad (2017)",                "iphoneos", "arm64"),
                           ADD_DEVICE("J72tAP", "iPad (2017)",                "iphoneos", "arm64"),
+                          ADD_DEVICE("J71bAP", "iPad (2018)",                "iphoneos", "arm64"),
+                          ADD_DEVICE("J72bAP", "iPad (2018)",                "iphoneos", "arm64"),
+                          ADD_DEVICE("J217AP", "iPad Air 3",                 "iphoneos", "arm64e"),
+                          ADD_DEVICE("J218AP", "iPad Air 3 (Cellular)",      "iphoneos", "arm64e"),
 
                           // iPad Pro
 
@@ -66,6 +72,22 @@
                           ADD_DEVICE("J128AP",  "iPad Pro (9.7\")",          "iphoneos", "arm64"),
                           ADD_DEVICE("J207AP",  "iPad Pro (10.5\")",         "iphoneos", "arm64"),
                           ADD_DEVICE("J208AP",  "iPad Pro (10.5\")",         "iphoneos", "arm64"),
+                          ADD_DEVICE("J317AP",  "iPad Pro (11\")",           "iphoneos", "arm64e"),
+                          ADD_DEVICE("J317xAP", "iPad Pro (11\")",           "iphoneos", "arm64e"),
+                          ADD_DEVICE("J318AP",  "iPad Pro (11\")",           "iphoneos", "arm64e"),
+                          ADD_DEVICE("J318xAP", "iPad Pro (11\")",           "iphoneos", "arm64e"),
+                          ADD_DEVICE("J318AP",  "iPad Pro (11\")",           "iphoneos", "arm64e"),
+                          ADD_DEVICE("J318xAP", "iPad Pro (11\")",           "iphoneos", "arm64e"),
+                          ADD_DEVICE("J318AP",  "iPad Pro (11\")",           "iphoneos", "arm64e"),
+                          ADD_DEVICE("J318xAP", "iPad Pro (11\")",           "iphoneos", "arm64e"),
+                          ADD_DEVICE("J320AP",  "iPad Pro 3G (12.9\")",      "iphoneos", "arm64e"),
+                          ADD_DEVICE("J320xAP", "iPad Pro 3G (12.9\")",      "iphoneos", "arm64e"),
+                          ADD_DEVICE("J321AP",  "iPad Pro 3G (12.9\")",      "iphoneos", "arm64e"),
+                          ADD_DEVICE("J321xAP", "iPad Pro 3G (12.9\")",      "iphoneos", "arm64e"),
+                          ADD_DEVICE("J321AP",  "iPad Pro 3G (12.9\")",      "iphoneos", "arm64e"),
+                          ADD_DEVICE("J321xAP", "iPad Pro 3G (12.9\")",      "iphoneos", "arm64e"),
+                          ADD_DEVICE("J321AP",  "iPad Pro 3G (12.9\")",      "iphoneos", "arm64e"),
+                          ADD_DEVICE("J321xAP", "iPad Pro 3G (12.9\")",      "iphoneos", "arm64e"),
 
                           // iPad Mini
 
@@ -80,6 +102,8 @@
                           ADD_DEVICE("J87MAP", "iPad mini 3 (CDMA)",         "iphoneos", "arm64"),
                           ADD_DEVICE("J96AP",  "iPad mini 4",                "iphoneos", "arm64"),
                           ADD_DEVICE("J97AP",  "iPad mini 4 (GSM)",          "iphoneos", "arm64"),
+                          ADD_DEVICE("J210AP", "iPad mini 5",                "iphoneos", "arm64e"),
+                          ADD_DEVICE("J211AP", "iPad mini 5 (Cellular)",     "iphoneos", "arm64e"),
 
                           // iPhone
 
@@ -108,6 +132,19 @@
                           ADD_DEVICE("D101AP", "iPhone 7",                   "iphoneos", "arm64"),
                           ADD_DEVICE("D11AP",  "iPhone 7 Plus",              "iphoneos", "arm64"),
                           ADD_DEVICE("D111AP", "iPhone 7 Plus",              "iphoneos", "arm64"),
+                          ADD_DEVICE("D20AP",  "iPhone 8",                   "iphoneos", "arm64"),
+                          ADD_DEVICE("D20AAP", "iPhone 8",                   "iphoneos", "arm64"),
+                          ADD_DEVICE("D201AP", "iPhone 8",                   "iphoneos", "arm64"),
+                          ADD_DEVICE("D201AAP","iPhone 8",                   "iphoneos", "arm64"),
+                          ADD_DEVICE("D21AP",  "iPhone 8 Plus",              "iphoneos", "arm64"),
+                          ADD_DEVICE("D21AAP", "iPhone 8 Plus",              "iphoneos", "arm64"),
+                          ADD_DEVICE("D211AP", "iPhone 8 Plus",              "iphoneos", "arm64"),
+                          ADD_DEVICE("D211AAP","iPhone 8 Plus",              "iphoneos", "arm64"),
+                          ADD_DEVICE("D22AP",  "iPhone X",                   "iphoneos", "arm64"),
+                          ADD_DEVICE("D221AP", "iPhone X",                   "iphoneos", "arm64"),
+                          ADD_DEVICE("N841AP", "iPhone XR",                  "iphoneos", "arm64e"),
+                          ADD_DEVICE("D321AP", "iPhone XS",                  "iphoneos", "arm64e"),
+                          ADD_DEVICE("D331pAP","iPhone XS Max",              "iphoneos", "arm64e"),
 
                           // Apple TV
 
@@ -115,4 +152,5 @@
                           ADD_DEVICE("J33AP",  "Apple TV 3G",                "appletvos", "armv7"),
                           ADD_DEVICE("J33IAP", "Apple TV 3.1G",              "appletvos", "armv7"),
                           ADD_DEVICE("J42dAP", "Apple TV 4G",                "appletvos", "arm64"),
+                          ADD_DEVICE("J105aAP","Apple TV 4K",                "appletvos", "arm64"),
                           };
diff --git a/src/ios-deploy/ios-deploy.m b/src/ios-deploy/ios-deploy.m
index 3efd346..29c2c33 100644
--- a/src/ios-deploy/ios-deploy.m
+++ b/src/ios-deploy/ios-deploy.m
@@ -20,7 +20,7 @@
 #import "device_db.h"
 
 #define PREP_CMDS_PATH @"/tmp/%@/fruitstrap-lldb-prep-cmds-"
-#define LLDB_SHELL @"lldb -s %@"
+#define LLDB_SHELL @"PATH=/usr/bin /usr/bin/lldb -s %@"
 /*
  * Startup script passed to lldb.
  * To see how xcode interacts with lldb, put this into .lldbinit:
@@ -32,6 +32,8 @@
     target create \"{disk_app}\"\n\
     script fruitstrap_device_app=\"{device_app}\"\n\
     script fruitstrap_connect_url=\"connect://127.0.0.1:{device_port}\"\n\
+    script fruitstrap_output_path=\"{output_path}\"\n\
+    script fruitstrap_error_path=\"{error_path}\"\n\
     target modules search-paths add {modules_search_paths_pairs}\n\
     command script import \"{python_file_path}\"\n\
     command script add -f {python_command}.connect_command connect\n\
@@ -66,14 +68,18 @@
     #include "lldb.py.h"
 ;
 
+const char* output_path = NULL;
+const char* error_path = NULL;
 
 typedef struct am_device * AMDeviceRef;
-mach_error_t AMDeviceSecureStartService(struct am_device *device, CFStringRef service_name, unsigned int *unknown, service_conn_t *handle);
+mach_error_t AMDeviceSecureStartService(AMDeviceRef device, CFStringRef service_name, unsigned int *unknown, ServiceConnRef * handle);
+mach_error_t AMDeviceCreateHouseArrestService(AMDeviceRef device, CFStringRef identifier, void * unknown, AFCConnectionRef * handle);
+CFSocketNativeHandle  AMDServiceConnectionGetSocket(ServiceConnRef con);
 int AMDeviceSecureTransferPath(int zero, AMDeviceRef device, CFURLRef url, CFDictionaryRef options, void *callback, int cbarg);
 int AMDeviceSecureInstallApplication(int zero, AMDeviceRef device, CFURLRef url, CFDictionaryRef options, void *callback, int cbarg);
 int AMDeviceMountImage(AMDeviceRef device, CFStringRef image, CFDictionaryRef options, void *callback, int cbarg);
 mach_error_t AMDeviceLookupApplications(AMDeviceRef device, CFDictionaryRef options, CFDictionaryRef *result);
-int AMDeviceGetInterfaceType(struct am_device *device);
+int AMDeviceGetInterfaceType(AMDeviceRef device);
 
 bool found_device = false, debug = false, verbose = false, unbuffered = false, nostart = false, debugserver_only = false, detect_only = false, install = true, uninstall = false, no_wifi = false;
 bool command_only = false;
@@ -86,9 +92,11 @@
 char *app_path = NULL;
 char *device_id = NULL;
 char *args = NULL;
+char *envs = NULL;
 char *list_root = NULL;
 int _timeout = 0;
 int _detectDeadlockTimeout = 0;
+bool _json_output = false;
 int port = 0;    // 0 means "dynamically assigned"
 CFStringRef last_path = NULL;
 service_conn_t gdbfd;
@@ -97,7 +105,6 @@
 pid_t child = 0;
 // Signal sent from child to parent process when LLDB finishes.
 const int SIGLLDB = SIGUSR1;
-AMDeviceRef best_device_match = NULL;
 NSString* tmpUUID;
 struct am_device_notification *notify;
 
@@ -126,7 +133,9 @@
     NSString* str = [[[NSString alloc] initWithFormat:format arguments:valist] autorelease];
     va_end(valist);
 
-    NSLog(@"[ !! ] %@", str);
+    if (!_json_output) {
+        NSLog(@"[ !! ] %@", str);
+    }
 
     exit(exitcode_error);
 }
@@ -149,14 +158,7 @@
 }
 
 void NSLogOut(NSString* format, ...) {
-    va_list valist;
-    va_start(valist, format);
-    __NSLogOut(format, valist);
-    va_end(valist);
-}
-
-void NSLogVerbose(NSString* format, ...) {
-    if (verbose) {
+    if (!_json_output) {
         va_list valist;
         va_start(valist, format);
         __NSLogOut(format, valist);
@@ -164,6 +166,30 @@
     }
 }
 
+void NSLogVerbose(NSString* format, ...) {
+    if (verbose && !_json_output) {
+        va_list valist;
+        va_start(valist, format);
+        __NSLogOut(format, valist);
+        va_end(valist);
+    }
+}
+
+void NSLogJSON(NSDictionary* jsonDict) {
+    if (_json_output) {
+        NSError *error;
+        NSData *data = [NSJSONSerialization dataWithJSONObject:jsonDict
+                                                           options:NSJSONWritingPrettyPrinted
+                                                             error:&error];
+        assert(data != NULL);
+        NSString *jsonString = @"{\"error\": \"JSON error\"}";
+        if (data) {
+            jsonString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
+        }
+        [jsonString writeToFile:@"/dev/stdout" atomically:NO encoding:NSUTF8StringEncoding error:nil];
+    }
+}
+
 
 BOOL mkdirp(NSString* path) {
     NSError* error = nil;
@@ -313,23 +339,6 @@
     return res;
 }
 
-char * MYCFStringCopyUTF8String(CFStringRef aString) {
-  if (aString == NULL) {
-    return NULL;
-  }
-
-  CFIndex length = CFStringGetLength(aString);
-  CFIndex maxSize =
-  CFStringGetMaximumSizeForEncoding(length,
-                                    kCFStringEncodingUTF8);
-  char *buffer = (char *)malloc(maxSize);
-  if (CFStringGetCString(aString, buffer, maxSize,
-                         kCFStringEncodingUTF8)) {
-    return buffer;
-  }
-  return NULL;
-}
-
 CFStringRef get_device_full_name(const AMDeviceRef device) {
     CFStringRef full_name = NULL,
                 device_udid = AMDeviceCopyDeviceIdentifier(device),
@@ -379,6 +388,49 @@
     return full_name;
 }
 
+NSDictionary* get_device_json_dict(const AMDeviceRef device) {
+    NSMutableDictionary *json_dict = [NSMutableDictionary new];
+    AMDeviceConnect(device);
+    
+    CFStringRef device_udid = AMDeviceCopyDeviceIdentifier(device);
+    if (device_udid) {
+        [json_dict setValue:(__bridge NSString *)device_udid forKey:@"DeviceIdentifier"];
+        CFRelease(device_udid);
+    }
+    
+    CFStringRef device_hardware_model = AMDeviceCopyValue(device, 0, CFSTR("HardwareModel"));
+    if (device_hardware_model) {
+        [json_dict setValue:(NSString*)device_hardware_model forKey:@"HardwareModel"];
+        size_t device_db_length = sizeof(device_db) / sizeof(device_desc);
+        for (size_t i = 0; i < device_db_length; i ++) {
+            if (CFStringCompare(device_hardware_model, device_db[i].model, kCFCompareNonliteral | kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
+                device_desc dev = device_db[i];
+                [json_dict setValue:(__bridge NSString *)dev.name forKey:@"modelName"];
+                [json_dict setValue:(__bridge NSString *)dev.sdk forKey:@"modelSDK"];
+                [json_dict setValue:(__bridge NSString *)dev.arch forKey:@"modelArch"];
+                break;
+            }
+        }
+        CFRelease(device_hardware_model);
+    }
+    
+    for (NSString *deviceValue in @[@"DeviceName",
+                                    @"BuildVersion",
+                                    @"DeviceClass",
+                                    @"ProductType",
+                                    @"ProductVersion"]) {
+        CFStringRef cf_value = AMDeviceCopyValue(device, 0, (__bridge CFStringRef)deviceValue);
+        if (cf_value) {
+            [json_dict setValue:(__bridge NSString *)cf_value forKey:deviceValue];
+            CFRelease(cf_value);
+        }
+    }
+    
+    AMDeviceDisconnect(device);
+
+    return json_dict;
+}
+
 CFStringRef get_device_interface_name(const AMDeviceRef device) {
     // AMDeviceGetInterfaceType(device) 0=Unknown, 1 = Direct/USB, 2 = Indirect/WIFI
     switch(AMDeviceGetInterfaceType(device)) {
@@ -489,7 +541,8 @@
 
     FILE* sig = fopen(CFStringGetCStringPtr(sig_path, kCFStringEncodingMacRoman), "rb");
     void *sig_buf = malloc(128);
-    assert(fread(sig_buf, 1, 128, sig) == 128);
+    size_t bytes_read = fread(sig_buf, 1, 128, sig);
+    assert( bytes_read == 128);
     fclose(sig);
     CFDataRef sig_data = CFDataCreateWithBytesNoCopy(NULL, sig_buf, 128, NULL);
     CFRelease(sig_path);
@@ -505,6 +558,12 @@
     } else if (result == 0xe8000076 /* already mounted */) {
         NSLogOut(@"[ 95%%] Developer disk image already mounted");
     } else {
+        if (result == 0xe80000e2 /* device locked */) {
+            NSLogOut(@"The device is locked.");
+            NSLogJSON(@{@"Event": @"Error",
+                        @"Status": @"DeviceLocked"
+                        });
+        }
         on_error(@"Unable to mount developer disk image. (%x)", result);
     }
 
@@ -521,7 +580,13 @@
         CFStringRef path = CFDictionaryGetValue(dict, CFSTR("Path"));
 
         if ((last_path == NULL || !CFEqual(path, last_path)) && !CFStringHasSuffix(path, CFSTR(".ipa"))) {
-            NSLogOut(@"[%3d%%] Copying %@ to device", percent / 2, path);
+            int overall_percent = percent / 2;
+            NSLogOut(@"[%3d%%] Copying %@ to device", overall_percent, path);
+            NSLogJSON(@{@"Event": @"BundleCopy",
+                        @"OverallPercent": @(overall_percent),
+                        @"Percent": @(percent),
+                        @"Path": (__bridge NSString *)path
+                        });
         }
 
         if (last_path != NULL) {
@@ -538,7 +603,13 @@
     CFStringRef status = CFDictionaryGetValue(dict, CFSTR("Status"));
     CFNumberGetValue(CFDictionaryGetValue(dict, CFSTR("PercentComplete")), kCFNumberSInt32Type, &percent);
 
-    NSLogOut(@"[%3d%%] %@", (percent / 2) + 50, status);
+    int overall_percent = (percent / 2) + 50;
+    NSLogOut(@"[%3d%%] %@", overall_percent, status);
+    NSLogJSON(@{@"Event": @"BundleInstall",
+                @"OverallPercent": @(overall_percent),
+                @"Percent": @(percent),
+                @"Status": (__bridge NSString *)status
+                });
     return 0;
 }
 
@@ -651,6 +722,22 @@
         //printf("write_lldb_prep_cmds: [%s][%s]\n", CFStringGetCStringPtr (cmds,kCFStringEncodingMacRoman),
         //    CFStringGetCStringPtr(pmodule, kCFStringEncodingMacRoman));
     }
+
+    if (envs) {
+        CFStringRef cf_envs = CFStringCreateWithCString(NULL, envs, kCFStringEncodingUTF8);
+        CFStringFindAndReplace(cmds, CFSTR("{envs}"), cf_envs, range, 0);
+        rangeLLDB.length = CFStringGetLength(pmodule);
+        CFStringFindAndReplace(pmodule, CFSTR("{envs}"), cf_envs, rangeLLDB, 0);
+
+        //printf("write_lldb_prep_cmds:envs: [%s][%s]\n", CFStringGetCStringPtr (cmds,kCFStringEncodingMacRoman),
+        //    CFStringGetCStringPtr(pmodule, kCFStringEncodingMacRoman));
+        CFRelease(cf_envs);
+    } else {
+        CFStringFindAndReplace(cmds, CFSTR("{envs}"), CFSTR(""), range, 0);
+        CFStringFindAndReplace(pmodule, CFSTR("{envs}"), CFSTR(""), rangeLLDB, 0);
+        //printf("write_lldb_prep_cmds: [%s][%s]\n", CFStringGetCStringPtr (cmds,kCFStringEncodingMacRoman),
+        //    CFStringGetCStringPtr(pmodule, kCFStringEncodingMacRoman));
+    }
     range.length = CFStringGetLength(cmds);
 
     CFStringRef bundle_identifier = copy_disk_app_identifier(disk_app_url);
@@ -667,6 +754,21 @@
     CFStringFindAndReplace(cmds, CFSTR("{device_port}"), device_port, range, 0);
     range.length = CFStringGetLength(cmds);
 
+    if (output_path) {
+        CFStringRef output_path_str = CFStringCreateWithFormat(NULL, NULL, CFSTR("%s"), output_path);
+        CFStringFindAndReplace(cmds, CFSTR("{output_path}"), output_path_str, range, 0);
+    } else {
+        CFStringFindAndReplace(cmds, CFSTR("{output_path}"), CFSTR(""), range, 0);
+    }
+    range.length = CFStringGetLength(cmds);
+    if (error_path) {
+        CFStringRef error_path_str = CFStringCreateWithFormat(NULL, NULL, CFSTR("%s"), error_path);
+        CFStringFindAndReplace(cmds, CFSTR("{error_path}"), error_path_str, range, 0);
+    } else {
+        CFStringFindAndReplace(cmds, CFSTR("{error_path}"), CFSTR(""), range, 0);
+    }
+    range.length = CFStringGetLength(cmds);
+
     CFURLRef device_container_url = CFURLCreateCopyDeletingLastPathComponent(NULL, device_app_url);
     CFStringRef device_container_path = CFURLCopyFileSystemPath(device_container_url, kCFURLPOSIXPathStyle);
     CFMutableStringRef dcp_noprivate = CFStringCreateMutableCopy(NULL, 0, device_container_path);
@@ -795,9 +897,11 @@
 
 void start_remote_debug_server(AMDeviceRef device) {
 
-    check_error(AMDeviceStartService(device, CFSTR("com.apple.debugserver"), &gdbfd, NULL));
-    assert(gdbfd > 0);
+    ServiceConnRef con;
 
+    check_error(AMDeviceSecureStartService(device, CFSTR("com.apple.debugserver"), NULL, &con));
+    assert(con != NULL);
+    gdbfd = AMDServiceConnectionGetSocket(con);
     /*
      * The debugserver connection is through a fd handle, while lldb requires a host/port to connect, so create an intermediate
      * socket to transfer data.
@@ -911,12 +1015,6 @@
 
     NSLogOut(@"------ Debug phase ------");
 
-    if(AMDeviceGetInterfaceType(device) == 2)
-    {
-        NSLogOut(@"Cannot debug %@ over %@.", device_full_name, device_interface_name);
-        exit(0);
-    }
-
     NSLogOut(@"Starting debug of %@ connected through %@...", device_full_name, device_interface_name);
 
     mount_developer_image(device);      // put debugserver on the device
@@ -1035,6 +1133,10 @@
 
     NSLogOut(@"debugserver port: %d", port);
     NSLogOut(@"App path: %@", device_app_path);
+    NSLogJSON(@{@"Event": @"DebugServerLaunched",
+                @"Port": @(port),
+                @"Path": (__bridge NSString *)device_app_path
+                });
 }
 
 CFStringRef get_bundle_id(CFURLRef app_url)
@@ -1074,17 +1176,11 @@
 }
 
 
-void read_dir(service_conn_t afcFd, afc_connection* afc_conn_p, const char* dir,
-              void(*callback)(afc_connection *conn,const char *dir,int file))
+void read_dir(AFCConnectionRef afc_conn_p, const char* dir,
+              void(*callback)(AFCConnectionRef conn, const char *dir, int file))
 {
     char *dir_ent;
 
-    afc_connection afc_conn;
-    if (!afc_conn_p) {
-        afc_conn_p = &afc_conn;
-        AFCConnectionOpen(afcFd, 0, &afc_conn_p);
-    }
-
     afc_dictionary* afc_dict_p;
     char *key, *val;
     int not_dir = 0;
@@ -1138,7 +1234,7 @@
         if (dir_joined[strlen(dir)-1] != '/')
             strcat(dir_joined, "/");
         strcat(dir_joined, dir_ent);
-        read_dir(afcFd, afc_conn_p, dir_joined, callback);
+        read_dir(afc_conn_p, dir_joined, callback);
         free(dir_joined);
     }
 
@@ -1147,20 +1243,20 @@
 
 
 // Used to send files to app-specific sandbox (Documents dir)
-service_conn_t start_house_arrest_service(AMDeviceRef device) {
+AFCConnectionRef start_house_arrest_service(AMDeviceRef device) {
     AMDeviceConnect(device);
     assert(AMDeviceIsPaired(device));
     check_error(AMDeviceValidatePairing(device));
     check_error(AMDeviceStartSession(device));
 
-    service_conn_t houseFd;
+    AFCConnectionRef conn = NULL;
 
     if (bundle_id == NULL) {
         on_error(@"Bundle id is not specified");
     }
 
     CFStringRef cf_bundle_id = CFStringCreateWithCString(NULL, bundle_id, kCFStringEncodingUTF8);
-    if (AMDeviceStartHouseArrestService(device, cf_bundle_id, 0, &houseFd, 0) != 0)
+    if (AMDeviceCreateHouseArrestService(device, cf_bundle_id, 0, &conn) != 0)
     {
         on_error(@"Unable to find bundle with id: %@", [NSString stringWithUTF8String:bundle_id]);
     }
@@ -1169,7 +1265,7 @@
     check_error(AMDeviceDisconnect(device));
     CFRelease(cf_bundle_id);
 
-    return houseFd;
+    return conn;
 }
 
 char const* get_filename_from_path(char const* path)
@@ -1208,16 +1304,12 @@
     fclose(fd);
     return content;
 }
-
 void list_files(AMDeviceRef device)
 {
-    service_conn_t houseFd = start_house_arrest_service(device);
-
-    afc_connection* afc_conn_p;
-    if (AFCConnectionOpen(houseFd, 0, &afc_conn_p) == 0) {
-        read_dir(houseFd, afc_conn_p, list_root?list_root:"/", NULL);
-        AFCConnectionClose(afc_conn_p);
-    }
+    AFCConnectionRef afc_conn_p = start_house_arrest_service(device);
+    assert(afc_conn_p);
+    read_dir(afc_conn_p, list_root?list_root:"/", NULL);
+    check_error(AFCConnectionClose(afc_conn_p));
 }
 
 int app_exists(AMDeviceRef device)
@@ -1250,6 +1342,22 @@
     return -1;
 }
 
+void get_battery_level(AMDeviceRef device)
+{
+    
+    AMDeviceConnect(device);
+    assert(AMDeviceIsPaired(device));
+    check_error(AMDeviceValidatePairing(device));
+    check_error(AMDeviceStartSession(device));
+
+    CFStringRef result = AMDeviceCopyValue(device, (void*)@"com.apple.mobile.battery", (__bridge CFStringRef)@"BatteryCurrentCapacity");
+    NSLogOut(@"BatteryCurrentCapacity:%@",result);
+    CFRelease(result);
+    
+    check_error(AMDeviceStopSession(device));
+    check_error(AMDeviceDisconnect(device));
+}
+
 void list_bundle_id(AMDeviceRef device)
 {
     AMDeviceConnect(device);
@@ -1275,7 +1383,7 @@
     check_error(AMDeviceDisconnect(device));
 }
 
-void copy_file_callback(afc_connection* afc_conn_p, const char *name,int file)
+void copy_file_callback(AFCConnectionRef afc_conn_p, const char *name,int file)
 {
     const char *local_name=name;
 
@@ -1318,8 +1426,8 @@
 
 void download_tree(AMDeviceRef device)
 {
-    service_conn_t houseFd = start_house_arrest_service(device);
-    afc_connection* afc_conn_p = NULL;
+    AFCConnectionRef afc_conn_p = start_house_arrest_service(device);
+    assert(afc_conn_p);
     char *dirname = NULL;
 
     list_root = list_root? list_root : "/";
@@ -1328,40 +1436,33 @@
     NSString* targetPath = [NSString pathWithComponents:@[ @(target_filename), @(list_root)] ];
     mkdirp([targetPath stringByDeletingLastPathComponent]);
 
-    if (AFCConnectionOpen(houseFd, 0, &afc_conn_p) == 0)  do {
-
-    if (target_filename) {
-        dirname = strdup(target_filename);
-        mkdirp(@(dirname));
-        if (mkdir(dirname,0777) && errno!=EEXIST) {
-        fprintf(stderr,"mkdir(\"%s\") failed: %s\n",dirname,strerror(errno));
-        break;
+    do {
+        if (target_filename) {
+            dirname = strdup(target_filename);
+            mkdirp(@(dirname));
+            if (mkdir(dirname,0777) && errno!=EEXIST) {
+                fprintf(stderr,"mkdir(\"%s\") failed: %s\n",dirname,strerror(errno));
+                break;
+            }
+            if (chdir(dirname)) {
+                fprintf(stderr,"chdir(\"%s\") failed: %s\n",dirname,strerror(errno));
+                break;
+            }
         }
-        if (chdir(dirname)) {
-        fprintf(stderr,"chdir(\"%s\") failed: %s\n",dirname,strerror(errno));
-        break;
-        }
-    }
-
-    read_dir(houseFd, afc_conn_p, list_root, copy_file_callback);
-
+        read_dir(afc_conn_p, list_root, copy_file_callback);
     } while(0);
 
     if (dirname) free(dirname);
     if (afc_conn_p) AFCConnectionClose(afc_conn_p);
 }
 
-void upload_dir(AMDeviceRef device, afc_connection* afc_conn_p, NSString* source, NSString* destination);
-void upload_single_file(AMDeviceRef device, afc_connection* afc_conn_p, NSString* sourcePath, NSString* destinationPath);
+void upload_dir(AMDeviceRef device, AFCConnectionRef afc_conn_p, NSString* source, NSString* destination);
+void upload_single_file(AMDeviceRef device, AFCConnectionRef afc_conn_p, NSString* sourcePath, NSString* destinationPath);
 
 void upload_file(AMDeviceRef device)
 {
-    service_conn_t houseFd = start_house_arrest_service(device);
-
-    afc_connection afc_conn;
-    afc_connection* afc_conn_p = &afc_conn;
-    AFCConnectionOpen(houseFd, 0, &afc_conn_p);
-
+    AFCConnectionRef afc_conn_p = start_house_arrest_service(device);
+    assert(afc_conn_p);
     //        read_dir(houseFd, NULL, "/", NULL);
 
     if (!target_filename)
@@ -1386,10 +1487,10 @@
     {
         upload_single_file(device, afc_conn_p, sourcePath, destinationPath);
     }
-    assert(AFCConnectionClose(afc_conn_p) == 0);
+    check_error(AFCConnectionClose(afc_conn_p));
 }
 
-void upload_single_file(AMDeviceRef device, afc_connection* afc_conn_p, NSString* sourcePath, NSString* destinationPath) {
+void upload_single_file(AMDeviceRef device, AFCConnectionRef afc_conn_p, NSString* sourcePath, NSString* destinationPath) {
 
     afc_file_ref file_ref;
 
@@ -1418,13 +1519,13 @@
         on_error(@"Target %@ is a directory.", destinationPath);
     }
     assert(ret == 0);
-    assert(AFCFileRefWrite(afc_conn_p, file_ref, file_content, file_size) == 0);
-    assert(AFCFileRefClose(afc_conn_p, file_ref) == 0);
+    check_error(AFCFileRefWrite(afc_conn_p, file_ref, file_content, file_size));
+    check_error(AFCFileRefClose(afc_conn_p, file_ref));
 
     free(file_content);
 }
 
-void upload_dir(AMDeviceRef device, afc_connection* afc_conn_p, NSString* source, NSString* destination)
+void upload_dir(AMDeviceRef device, AFCConnectionRef afc_conn_p, NSString* source, NSString* destination)
 {
     check_error(AFCDirectoryCreate(afc_conn_p, [destination fileSystemRepresentation]));
     destination = [destination copy];
@@ -1446,26 +1547,17 @@
 }
 
 void make_directory(AMDeviceRef device) {
-    service_conn_t houseFd = start_house_arrest_service(device);
-
-    afc_connection afc_conn;
-    afc_connection* afc_conn_p = &afc_conn;
-    AFCConnectionOpen(houseFd, 0, &afc_conn_p);
-
-    assert(AFCDirectoryCreate(afc_conn_p, target_filename) == 0);
-    assert(AFCConnectionClose(afc_conn_p) == 0);
+    AFCConnectionRef afc_conn_p = start_house_arrest_service(device);
+    assert(afc_conn_p);
+    check_error(AFCDirectoryCreate(afc_conn_p, target_filename));
+    check_error(AFCConnectionClose(afc_conn_p));
 }
 
 void remove_path(AMDeviceRef device) {
-    service_conn_t houseFd = start_house_arrest_service(device);
-
-    afc_connection afc_conn;
-    afc_connection* afc_conn_p = &afc_conn;
-    AFCConnectionOpen(houseFd, 0, &afc_conn_p);
-
-
-    assert(AFCRemovePath(afc_conn_p, target_filename) == 0);
-    assert(AFCConnectionClose(afc_conn_p) == 0);
+    AFCConnectionRef afc_conn_p = start_house_arrest_service(device);
+    assert(afc_conn_p);
+    check_error(AFCRemovePath(afc_conn_p, target_filename));
+    check_error(AFCConnectionClose(afc_conn_p));
 }
 
 void uninstall_app(AMDeviceRef device) {
@@ -1509,7 +1601,13 @@
                 device_interface_name = get_device_interface_name(device);
 
     if (detect_only) {
-        NSLogOut(@"[....] Found %@ connected through %@.", device_full_name, device_interface_name);
+        if (_json_output) {
+            NSLogJSON(@{@"Event": @"DeviceDetected",
+                        @"Device": get_device_json_dict(device)
+                        });
+        } else {
+            NSLogOut(@"[....] Found %@ connected through %@.", device_full_name, device_interface_name);
+        }
         found_device = true;
         return;
     }
@@ -1523,7 +1621,8 @@
             return;
         }
     } else {
-        device_id = MYCFStringCopyUTF8String(found_device_id);
+        // Use the first device we find if a device_id wasn't specified.
+        device_id = strdup(CFStringGetCStringPtr(found_device_id, kCFStringEncodingUTF8));
         found_device = true;
     }
 
@@ -1546,6 +1645,8 @@
             uninstall_app(device);
         } else if (strcmp("list_bundle_id", command) == 0) {
             list_bundle_id(device);
+        } else if (strcmp("get_battery_level", command) == 0) {
+            get_battery_level(device);
         }
         exit(0);
     }
@@ -1601,7 +1702,7 @@
 
 
         // NOTE: the secure version doesn't seem to require us to start the AFC service
-        service_conn_t afcFd;
+        ServiceConnRef afcFd;
         check_error(AMDeviceSecureStartService(device, CFSTR("com.apple.afc"), NULL, &afcFd));
         check_error(AMDeviceStopSession(device));
         check_error(AMDeviceDisconnect(device));
@@ -1612,8 +1713,7 @@
 
         //assert(AMDeviceTransferApplication(afcFd, path, NULL, transfer_callback, NULL) == 0);
         check_error(AMDeviceSecureTransferPath(0, device, url, options, transfer_callback, 0));
-
-        close(afcFd);
+        close(*afcFd);
 
 
 
@@ -1641,6 +1741,11 @@
         CFRelease(options);
 
         NSLogOut(@"[100%%] Installed package %@", [NSString stringWithUTF8String:app_path]);
+        NSLogJSON(@{@"Event": @"BundleInstall",
+                    @"OverallPercent": @(100),
+                    @"Percent": @(100),
+                    @"Status": @"Complete"
+                    });
     }
 
     if (!debug)
@@ -1658,20 +1763,14 @@
 void device_callback(struct am_device_notification_callback_info *info, void *arg) {
     switch (info->msg) {
         case ADNCI_MSG_CONNECTED:
-            if(device_id != NULL || !debug || AMDeviceGetInterfaceType(info->dev) != 2) {
-                if (no_wifi && AMDeviceGetInterfaceType(info->dev) == 2)
-                {
-                    NSLogVerbose(@"Skipping wifi device (type: %d)", AMDeviceGetInterfaceType(info->dev));
-                }
-                else
-                {
-                    NSLogVerbose(@"Handling device type: %d", AMDeviceGetInterfaceType(info->dev));
-                    handle_device(info->dev);
-                }
-            } else if(best_device_match == NULL) {
-                NSLogVerbose(@"Best device match: %d", AMDeviceGetInterfaceType(info->dev));
-                best_device_match = info->dev;
-                CFRetain(best_device_match);
+            if (no_wifi && AMDeviceGetInterfaceType(info->dev) == 2)
+            {
+                NSLogVerbose(@"Skipping wifi device (type: %d)", AMDeviceGetInterfaceType(info->dev));
+            }
+            else
+            {
+                NSLogVerbose(@"Handling device type: %d", AMDeviceGetInterfaceType(info->dev));
+                handle_device(info->dev);
             }
         default:
             break;
@@ -1685,21 +1784,11 @@
             return;
 
         // App running for too long
-        NSLog(@"[ !! ] App is running for too long");
+        NSLogOut(@"[ !! ] App is running for too long");
         exit(exitcode_timeout);
         return;
     } else if ((!found_device) && (!detect_only))  {
-        // Device not found timeout
-        if (best_device_match != NULL) {
-            NSLogVerbose(@"Handling best device match.");
-            handle_device(best_device_match);
-
-            CFRelease(best_device_match);
-            best_device_match = NULL;
-        }
-
-        if (!found_device)
-            on_error(@"Timed out waiting for device.");
+        on_error(@"Timed out waiting for device.");
     }
     else
     {
@@ -1731,6 +1820,7 @@
         @"  -c, --detect                 only detect if the device is connected\n"
         @"  -b, --bundle <bundle.app>    the path to the app bundle to be installed\n"
         @"  -a, --args <args>            command line arguments to pass to the app when launching it\n"
+        @"  -s, --envs <envs>            environment variables, space separated key-value pairs, to pass to the app when launching it\n"
         @"  -t, --timeout <timeout>      number of seconds to wait for a device to be connected\n"
         @"  -u, --unbuffered             don't buffer stdout\n"
         @"  -n, --nostart                do not start the app when debugging\n"
@@ -1743,9 +1833,9 @@
         @"  -r, --uninstall              uninstall the app before install (do not use with -m; app cache and data are cleared) \n"
         @"  -9, --uninstall_only         uninstall the app ONLY. Use only with -1 <bundle_id> \n"
         @"  -1, --bundle_id <bundle id>  specify bundle id for list and upload\n"
-        @"  -l, --list                   list files\n"
+        @"  -l, --list[=<dir>]           list all app files or the specified directory\n"
         @"  -o, --upload <file>          upload file\n"
-        @"  -w, --download               download app tree\n"
+        @"  -w, --download[=<path>]      download app tree or the specified file/directory\n"
         @"  -2, --to <target pathname>   use together with up/download file/tree. specify target\n"
         @"  -D, --mkdir <dir>            make directory on device\n"
         @"  -R, --rm <path>              remove file or directory on device (directories must be empty)\n"
@@ -1753,7 +1843,11 @@
         @"  -e, --exists                 check if the app with given bundle_id is installed or not \n"
         @"  -B, --list_bundle_id         list bundle_id \n"
         @"  -W, --no-wifi                ignore wifi devices\n"
-        @"  --detect_deadlocks <sec>     start printing backtraces for all threads periodically after specific amount of seconds\n",
+        @"  -C, --get_battery_level      get battery current capacity \n"
+        @"  -O, --output <file>          write stdout to this file\n"
+        @"  -E, --error_output <file>    write stderr to this file\n"
+        @"  --detect_deadlocks <sec>     start printing backtraces for all threads periodically after specific amount of seconds\n"
+        @"  -j, --json                   format output as JSON\n",
         [NSString stringWithUTF8String:app]);
 }
 
@@ -1776,6 +1870,7 @@
         { "id", required_argument, NULL, 'i' },
         { "bundle", required_argument, NULL, 'b' },
         { "args", required_argument, NULL, 'a' },
+        { "envs", required_argument, NULL, 's' },
         { "verbose", no_argument, NULL, 'v' },
         { "timeout", required_argument, NULL, 't' },
         { "unbuffered", no_argument, NULL, 'u' },
@@ -1799,12 +1894,16 @@
         { "exists", no_argument, NULL, 'e'},
         { "list_bundle_id", no_argument, NULL, 'B'},
         { "no-wifi", no_argument, NULL, 'W'},
+        { "get_battery_level", no_argument, NULL, 'C'},
+        { "output", required_argument, NULL, 'O' },
+        { "error_output", required_argument, NULL, 'E' },
         { "detect_deadlocks", required_argument, NULL, 1000 },
+        { "json", no_argument, NULL, 'j'},
         { NULL, 0, NULL, 0 },
     };
     int ch;
 
-    while ((ch = getopt_long(argc, argv, "VmcdvunNrILeD:R:i:b:a:t:g:x:p:1:2:o:l::w::9::B::W", longopts, NULL)) != -1)
+    while ((ch = getopt_long(argc, argv, "VmcdvunrILeD:R:i:b:a:t:p:1:2:o:l:w:9BWjNs:OE:C", longopts, NULL)) != -1)
     {
         switch (ch) {
         case 'm':
@@ -1823,6 +1922,9 @@
         case 'a':
             args = optarg;
             break;
+        case 's':
+            envs = optarg;
+            break;
         case 'v':
             verbose = true;
             break;
@@ -1907,9 +2009,22 @@
         case 'W':
             no_wifi = true;
             break;
+        case 'C':
+            command_only = true;
+            command = "get_battery_level";
+            break;
+        case 'O':
+            output_path = optarg;
+            break;
+        case 'E':
+            error_path = optarg;
+            break;
         case 1000:
             _detectDeadlockTimeout = atoi(optarg);
             break;
+        case 'j':
+            _json_output = true;
+            break;
         default:
             usage(argv[0]);
             return exitcode_error;
diff --git a/src/ios-deploy/version.h b/src/ios-deploy/version.h
index 65be40a..84c8e7f 100644
--- a/src/ios-deploy/version.h
+++ b/src/ios-deploy/version.h
@@ -1 +1 @@
-"1.9.4"
+"1.10.0-beta.1"
diff --git a/src/scripts/check_reqs.js b/src/scripts/check_reqs.js
index 34e00db..d1c7b67 100755
--- a/src/scripts/check_reqs.js
+++ b/src/scripts/check_reqs.js
@@ -5,16 +5,10 @@
 var child_process = require('child_process');
 var semver = require('semver')
 
-var XCODEBUILD_MIN_VERSION = 7.0;
-var XCODEBUILD_NOT_FOUND_MESSAGE = util.format('Please install Xcode version %s or greater from the Mac App Store.', XCODEBUILD_MIN_VERSION);
+var XCODEBUILD_NOT_FOUND_MESSAGE = 'Please install Xcode from the Mac App Store.';
 var TOOL = 'xcodebuild';
 
-var xcode_version = child_process.spawn(TOOL, ['-version']),
-	version_string = '';
-
-xcode_version.stdout.on('data', function (data) {
-	version_string += data;
-});
+var xcode_version = child_process.spawn(TOOL, ['-version']);
 
 xcode_version.stderr.on('data', function (data) {
 	console.log('stderr: ' + data);
@@ -44,7 +38,7 @@
 			console.log('!!!! WARNING:   `--unsafe-perm=true` flag when running `npm install`');
 			console.log('!!!! WARNING:   or else it will fail.');
 			console.log('!!!! WARNING: link:');
-			console.log('!!!! WARNING:   https://github.com/phonegap/ios-deploy#os-x-1011-el-capitan-or-greater');
+			console.log('!!!! WARNING:   https://github.com/ios-control/ios-deploy#os-x-1011-el-capitan-or-greater');
 			console.log('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!');
 		}
 
diff --git a/src/scripts/lldb.py b/src/scripts/lldb.py
index 3e149aa..04e6aca 100644
--- a/src/scripts/lldb.py
+++ b/src/scripts/lldb.py
@@ -5,6 +5,7 @@
 import lldb
 
 listener = None
+startup_error = lldb.SBError()
 
 def connect_command(debugger, command, result, internal_dict):
     # These two are passed in by the script which loads us
@@ -41,7 +42,6 @@
 def run_command(debugger, command, result, internal_dict):
     device_app = internal_dict['fruitstrap_device_app']
     args = command.split('--',1)
-    error = lldb.SBError()
     lldb.target.modules[0].SetPlatformFileSpec(lldb.SBFileSpec(device_app))
     args_arr = []
     if len(args) > 1:
@@ -55,14 +55,20 @@
     #This env variable makes NSLog, CFLog and os_log messages get mirrored to stderr
     #https://stackoverflow.com/a/39581193 
     launchInfo.SetEnvironmentEntries(['OS_ACTIVITY_DT_MODE=enable'], True)
+
+    envs_arr = []
+    if len(args) > 1:
+        envs_arr = shlex.split(args[1])
+    envs_arr = envs_arr + shlex.split('{envs}')
+    launchInfo.SetEnvironmentEntries(envs_arr, True)
     
-    lldb.target.Launch(launchInfo, error)
+    lldb.target.Launch(launchInfo, startup_error)
     lockedstr = ': Locked'
-    if lockedstr in str(error):
+    if lockedstr in str(startup_error):
        print('\\nDevice Locked\\n')
        os._exit(254)
     else:
-       print(str(error))
+       print(str(startup_error))
 
 def safequit_command(debugger, command, result, internal_dict):
     process = lldb.target.process
@@ -79,6 +85,19 @@
 def autoexit_command(debugger, command, result, internal_dict):
     global listener
     process = lldb.target.process
+    if not startup_error.Success():
+        print('\\nPROCESS_NOT_STARTED\\n')
+        os._exit({exitcode_app_crash})
+
+    output_path = internal_dict['fruitstrap_output_path']
+    out = None
+    if output_path:
+        out = open(output_path, 'w')
+
+    error_path = internal_dict['fruitstrap_error_path']
+    err = None
+    if error_path:
+        err = open(error_path, 'w')
 
     detectDeadlockTimeout = {detect_deadlock_timeout}
     printBacktraceTime = time.time() + detectDeadlockTimeout if detectDeadlockTimeout > 0 else None
@@ -91,14 +110,26 @@
     def ProcessSTDOUT():
         stdout = process.GetSTDOUT(1024)
         while stdout:
-            sys.stdout.write(stdout)
+            if out:
+                out.write(stdout)
+            else:
+                sys.stdout.write(stdout)
             stdout = process.GetSTDOUT(1024)
 
     def ProcessSTDERR():
         stderr = process.GetSTDERR(1024)
         while stderr:
-            sys.stdout.write(stderr)
+            if err:
+                err.write(stderr)
+            else:
+                sys.stdout.write(stderr)
             stderr = process.GetSTDERR(1024)
+
+    def CloseOut():
+        if (out):
+            out.close()
+        if (err):
+            err.close()
     
     while True:
         if listener.WaitForEvent(1, event) and lldb.SBProcess.EventIsProcessEvent(event):
@@ -121,17 +152,21 @@
 
         if state == lldb.eStateExited:
             sys.stdout.write( '\\nPROCESS_EXITED\\n' )
+            CloseOut()
             os._exit(process.GetExitStatus())
         elif printBacktraceTime is None and state == lldb.eStateStopped:
             sys.stdout.write( '\\nPROCESS_STOPPED\\n' )
             debugger.HandleCommand('bt')
+            CloseOut()
             os._exit({exitcode_app_crash})
         elif state == lldb.eStateCrashed:
             sys.stdout.write( '\\nPROCESS_CRASHED\\n' )
             debugger.HandleCommand('bt')
+            CloseOut()
             os._exit({exitcode_app_crash})
         elif state == lldb.eStateDetached:
             sys.stdout.write( '\\nPROCESS_DETACHED\\n' )
+            CloseOut()
             os._exit({exitcode_app_crash})
         elif printBacktraceTime is not None and time.time() >= printBacktraceTime:
             printBacktraceTime = None