[AltServer] Adds “Enable JIT” menu option

Allows user to enable JIT execution for any sideloaded app by starting (and immediately stopping) a debug session on device.
This commit is contained in:
Riley Testut
2021-06-04 12:35:01 -07:00
parent d07bd33e06
commit 07ef7ae18f
3 changed files with 121 additions and 2 deletions

View File

@@ -5,6 +5,7 @@
#import "ALTDeviceManager.h"
#import "ALTWiredConnection.h"
#import "ALTNotificationConnection.h"
#import "ALTDebugConnection.h"
// Shared
#import "ALTConstants.h"

View File

@@ -37,6 +37,8 @@ class AppDelegate: NSObject, NSApplicationDelegate {
@IBOutlet private var appMenu: NSMenu!
@IBOutlet private var connectedDevicesMenu: NSMenu!
@IBOutlet private var sideloadIPAConnectedDevicesMenu: NSMenu!
@IBOutlet private var enableJITMenu: NSMenu!
@IBOutlet private var launchAtLoginMenuItem: NSMenuItem!
@IBOutlet private var installMailPluginMenuItem: NSMenuItem!
@@ -45,6 +47,9 @@ class AppDelegate: NSObject, NSApplicationDelegate {
private var connectedDevicesMenuController: MenuController<ALTDevice>!
private var sideloadIPAConnectedDevicesMenuController: MenuController<ALTDevice>!
private var enableJITMenuController: MenuController<ALTDevice>!
private var _jitAppListMenuControllers = [AnyObject]()
func applicationDidFinishLaunching(_ aNotification: Notification)
{
@@ -76,6 +81,9 @@ class AppDelegate: NSObject, NSApplicationDelegate {
self?.sideloadIPA(to: device)
}
self.enableJITMenuController = MenuController<ALTDevice>(menu: self.enableJITMenu, items: [])
self.enableJITMenuController.placeholder = placeholder
UNUserNotificationCenter.current().requestAuthorization(options: [.alert]) { (success, error) in
guard success else { return }
@@ -123,6 +131,47 @@ private extension AppDelegate
}
}
func enableJIT(for app: InstalledApp, on device: ALTDevice)
{
func finish(_ result: Result<Void, Error>)
{
DispatchQueue.main.async {
switch result
{
case .failure(let error):
self.showErrorAlert(title: String(format: NSLocalizedString("JIT compilation could not be enabled for %@.", comment: ""), app.name), error: error)
case .success:
let alert = NSAlert()
alert.messageText = String(format: NSLocalizedString("Successfully enabled JIT for %@.", comment: ""), app.name)
alert.informativeText = String(format: NSLocalizedString("JIT will remain enabled until you quit the app. You can now disconnect %@ from your computer.", comment: ""), device.name)
alert.runModal()
}
}
}
ALTDeviceManager.shared.prepare(device) { (result) in
switch result
{
case .failure(let error as NSError): return finish(.failure(error))
case .success:
ALTDeviceManager.shared.startDebugConnection(to: device) { (connection, error) in
guard let connection = connection else {
return finish(.failure(error! as NSError))
}
connection.enableUnsignedCodeExecutionForProcess(withName: app.executableName) { (success, error) in
guard success else {
return finish(.failure(error!))
}
finish(.success(()))
}
}
}
}
}
func installApplication(at url: URL, to device: ALTDevice)
{
let alert = NSAlert()
@@ -311,11 +360,15 @@ extension AppDelegate: NSMenuDelegate
func menuWillOpen(_ menu: NSMenu)
{
guard menu == self.appMenu else { return }
// Clear any cached _jitAppListMenuControllers.
self._jitAppListMenuControllers.removeAll()
self.connectedDevices = ALTDeviceManager.shared.availableDevices
self.connectedDevicesMenuController.items = self.connectedDevices
self.sideloadIPAConnectedDevicesMenuController.items = self.connectedDevices
self.enableJITMenuController.items = self.connectedDevices
self.launchAtLoginMenuItem.target = self
self.launchAtLoginMenuItem.action = #selector(AppDelegate.toggleLaunchAtLogin(_:))
@@ -335,6 +388,60 @@ extension AppDelegate: NSMenuDelegate
}
self.installMailPluginMenuItem.target = self
self.installMailPluginMenuItem.action = #selector(AppDelegate.handleInstallMailPluginMenuItem(_:))
// Need to re-set this every time menu appears so we can refresh device app list.
self.enableJITMenuController.submenuHandler = { [weak self] device in
let submenu = NSMenu(title: NSLocalizedString("Sideloaded Apps", comment: ""))
guard let `self` = self else { return submenu }
let submenuController = MenuController<InstalledApp>(menu: submenu, items: [])
submenuController.placeholder = NSLocalizedString("Loading...", comment: "")
submenuController.action = { [weak self] (appInfo) in
self?.enableJIT(for: appInfo, on: device)
}
// Keep strong reference
self._jitAppListMenuControllers.append(submenuController)
ALTDeviceManager.shared.fetchInstalledApps(on: device) { (installedApps, error) in
DispatchQueue.main.async {
guard let installedApps = installedApps else {
print("Failed to fetch installed apps from \(device).", error!)
submenuController.placeholder = error?.localizedDescription
return
}
print("Fetched \(installedApps.count) apps for \(device).")
let sortedApps = installedApps.sorted { (app1, app2) in
if app1.name == app2.name
{
return app1.bundleIdentifier < app2.bundleIdentifier
}
else
{
return app1.name < app2.name
}
}
submenuController.items = sortedApps
if submenuController.items.isEmpty
{
submenuController.placeholder = NSLocalizedString("No Sideloaded Apps", comment: "")
}
}
}
return submenu
}
}
func menuDidClose(_ menu: NSMenu)
{
// Clearing _jitAppListMenuControllers now prevents action handler from being called.
// self._jitAppListMenuControllers = []
}
}

View File

@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="17503.1" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="18122" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="17503.1"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="18122"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
@@ -62,6 +62,7 @@
<outlet property="authenticationAppleIDTextField" destination="zLd-d8-ghZ" id="wW5-0J-zdq"/>
<outlet property="authenticationPasswordTextField" destination="9rp-Vx-rvB" id="ZoC-DI-jzQ"/>
<outlet property="connectedDevicesMenu" destination="KJ9-WY-pW1" id="Mcv-64-iFU"/>
<outlet property="enableJITMenu" destination="la4-Sa-L3C" id="YrW-hR-uA7"/>
<outlet property="installMailPluginMenuItem" destination="3CM-gV-X2G" id="lio-ha-z0S"/>
<outlet property="launchAtLoginMenuItem" destination="IyR-FQ-upe" id="Fxn-EP-hwH"/>
<outlet property="sideloadIPAConnectedDevicesMenu" destination="IuI-bV-fTY" id="QQw-St-HfG"/>
@@ -108,6 +109,16 @@
</items>
</menu>
</menuItem>
<menuItem title="Enable JIT" id="TvL-8M-oPZ">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Enable JIT" systemMenu="recentDocuments" id="la4-Sa-L3C">
<items>
<menuItem title="No Connected Devices" id="Aya-FP-EzE">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem isSeparatorItem="YES" id="1ZZ-BB-xHy"/>
<menuItem title="Launch at Login" id="IyR-FQ-upe" userLabel="Launch At Login">
<modifierMask key="keyEquivalentModifierMask"/>