Detecting screen recording settings on macOS Catalina

后端 未结 7 1556
清歌不尽
清歌不尽 2020-11-30 23:01

What\'s is a reliable way to detect if user has enabled this API?

CGWindowListCreateImage returns a valid object even if screen recording API is disable

7条回答
  •  误落风尘
    2020-11-30 23:43

    As of MacOS 10.15.7 the heuristics of obtaining window-names for visible windows, and so know we have screen-capture permission, doesn't always work. Sometimes we just don't find valid windows we can query, and would wrongly deduce we don't have permissions.

    However, I found another way to directly query (using sqlite) the Apple TCC database - the model where permissions are persisted. The screen-recording permissions are to be found in the "System level" TCC database ( residing in /Library/Application Support/com.apple.TCC/TCC.db). If you open the database using sqlite, and query: SELECT allowed FROM access WHERE client="com.myCompany.myApp" AND service="kTCCServiceScreenCapture" you'll get your answer.

    Two downsides comparing to other answers:

    • to open this TCC.db database, your app must have "Full Disk Access" permission. It doesn't need to run with 'root' privileges, and root privileges won't help if you don't have the "Full disk access".
    • it takes about 15 millisec to run, which is slower than querying the window list.

    The up side -- it's a direct query of the actual thing, and does not rely on any windows, or processes to exist at the time of query.

    Here's some draft code to do this:

    NSString *client = @"com.myCompany.myApp";
    sqlite3 *tccDb = NULL;
    sqlite3_stmt *statement = NULL;
    
    NSString *pathToSystemTCCDB = @"/Library/Application Support/com.apple.TCC/TCC.db";
    const char *pathToDBFile = [pathToSystemTCCDB fileSystemRepresentation];
    if (sqlite3_open(pathToDBFile, &tccDb) != SQLITE_OK)
       return nil;
        
    const char *query = [[NSString stringWithFormat: @"SELECT allowed FROM access WHERE client=\"%@\" AND service=\"kTCCServiceScreenCapture\"",client] UTF8String];
    if (sqlite3_prepare_v2(tccDb, query , -1, &statement, nil) != SQLITE_OK)
       return nil;
        
    BOOL allowed = NO;
    while (sqlite3_step(statement) == SQLITE_ROW)
        allowed |= (sqlite3_column_int(statement, 0) == 1);
    
    if (statement)
        sqlite3_finalize(statement);
    
    if (tccDb)
        sqlite3_close(tccDb);
    
    return @(allowed);
    

    }

提交回复
热议问题