objective-c macos cocoa accessibility accessibility-api

objective c - Habilite el acceso para dispositivos de asistencia mediante programación en 10.9



objective-c macos (8)

Deseo habilitar el acceso de los dispositivos de asistencia programáticamente en 10.9. En la versión 10.8 o inferior, estaba usando Applescript para habilitar el acceso a los dispositivos de asistencia:

tell application "System Events" if UI elements enabled is false then set UI elements enabled to true end if end tell

Con 10.9, Apple ha movido las opciones de accesibilidad a Preferencias del sistema ➞ Seguridad y privacidad ➞ Privacidad ➞ Accesibilidad. A diferencia de las versiones anteriores de OS X, que utilizaba una casilla de verificación universal para todas las aplicaciones, la nueva funcionalidad en 10.9 permite a los usuarios elegir individualmente qué aplicaciones pueden obtener el control del sistema para realizar sus diversas funciones con guiones.

Apple NO ha proporcionado ninguna API a los desarrolladores para habilitar de forma programática la accesibilidad de una aplicación. Por lo tanto, Mac OS 10.9 solicitará un cuadro de diálogo para el permiso del usuario final para habilitar la Accesibilidad cuando la aplicación use API de accesibilidad. Además, el usuario debe reiniciar la aplicación después de habilitar la accesibilidad.

¿Podemos habilitar el acceso de los dispositivos de asistencia mediante programación en 10.9 utilizando Applescript o cualquier otra API? Cualquier ayuda para solucionar este problema sería muy apreciada.


El sqlite3 "hack" es genial.

Tuve que usar permisos "1,1,1" (lo que sea que eso signifique) para que esto funcione.

Tenga en cuenta que la combinación de permisos, no el cliente (es decir, el nombre del programa) es la clave única de la base de datos.


Esto no responde a su pregunta, pero es bueno saber sobre una nueva llamada a API que apareció en 10.9 y le permite mostrar la pantalla de autorización o omitirla:

NSDictionary *options = @{(id)kAXTrustedCheckOptionPrompt: @YES}; BOOL accessibilityEnabled = AXIsProcessTrustedWithOptions((CFDictionaryRef)options);

Al pasar YES se forzará la pantalla de autorización para que aparezca, pasando NO se omitirá en silencio. El valor de retorno es el mismo que el devuelto por AXAPIEnabled() , que se está desaprobando en 10.9. Para asegurarse de que la función esté disponible en su sistema, simplemente compárela con NULL :

if (AXIsProcessTrustedWithOptions != NULL) { // 10.9 and later } else { // 10.8 and older }

Deberá agregar ApplicationServices.framework a su proyecto e importarlo a su archivo .m o .h:

#import <ApplicationServices/ApplicationServices.h>

Es una pena que la pantalla de autorización no permita al usuario autorizar la aplicación directamente, solo abre la parte derecha de las Preferencias del sistema. Lo cual, por cierto, puedes hacer directamente sin pasar por el diálogo inútil del sistema:

tell application "System Preferences" set securityPane to pane id "com.apple.preference.security" tell securityPane to reveal anchor "Privacy_Accessibility" activate end tell

o usando Objective C:

NSString *urlString = @"x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility"; [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:urlString]];

Esto se puede emparejar con el primer fragmento de código para probar si accessibilityEnabled pasando @NO a kAXTrustedCheckOptionPrompt mientras se evita que kAXTrustedCheckOptionPrompt @NO kAXTrustedCheckOptionPrompt del sistema y, en su lugar, se abre el panel de preferencias de Accesibilidad directamente:

NSDictionary *options = @{(id)kAXTrustedCheckOptionPrompt: @NO}; BOOL accessibilityEnabled = AXIsProcessTrustedWithOptions((CFDictionaryRef)options); if (!accessibilityEnabled) { NSString *urlString = @"x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility"; [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:urlString]]; }


Gracias a todos.

Emití lo siguiente desde la ventana de inicio de sesión para asegurarme de que el control solo se da a los elementos que queremos en cada sesión:

# Enable Service Accessibility for Textpander and others # Clear the acess table. sqlite3 /Library/Application/ Support/com.apple.TCC/TCC.db "DELETE FROM access" # Enter the access we wish to have. sqlite3 /Library/Application/ Support/com.apple.TCC/TCC.db "INSERT INTO access VALUES (''kTCCServiceAccessibility'',''com.apple.systempreferences'',0,1,1,NULL)" sqlite3 /Library/Application/ Support/com.apple.TCC/TCC.db "INSERT INTO access VALUES (''kTCCServiceAccessibility'',''de.petermaurer.textpanderdaemon'',0,1,1,NULL)"


Gracias por los ejemplos de script de shell de @NightFlight, que son realmente útiles. Lo usé con AppleScript en una aplicación de Python, como la siguiente:

set sh to "touch /private/var/db/.AccessibilityAPIEnabled && sqlite3 //"/Library/Application Support/com.apple.TCC/TCC.db//" //"INSERT or REPLACE INTO access VALUES(''kTCCServiceAccessibility'',''com.godevnode'',0,1,0,NULL);//"" do shell script sh with administrator privileges

Me funcionó bien en el código de Python como una cadena.

Editar (7 de noviembre de 2014):

