如何通过应用程序ID确定iOS上哪些应用程序是后台应用程序,哪些是前台应用程序

11

使用 这篇问答 中所描述的方法,我可以获取运行在iOS设备上的应用程序列表。我知道PID并且可以访问它们的 kinfo_proc 结构。

如何确定哪些是前台进程,哪些是后台进程(假设我的应用程序在后台)?

我尝试根据 kinfo_proc 中的信息 (见第一个链接),通过 kp_proc.p_priority 进行推断,但似乎无法从优先级中判断出后台/前台状态。

我不关心这是否对 AppStore 审核有效,但我希望能找到一种不需要越狱就能使用的方法(私有API也可以,但需要哪些?)。我希望这至少能在 iOS 5 上使用。

我考虑编写一个简单的 MobileSubstrate 扩展,将其注入到所有应用程序中,并勾住每个人的 applicationDidBecomeActive,但这需要越狱并且过于侵入性。

3个回答

10

看起来在模拟器上对SpringBoardServices二进制文件使用nm和IDA有所帮助。以下代码适用于运行在iPod Touch 4、iPhone 4和iPad1 WiFi上的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);
}

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

1
我认为这个解决方案存在一个问题。至少,我在我的iPad 1(使用iOS 5.1)上尝试过。SBFrontmostApplicationDisplayIdentifier返回最后一个非Springboard应用程序。这意味着如果您进入了邮件,然后退出到Springboard,它仍将报告邮件是最重要的应用程序。我不确定这个API是否按设计或iPad 1或isOS 5.1的特定问题。 - Victor Ronin
1
这里的工作量太大了。只需将 YES 传递给 SBSCopyApplicationDisplayIdentifiers 的第二个参数,您就不需要其他任何代码,因为该参数意味着获取活动应用程序。 - malhal
2
@VikartiAnatra 在iOS 6和iOS 7中使用了上述代码,它可以正常工作。然而,在iOS 8中它不起作用。发现苹果在iOS 8中提高了安全性以防止这种情况。是否有任何替代方法来绕过这个问题。链接:http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2014-4361 - Avinash
1
@Avinash,你有没有找到iOS 8.0的任何绕过解决方案或替代方法? - Bittu
@Bittu 没有,如果我找到了会告诉你。 - Avinash
显示剩余8条评论

5

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

首先确保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);

2
有一种更简单的方法:extern NSString * SBSCopyFrontmostApplicationDisplayIdentifier(); - malhal
在iOS 6和iOS 7中使用上述代码运行良好。然而,在iOS 8中无法正常工作。发现苹果在iOS 8中提高了安全性以防止此类情况。是否有任何替代方法来绕过这个问题?链接:http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2014-4361 - Avinash
据我所知,现在绝对没有办法再这样做了 :) - hackerdiehack

-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吗? - Manan Devani

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接