Habilitar el acceso para dispositivos de asistencia mediante programación en la versión 10.9


Quiero habilitar el acceso para dispositivos de asistencia programáticamente en 10.9. En 10.8 y versiones anteriores estaba usando Applescript siguiente para habilitar el acceso para 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 la versión 10.9, Apple ha movido las opciones de accesibilidad a Preferencias del sistema Security Seguridad y Privacidad Privacy Privacidad Accessibility Accesibilidad. A diferencia de las versiones anteriores de OS X, que usaban 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 de el sistema para realizar sus diversas funciones de scripted.

Las nuevas preferencias del sistema en materia de accesibilidad

Apple NO ha proporcionado ninguna API a los desarrolladores para habilitar la accesibilidad de una aplicación mediante programación. Por lo tanto, Mac OS 10.9 solicitará un diálogo para que el permiso del usuario final habilite la accesibilidad cuando la aplicación use las API de accesibilidad. Además, el usuario tiene que volver a iniciar la aplicación después de habilitar la accesibilidad.

Diálogo de solicitud predeterminado puesto por 10.9 OS para Xcode

Podemos habilitar el acceso para 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.

Author: zoul, 2013-07-17

8 answers

Esto no responde a su pregunta, pero es bueno saber acerca de una nueva llamada a la 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);

Pasar YES forzará a que aparezca la pantalla de autorización, pasar NO la saltará silenciosamente. El valor devuelto es el mismo que el devuelto por AXAPIEnabled(), que está quedando obsoleto en 10.9. Para asegurarte de que la función está disponible en tu sistema, solo compárala con NULL:

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

Necesita agregar ApplicationServices.framework a su proyecto, e importar a su .m or .archivo 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 que, por cierto, puedes hacer directamente sin pasar por el inútil diálogo 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 el Objetivo 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 aparezca la ventana emergente 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]];
}
 36
Author: zoul,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-02-12 22:07:48

Mientras que la respuesta de @user2865860 funciona bien, pensé que publicaría todo el ejemplo de código que funciona perfectamente en 10.9 para ahorrar tiempo a otros. Necesita obtener privilegios de root, por lo que le pedirá al usuario que introduzca 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...
    }
}
 9
Author: Max Al Farakh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-12-14 10:18:56

Puede editar el TCC.archivo db directamente. Tuve que hacer esto con el fin de hacer Divvy instalar sin la interacción del usuario. Sólo reemplaza a com.mizage.divida 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';"
 8
Author: user2865860,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-10-24 09:08:34

He encontrado el siguiente fragmento de código que solicita permisos de accesibilidad correctamente 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
 7
Author: Sergey L.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-07-21 11:22:22

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

  1. Hackear la base de datos sqlite tiene el mayor inconveniente en el uso de servicios de autorización. En primer lugar, aparecerá un cuadro de diálogo que le dirá al usuario que una aplicación desea instalar un ayudante de utilidad (a pesar de que es solo un envío de launchd usando 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. Necesita usar launchd con el SMJobSubmit anterior. De todos modos, esto todavía requiere autorización. También requiere una aplicación auxiliar como esta one.

Supongo que lo mejor es usar cualquiera de los dos:

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 usando, por ejemplo, scripting bridge framework:

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 viene de un archivo SBSystemPreferences.h que se puede generar:

sdef "/Applications/System Preferences.app" | sdp -fh --basename SBSystemPreferences -o SBSystemPreferences.h
 2
Author: fikovnik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-06-07 18:59:15

Gracias por estas muestras de script de shell de @NightFlight, que son realmente útiles. Usé esto con AppleScript en una aplicación Python, como lo 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

Funcionó bien para mí en código Python como una cadena.

Editar (7 de noviembre de 2014):

Si desea probar esto en el editor AppleScript, use un escape de carácter 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 tiene que tocar un archivo. El AppleScript mencionado en el código Python anterior también se puede utilizar en otros idiomas.

 1
Author: Jake W,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-11-07 07:16:57

Gracias a todos.

Emito lo siguiente activado desde la ventana de inicio de sesión para asegurar que el control se da solo a los elementos que queremos 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)"
 0
Author: NightFlight,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-11-07 18:59:05

El "hack" de sqlite3 es genial.

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

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

 0
Author: a.out,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-11 05:21:26