Si quiere probar esto en el Editor de AppleScript, use un escape de caracteres ligeramente diferente como se muestra a continuación:

set sh to "touch /private/var/db/.AccessibilityAPIEnabled && sqlite3 /"/Library/Application Support/com.apple.TCC/TCC.db/" /"INSERT or REPLACE INTO access VALUES(''kTCCServiceAccessibility'',''com.godevnode'',0,1,0,NULL);/"" do shell script sh with administrator privileges

Para Mac OS X antes de 10.9, es aún más simple:

accessibility_api_file = "/private/var/db/.AccessibilityAPIEnabled" def __enable_accessibility_api(): try: script = ''do shell script "touch %s" with administrator '' / ''privileges'' % accessibility_api_file result = applescript.AppleScript(script).run() log.debug("Tried to enable accessibility api, result=" + result) return True except applescript.ScriptError as err: log.error(str(err)) return False

Solo necesito tocar un archivo. El AppleScript mencionado en el código de Python anterior también se puede usar en otros idiomas.


He encontrado el siguiente fragmento de código que solicita correctamente los permisos de accesibilidad en OS X 10.9:

if (AXIsProcessTrustedWithOptions != NULL) { // 10.9 and later const void * keys[] = { kAXTrustedCheckOptionPrompt }; const void * values[] = { kCFBooleanTrue }; CFDictionaryRef options = CFDictionaryCreate( kCFAllocatorDefault, keys, values, sizeof(keys) / sizeof(*keys), &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); return AXIsProcessTrustedWithOptions(options); } // OS X 10.8 and older


Puede editar el archivo TCC.db directamente. Tenía que hacer esto para hacer que Divvy se instale sin interacción del usuario. Simplemente reemplace com.mizage.divvy con su programa.

sudo sqlite3 /Library/Application/ Support/com.apple.TCC/TCC.db "INSERT INTO access VALUES(''kTCCServiceAccessibility'',''com.mizage.divvy'',0,1,1,NULL);"

Para eliminar la entrada:

sudo sqlite3 /Library/Application/ Support/com.apple.TCC/TCC.db "delete from access where client=''com.mizage.divvy'';"


Si bien la respuesta de @ user2865860 funciona bien, pensé en publicar todo el código de muestra que funciona perfectamente en 10.9 para ahorrar tiempo a otros. Necesita obtener privilegios de administrador, por lo que pedirá a un usuario que ingrese la contraseña.

char *command= "/usr/bin/sqlite3"; char *args[] = {"/Library/Application Support/com.apple.TCC/TCC.db", "INSERT or REPLACE INTO access VALUES(''kTCCServiceAccessibility'',''com.yourapp'',0,1,0,NULL);", nil}; AuthorizationRef authRef; OSStatus status = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &authRef); if (status == errAuthorizationSuccess) { status = AuthorizationExecuteWithPrivileges(authRef, command, kAuthorizationFlagDefaults, args, NULL); AuthorizationFree(authRef, kAuthorizationFlagDestroyRights); if(status != 0){ //handle errors... } }


Yo estaba luchando con esto yo mismo y después de un poco de investigación encontré lo siguiente:

  1. La piratería de la base de datos sqlite tiene el mayor inconveniente al usar servicios de autorización. En primer lugar, se abrirá un diálogo que le indicará al usuario que una aplicación desea instalar un programa auxiliar de utilidades (aunque solo se trata de una presentación de SMJobSubmit utilizando SMJobSubmit ). En segundo lugar, no funciona para aplicaciones de espacio aislado y, por lo tanto, no hay tienda de aplicaciones.

  2. @Max Al Faeakh usa AuthorizationExecuteWithPrivileges que está en desuso. SMJobSubmit utilizar launchd con el SMJobSubmit anterior. De todos modos, esto todavía requiere autorización. También requiere una aplicación auxiliar como esta.

Creo que lo mejor es usar cualquiera de los siguientes:

NSDictionary *options = @{(id)kAXTrustedCheckOptionPrompt: @YES}; BOOL accessibilityEnabled = AXIsProcessTrustedWithOptions((CFDictionaryRef)options);

o

NSDictionary *options = @{(id)kAXTrustedCheckOptionPrompt: @NO}; BOOL accessibilityEnabled = AXIsProcessTrustedWithOptions((CFDictionaryRef)options);

y abra el panel de preferencias manualmente utilizando, por ejemplo, el marco de scripting bridge:

SBSystemPreferencesApplication *prefs = [SBApplication applicationWithBundleIdentifier:@"com.apple.systempreferences"]; [prefs activate]; SBSystemPreferencesPane *pane = [[prefs panes] find:^BOOL(SBSystemPreferencesPane *elem) { return [[elem id] isEqualToString:@"com.apple.preference.security"]; }]; SBSystemPreferencesAnchor *anchor = [[pane anchors] find:^BOOL(SBSystemPreferencesAnchor *elem) { return [[elem name] isEqualToString:@"Privacy_Accessibility"]; }]; [anchor reveal];

La clase SBSystemPreferencesPane proviene de un archivo SBSystemPreferences.h que se puede generar:

sdef "/Applications/System Preferences.app" | sdp -fh --basename SBSystemPreferences -o SBSystemPreferences.h