Question

I want to simulate a click on the icon of an app on the home screen of jailbroken iPhone. I used the code below, but it could not take effect. Did I do something wrong?

Is the method to getFrontMostAppPort() right? Event if I tried to use GSSendSystemEvent(), it had no effect.

BTW, I mean jailbroken device. Could anyone give me a help? I very much appreciate it.

// Framework Paths
#define SBSERVPATH  "/System/Library/PrivateFrameworks/SpringBoardServices.framework/SpringBoardServices"

static mach_port_t getFrontMostAppPort() {
    bool locked;
    bool passcode;
    mach_port_t *port;
    void *lib = dlopen(SBSERVPATH, RTLD_LAZY);
    int (*SBSSpringBoardServerPort)() = dlsym(lib, "SBSSpringBoardServerPort");
    void* (*SBGetScreenLockStatus)(mach_port_t* port, bool *lockStatus, bool *passcodeEnabled) = dlsym(lib, "SBGetScreenLockStatus");
    port = (mach_port_t *)SBSSpringBoardServerPort();
    dlclose(lib);
    SBGetScreenLockStatus(port, &locked, &passcode);
    void *(*SBFrontmostApplicationDisplayIdentifier)(mach_port_t *port, char *result) = dlsym(lib, "SBFrontmostApplicationDisplayIdentifier");
    char appId[256];
    memset(appId, 0, sizeof(appId));
    SBFrontmostApplicationDisplayIdentifier(port, appId);
    NSString * frontmostApp=[NSString stringWithFormat:@"%s",appId];
    if([frontmostApp length] == 0 || locked)
        return GSGetPurpleSystemEventPort();
    else
        return GSCopyPurpleNamedPort(appId);
}

static void sendTouchEvent(GSHandInfoType handInfoType, CGPoint point) {

    uint8_t touchEvent[sizeof(GSEventRecord) + sizeof(GSHandInfo) + sizeof(GSPathInfo)];

    // structure of touch GSEvent
    struct GSTouchEvent {
        GSEventRecord record;
        GSHandInfo    handInfo;
    } * event = (struct GSTouchEvent*) &touchEvent;
    bzero(touchEvent, sizeof(touchEvent));

    // set up GSEvent
    event->record.type = kGSEventHand;
    event->record.subtype = kGSEventSubTypeUnknown;
    event->record.windowLocation = point;
    event->record.timestamp = GSCurrentEventTimestamp();
    event->record.infoSize = sizeof(GSHandInfo) + sizeof(GSPathInfo);
    event->handInfo.type = handInfoType;
    event->handInfo.x52 = 1;

    bzero(&event->handInfo.pathInfos[0], sizeof(GSPathInfo));
    event->handInfo.pathInfos[0].pathIndex     = 1;
    event->handInfo.pathInfos[0].pathIdentity  = 2;
    event->handInfo.pathInfos[0].pathProximity = (handInfoType == kGSHandInfoTypeTouchDown || handInfoType == kGSHandInfoTypeTouchDragged || handInfoType == kGSHandInfoTypeTouchMoved) ? 0x03 : 0x00;;
    event->handInfo.pathInfos[0].pathLocation  = point;


    mach_port_t port = (mach_port_t)getFrontMostAppPort();
    GSSendEvent((GSEventRecord *)event, port);
}

// why nothing happened?
static clickOnHome() {
    sendTouchEvent(kGSHandInfoTypeTouchDown, CGPointMake(100, 200));
    sleep(1);
    sendTouchEvent(kGSHandInfoTypeTouchUp, CGPointMake(100, 200));
}
Was it helpful?

Solution

Yes, I think your getFrontMostAppPort() method is fine, if you got it from here.

