【问题标题】:how to determine which apps are background and which app is foreground on iOS by application id如何通过应用程序 id 在 iOS 上确定哪些应用程序是后台应用程序以及哪些应用程序是前台程序
【发布时间】:2012-01-05 08:20:27
【问题描述】:

使用this question中描述的方法,我可以获得iOS设备上运行的应用列表。 我知道 PID 并且可以访问它们的 kinfo_proc 结构。 如何确定哪些是前台进程,哪些是后台(假设我的应用是后台)?

我试图通过kp_proc.p_priority 中的kinfo_proc(参见第一个链接)中的信息找出这一点,但似乎无法从优先级推断背景/前景状态。

我真的不在乎这是否适用于 AppStore Review,但我更喜欢一种无需越狱即可工作的方法(即私有 API 可以,但哪些?)。我希望它至少可以在 iOS 5 上运行

我考虑写一个简单的MobileSubstrate扩展,将它注入到所有应用程序中,然后挂钩每个人的applicationDidBecomeActive,但这需要越狱,而且侵入性太大。

【问题讨论】:

    标签: ios iphone-privateapi


    【解决方案1】:

    这对我来说适用于所有 IOS 设备:

    #define UIKITPATH "/System/Library/Framework/UIKit.framework/UIKit"
    #define SBSERVPATH "/System/Library/PrivateFrameworks/SpringBoardServices.framework/SpringBoardServices"
    
     - (NSArray*) getActiveApps
     {
    mach_port_t *p;
    void *uikit = dlopen(UIKITPATH, RTLD_LAZY);
    int (*SBSSpringBoardServerPort)() =
    dlsym(uikit, "SBSSpringBoardServerPort");
    p = (mach_port_t *)SBSSpringBoardServerPort();
    dlclose(uikit);
    
    if(self.frameWorkPath == nil || self.frameWorkPath.length == 0)
    {
        self.frameWorkPath = @SBSERVPATH;
        self.frameWorkPath = [self.frameWorkPath stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]];
    }
    
    const char *cString = [self.frameWorkPath cStringUsingEncoding:NSUTF8StringEncoding];
    //const char *bar = [self.frameWorkPath UTF8String];
    void *sbserv = dlopen(cString, RTLD_LAZY);
    NSArray* (*SBSCopyApplicationDisplayIdentifiers)(mach_port_t* port, BOOL runningApps,BOOL debuggable) =
    dlsym(sbserv, "SBSCopyApplicationDisplayIdentifiers");
    //SBDisplayIdentifierForPID - protype assumed,verification of params done
    void* (*SBDisplayIdentifierForPID)(mach_port_t* port, int pid,char * result) =
    dlsym(sbserv, "SBDisplayIdentifierForPID");
    //SBFrontmostApplicationDisplayIdentifier - prototype assumed,verification of params done,don't call this TOO often(every second on iPod touch 4G is 'too often,every 5 seconds is not)
    void* (*SBFrontmostApplicationDisplayIdentifier)(mach_port_t* port,char * result) =
    dlsym(sbserv, "SBFrontmostApplicationDisplayIdentifier");
    
    
    
    //Get frontmost application
    char frontmostAppS[512];
    memset(frontmostAppS,sizeof(frontmostAppS),0);
    SBFrontmostApplicationDisplayIdentifier(p,frontmostAppS);
    NSString * frontmostApp=[NSString stringWithFormat:@"%s",frontmostAppS];
    
    if([self iOsMajorVersion] >= 7){
        NSNumber *topmost = [NSNumber numberWithBool:YES];
        NSMutableDictionary * dict = [[NSMutableDictionary alloc] init];
        NSMutableArray  * splitted = [frontmostApp componentsSeparatedByString:@"."];
        if(frontmostApp.length > 0 && splitted != nil && splitted.count > 1 && topmost.boolValue == YES){
            NSString *appname = [splitted lastObject];
            [dict setObject:[appname capitalizedString] forKey:@"ProcessName"];
            [dict setObject:frontmostApp forKey:@"ProcessID"];
            [dict setObject:frontmostApp forKey:@"AppID"];
            [dict setObject:topmost forKey:@"isFrontmost"];
            NSLog(@"Running TOPMOST App %@",dict);
            return @[dict];
        }
        else{
            return nil;
        }
    }
    //NSLog(@"Frontmost app is %@",frontmostApp);
    //get list of running apps from SpringBoard
    NSArray *allApplications = SBSCopyApplicationDisplayIdentifiers(p,NO, NO);
    //Really returns ACTIVE applications(from multitasking bar)
       NSLog(@"Active applications:");
     for(NSString *identifier in allApplications) {
         // NSString * locName=SBSCopyLocalizedApplicationNameForDisplayIdentifier(p,identifier);
         NSLog(@"Active Application:%@",identifier);
     }
    
    
    //get list of all apps from kernel
    int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0};
    size_t miblen = 4;
    
    size_t size;
    int st = sysctl(mib, miblen, NULL, &size, NULL, 0);
    
    struct kinfo_proc * process = NULL;
    struct kinfo_proc * newprocess = NULL;
    
    do {
    
        size += size / 10;
        newprocess = realloc(process, size);
    
        if (!newprocess){
    
            if (process){
                free(process);
            }
    
            return nil;
        }
    
        process = newprocess;
        st = sysctl(mib, miblen, process, &size, NULL, 0);
    
    } while (st == -1 && errno == ENOMEM);
    
    if (st == 0){
    
        if (size % sizeof(struct kinfo_proc) == 0){
            int nprocess = size / sizeof(struct kinfo_proc);
    
            if (nprocess){
    
                NSMutableArray * array = [[NSMutableArray alloc] init];
    
                for (int i = nprocess - 1; i >= 0; i--){
    
                    int ruid=process[i].kp_eproc.e_pcred.p_ruid;
                    int uid=process[i].kp_eproc.e_ucred.cr_uid;
                    //short int nice=process[i].kp_proc.p_nice;
                    //short int u_prio=process[i].kp_proc.p_usrpri;
                    short int prio=process[i].kp_proc.p_priority;
                    NSString * processID = [[NSString alloc] initWithFormat:@"%d", process[i].kp_proc.p_pid];
                    NSString * processName = [[NSString alloc] initWithFormat:@"%s", process[i].kp_proc.p_comm];
    
    
                    BOOL systemProcess=YES;
                    if (ruid==501){
                        systemProcess=NO;
                    }
    
    
                    char * appid[256];
                    memset(appid,sizeof(appid),0);
                    int intID,intID2;
                    intID=process[i].kp_proc.p_pid,appid;
                    SBDisplayIdentifierForPID(p,intID,appid);
    
                    NSString * appId=[NSString stringWithFormat:@"%s",appid];
    
                    if (systemProcess==NO)
                    {
                        if ([appId isEqualToString:@""])
                        {
                            //final check.if no appid this is not springboard app
                            //NSLog(@"(potentially system)Found process with PID:%@ name %@,isSystem:%d,Priority:%d",processID,processName,systemProcess,prio);
                        }
                        else
                        {
    
                            BOOL isFrontmost=NO;
                            if ([frontmostApp isEqualToString:appId])
                            {
                                isFrontmost=YES;
                            }
                            NSNumber *isFrontmostN=[NSNumber numberWithBool:isFrontmost];
                            NSDictionary * dict = [[NSDictionary alloc] initWithObjects:[NSArray arrayWithObjects:processID, processName,appId,isFrontmostN, nil] 
                                                                                forKeys:[NSArray arrayWithObjects:@"ProcessID", @"ProcessName",@"AppID",@"isFrontmost", nil]];
                            NSLog(@"PID:%@, name: %@, AppID:%@,isFrontmost:%d",processID,processName,appId,isFrontmost);
                            [array addObject:dict];
                        }
                    }
                }
    
                free(process);
                return array;
            }
        }
    }
    
    dlclose(sbserv);
     }
    

    【讨论】:

    • 所有 iOS 设备是什么意思?这适用于 iOS >= 8
    【解决方案2】:

    很好的答案!但是您的代码中有一个小错字,应该是:

    首先确保定义了 SBSERVPATH 并且包含了正确的头文件:

    #import <sys/sysctl.h>
    #import <dlfcn.h>
    
    #define SBSERVPATH "/System/Library/PrivateFrameworks/SpringBoardServices.framework/SpringBoardServices"
    

    然后先找到正确的SB口:

    mach_port_t *port;
    void *lib = dlopen(SBSERVPATH, RTLD_LAZY);
    int (*SBSSpringBoardServerPort)() = 
    dlsym(lib, "SBSSpringBoardServerPort");
    port = (mach_port_t *)SBSSpringBoardServerPort(); 
    dlclose(lib);
    

    然后找到激活的应用:

    mach_port_t * port = [self getSpringBoardPort];
    // open springboard lib
    void *lib = dlopen(SBSERVPATH, RTLD_LAZY);
    
    // retrieve function SBFrontmostApplicationDisplayIdentifier
    void *(*SBFrontmostApplicationDisplayIdentifier)(mach_port_t *port, char *result) =
    dlsym(lib, "SBFrontmostApplicationDisplayIdentifier");
    
    // reserve memory for name
    char appId[256];
    memset(appId, 0, sizeof(appId));
    
    // retrieve front app name
    SBFrontmostApplicationDisplayIdentifier(port, appId);
    
    // close dynlib
    dlclose(lib);
    

    【讨论】:

    • 有一个更简单的方法:extern NSString * SBSCopyFrontmostApplicationDisplayIdentifier();
    • 在 iOS 6 和 iOS 7 中使用了上面的代码,它工作正常。但是它在 iOS 8 中不起作用。发现苹果已经改进了 iOS8 中的安全性来防止这种情况。有没有其他方法可以绕过这个。链接:cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2014-4361
    • 据我所知,绝对没有办法再这样做了 :)
    【解决方案3】:

    好吧,看起来在模拟器中的 SpringBoardServices 二进制文件中使用 nm 和 IDA 对我有所帮助。 以下代码适用于在 iPod Touch 4、iPhone 4 和 iPad1 WiFi(所有非 JB)上运行的 iOS 5.0.1 当然,您永远不应该尝试将其提交到 AppStore

    - (NSArray*) getActiveApps
    {
    mach_port_t *p;
    void *uikit = dlopen(UIKITPATH, RTLD_LAZY);
    int (*SBSSpringBoardServerPort)() = 
    dlsym(uikit, "SBSSpringBoardServerPort");
    p = (mach_port_t *)SBSSpringBoardServerPort(); 
    dlclose(uikit);
    
    void *sbserv = dlopen(SBSERVPATH, RTLD_LAZY);
    NSArray* (*SBSCopyApplicationDisplayIdentifiers)(mach_port_t* port, BOOL runningApps,BOOL debuggable) = 
    dlsym(sbserv, "SBSCopyApplicationDisplayIdentifiers");
    //SBDisplayIdentifierForPID - protype assumed,verification of params done
    void* (*SBDisplayIdentifierForPID)(mach_port_t* port, int pid,char * result) = 
    dlsym(sbserv, "SBDisplayIdentifierForPID");
    //SBFrontmostApplicationDisplayIdentifier - prototype assumed,verification of params done,don't call this TOO often(every second on iPod touch 4G is 'too often,every 5 seconds is not)
    void* (*SBFrontmostApplicationDisplayIdentifier)(mach_port_t* port,char * result) = 
    dlsym(sbserv, "SBFrontmostApplicationDisplayIdentifier");
    
    
    
    //Get frontmost application
    char frontmostAppS[256];
    memset(frontmostAppS,sizeof(frontmostAppS),0);
    SBFrontmostApplicationDisplayIdentifier(p,frontmostAppS);
    NSString * frontmostApp=[NSString stringWithFormat:@"%s",frontmostAppS];
    //NSLog(@"Frontmost app is %@",frontmostApp);
    //get list of running apps from SpringBoard
    NSArray *allApplications = SBSCopyApplicationDisplayIdentifiers(p,NO, NO);
    //Really returns ACTIVE applications(from multitasking bar)
    /*   NSLog(@"Active applications:");
     for(NSString *identifier in allApplications) {
     // NSString * locName=SBSCopyLocalizedApplicationNameForDisplayIdentifier(p,identifier);
     NSLog(@"Active Application:%@",identifier);
     }
     */ 
    
    //get list of all apps from kernel
    int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0};
    size_t miblen = 4;
    
    size_t size;
    int st = sysctl(mib, miblen, NULL, &size, NULL, 0);
    
    struct kinfo_proc * process = NULL;
    struct kinfo_proc * newprocess = NULL;
    
    do {
    
        size += size / 10;
        newprocess = realloc(process, size);
    
        if (!newprocess){
    
            if (process){
                free(process);
            }
    
            return nil;
        }
    
        process = newprocess;
        st = sysctl(mib, miblen, process, &size, NULL, 0);
    
    } while (st == -1 && errno == ENOMEM);
    
    if (st == 0){
    
        if (size % sizeof(struct kinfo_proc) == 0){
            int nprocess = size / sizeof(struct kinfo_proc);
    
            if (nprocess){
    
                NSMutableArray * array = [[NSMutableArray alloc] init];
    
                for (int i = nprocess - 1; i >= 0; i--){
    
                    int ruid=process[i].kp_eproc.e_pcred.p_ruid;
                    int uid=process[i].kp_eproc.e_ucred.cr_uid;
                    //short int nice=process[i].kp_proc.p_nice;
                    //short int u_prio=process[i].kp_proc.p_usrpri;
                    short int prio=process[i].kp_proc.p_priority;
                    NSString * processID = [[NSString alloc] initWithFormat:@"%d", process[i].kp_proc.p_pid];
                    NSString * processName = [[NSString alloc] initWithFormat:@"%s", process[i].kp_proc.p_comm];
    
    
                    BOOL systemProcess=YES;
                    if (ruid==501)
                        systemProcess=NO;
    
    
    
                    char * appid[256];
                    memset(appid,sizeof(appid),0);
                    int intID,intID2;
                    intID=process[i].kp_proc.p_pid,appid;
                    SBDisplayIdentifierForPID(p,intID,appid);/
    
                    NSString * appId=[NSString stringWithFormat:@"%s",appid];
    
                    if (systemProcess==NO)
                    {
                        if ([appId isEqualToString:@""])
                        {
                            //final check.if no appid this is not springboard app
                            NSLog(@"(potentially system)Found process with PID:%@ name %@,isSystem:%d,Priority:%d",processID,processName,systemProcess,prio);
                        }
                        else
                        {
    
                            BOOL isFrontmost=NO;
                            if ([frontmostApp isEqualToString:appId])
                            {
                                isFrontmost=YES;
                            }
                            NSNumber *isFrontmostN=[NSNumber numberWithBool:isFrontmost];
                            NSDictionary * dict = [[NSDictionary alloc] initWithObjects:[NSArray arrayWithObjects:processID, processName,appId,isFrontmostN, nil] 
                                                                                forKeys:[NSArray arrayWithObjects:@"ProcessID", @"ProcessName",@"AppID",@"isFrontmost", nil]];
    
                            NSLog(@"PID:%@, name: %@, AppID:%@,isFrontmost:%d",processID,processName,appId,isFrontmost);
                            [array addObject:dict];
                        }
                    }
                }
    
                free(process);
                return array;
            }
        }
      }
    
        dlclose(sbserv);
    }
    

    当然,第二个循环不是绝对必要的,但我也需要非本地化名称和 PID。

    【讨论】:

    • 我相信这个解决方案存在一个问题。至少,我在我的 iPad 1(使用 iOS 5.1)上尝试过。 SBFrontmostApplicationDisplayIdentifier 返回最后一个非 Springboard 应用程序。也就是说,如果你输入了Mail,后来又存在到Springboard,它仍然会报告Mail是最重要的应用程序。我不确定这是针对此 API 的设计还是 iPad 1 或 isOS 5.1 的特定问题
    • 好吧,对我来说它有效。 iPhone4、iPad1、iPad3 上的 iOS 5.1 和 iPhone4 上的 6.0beta2(尚未在 beta3 上检查)
    • 这里的工作太多了。只需将 YES 传递给 SBSCopyApplicationDisplayIdentifiers 以获得第二个参数,您不需要任何其他代码,因为该参数意味着获取活动应用程序。
    • @VikartiAnatra 在 iOS 6 和 iOS 7 中使用了上面的代码,它工作正常。但是它在 iOS 8 中不起作用。发现苹果已经改进了 iOS8 中的安全性来防止这种情况。有没有其他方法可以绕过这个。链接:cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2014-4361
    • @Avinash 你有没有为 ios 8.0 找到任何绕过解决方案或替代方案?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多