I'm trying to understand what you want:

  1. If you simply want to open up a particular app (by bundleId), then I would recommend using the command-line open utility available on Cydia. You can call this programmatically with system(), or an exec call. Or, if you want to build this "open" capability into your own app, see my answer here.

  2. Now, maybe you are writing code for an app or tweak that's in the background, not the foreground app. And, maybe you really do need to touch a specific {x,y} coordinate, not open an app by its bundleId. If you really want that, this code works for me (based on this answer ... with corrections):

static void sendTouchEvent(GSHandInfoType handInfoType, CGPoint point) {

   uint8_t gsTouchEvent[sizeof(GSEventRecord) + sizeof(GSHandInfo) + sizeof(GSPathInfo)];

   // structure of touch GSEvent
   struct GSTouchEvent {
      GSEventRecord record;
      GSHandInfo    handInfo;
   } * touchEvent = (struct GSTouchEvent*) &gsTouchEvent;
   bzero(touchEvent, sizeof(touchEvent));

   touchEvent->record.type = kGSEventHand;
   touchEvent->record.subtype = kGSEventSubTypeUnknown;
   touchEvent->record.location = point;
   touchEvent->record.windowLocation = point;
   touchEvent->record.infoSize = sizeof(GSHandInfo) + sizeof(GSPathInfo);
   touchEvent->record.timestamp = GSCurrentEventTimestamp();
   //touchEvent->record.window = winRef;
   //touchEvent->record.senderPID = 919;  
   bzero(&touchEvent->handInfo, sizeof(GSHandInfo));
   bzero(&touchEvent->handInfo.pathInfos[0], sizeof(GSPathInfo));
   GSHandInfo touchEventHandInfo;
   touchEventHandInfo._0x5C = 0;
   touchEventHandInfo.deltaX = 0;
   touchEventHandInfo.deltaY = 0;
   touchEventHandInfo.height = 0;
   touchEventHandInfo.width = 0;
   touchEvent->handInfo = touchEventHandInfo;
   touchEvent->handInfo.type = (handInfoType == kGSHandInfoTypeTouchDown) ? 2 : 1;
   touchEvent->handInfo.deltaX = 1;
   touchEvent->handInfo.deltaY = 1;
   touchEvent->handInfo.pathInfosCount = 0;
   touchEvent->handInfo.pathInfos[0].pathIndex = 1;
   touchEvent->handInfo.pathInfos[0].pathIdentity = 2;
   touchEvent->handInfo.pathInfos[0].pathProximity = (handInfoType == kGSHandInfoTypeTouchDown || handInfoType == kGSHandInfoTypeTouchDragged || handInfoType == kGSHandInfoTypeTouchMoved) ? 0x03: 0x00;
   touchEvent->handInfo.x52 = 1;  // iOS 5+, I believe
   touchEvent->handInfo.pathInfos[0].pathLocation = point;
   //touchEvent->handInfo.pathInfos[0].pathWindow = winRef;

   GSEventRecord* record = (GSEventRecord*) touchEvent;
   record->timestamp = GSCurrentEventTimestamp();

   GSSendEvent(record, getFrontMostAppPort());
}

- (void) simulateHomeScreenTouch {
   CGPoint location = CGPointMake(50, 50);
   sendTouchEvent(kGSHandInfoTypeTouchDown, location);

   double delayInSeconds = 0.1;
   dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
   dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
      sendTouchEvent(kGSHandInfoTypeTouchUp, location);
   });
}

This will send a touch down/up event pair to SpringBoard, as long as SpringBoard is the frontmost app (no other app open). Also, you can see where the handInfoType value is mapped to 2 or 1 for touch down and up events. The code above assumes that those are the only two types of events you're generating.


Note: I first tried with your coordinates of x=100, y=200, and nothing happened. I think that coordinate is between two home screen icons. Using other coordinates (e.g. x=50, y=50) works fine.


Note II: I don't believe this solution actually requires jailbreaking. It uses Private APIs, so it can't be submitted to the App Store, but this should work for jailed phones in an enterprise, or hobbyist deployment.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top