solicita sesion iniciar dispositivo con buscar appleid apple ios macos architecture nsnotificationcenter iphone-privateapi

sesion - Obtención de estado para las notificaciones de todo el sistema en iOS y OS X



itunes (1)

Estoy tratando de escribir un código que controlará la activación / desactivación de la pantalla en iOS (puede echar un vistazo a esta pregunta donde se discute un problema similar). Incluí la etiqueta OSX para esta pregunta, porque OSX tiene la misma función de notificación en todo el sistema. Y el problema que se describe a continuación es heredado de las notificaciones (vs iOS o OSX).

Existe un método bien conocido para registrarse en la notificación de todo el sistema com.apple.springboard.hasBlankedScreen para recibir notificaciones cuando la pantalla está apagada o encendida.

Solo para referencias (aquí están las API que se utilizan para registrarse para notificaciones de todo el sistema):

  • notify_post, notify_check_ notify_get_state y amigos
  • CFNotificationCenterPostNotification, CFNotificationCenterAddObserver y amigos (que utiliza notify_post y etc. internamente)

Sin embargo, hay dos problemas interrelacionados con este enfoque:

  • Las notificaciones para activar y desactivar la pantalla vienen con el mismo nombre (com.apple.springboard.hasBlankedScreen)
  • El observador no recibe un estado como parte de la notificación.

Por lo tanto, como resultado tenemos que implementar alguna solución que diferencie la activación y desactivación de la pantalla (ya que se llamará a la misma notificación de devolución de llamada y ninguno de los parámetros tendrá un estado).

En términos generales, el problema principal es que el estado está desacoplado de la devolución de llamadas de notificación. No veo cómo manejar esto con gracia.

Se me ocurren dos enfoques simples (cada uno de ellos tiene fallas). Y buscando ideas de otros enfoques o mejoras sobre este enfoque.

Conteo de solución

Podemos implementar un contador que cuente la cantidad de notificaciones que ya recibimos y, en base a esta información, sabremos si se trata de una notificación para activar o desactivar la pantalla (según la rareza de nuestro contador).

Sin embargo, tiene dos desventajas:

1) En el caso, si el sistema (por razones de tiempo de diseño desconocidas) enviará notificaciones adicionales con el mismo nombre, nuestra lógica se atornillará, porque se romperá la comprobación de la rareza.

2) Además, necesitamos establecer el estado inicial correctamente. Entonces, en algún lugar del código tendremos algo así:

counter = getInitialState(); registerForNotification();

En este caso, tenemos una condición de carrera. Si el sistema enviará una notificación y cambiará el estado después de getInitialState (), pero antes de registerForNotification () terminaremos con un valor de contador incorrecto.

Si haremos el siguiente código:

registerForNotification(); counter = getInitialState();

En este caso, tenemos otra condición de carrera. Si el sistema enviará una notificación y cambiará el estado luego de que registramos ForNotification (), pero antes de getInitialState (), obtendremos un contador, ingresaremos la devolución de la notificación y aumentará el contador (lo que hará que sea incorrecto).

Determinación del estado cuando la notificación recibió la solución

En este caso, no almacenamos ningún contador, sino que usamos la API notify_get_state en la notificación de devolución de llamada para obtener el estado actual.

Esto tiene su propio problema:

1) Notificación entregada a la aplicación de forma asíncrona. Entonces, como resultado, si apaga y enciende la pantalla realmente rápido, puede recibir ambas notificaciones cuando la pantalla ya estaba encendida. Entonces, notify_check obtendrá un estado actual (frente al estado en el momento en que se envió la notificación).

Como resultado, cuando la aplicación usará notify_get_state en la notificación de devolución de llamada, determinará que hubo dos notificaciones "se activó la pantalla", en lugar de que una pantalla de notificación "se desactivó" y otra "pantalla se activó".

PS En términos generales, todos los problemas descritos no son específicos de la pantalla de encendido / apagado. Son reales para cualquier sistema de notificaciones que tenga estados distintivos y envíe con el mismo nombre de notificación.

Actualización 1

No probé exactamente el escenario con encender / apagar la pantalla muy rápido y obtener los mismos resultados de notify_get_state ().

Sin embargo, tengo una especie de escenario similar cuando recibí dos notificaciones com.apple.springboard.lockstate (suscrito a través de CFNotificationCenterAddObserver) y utilicé otra API para obtener un estado bloqueado actual del dispositivo y recibí los mismos valores para ambas notificaciones.

Así que solo asumo que notify_get_state también devolverá los mismos valores. Sin embargo, creo que es una conjetura educada. El parámetro de entrada para notify_get_state será el mismo para dos llamadas (no cambia). Y no creo que el sistema almacene la cola de estados FIFO que debería devolver el estado notify_get_state.


Entonces, construí un experimento muy simple. Ejecuté esto en un iOS 6.1 iPhone 5 con jailbreak, fuera del depurador.

Código

Creé una aplicación para el consumidor, con el siguiente código:

#define EVENT "com.mycompany.bs" - (void)registerForNotifications { int result = notify_register_dispatch(EVENT, &notifyToken, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0l), ^(int info) { uint64_t state; notify_get_state(notifyToken, &state); NSLog(@"notify_register_dispatch() : %d", (int)state); }); if (result != NOTIFY_STATUS_OK) { NSLog(@"register failure = %d", result); } CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), //center NULL, // observer notifyCallback, // callback CFSTR(EVENT), // event name NULL, // object CFNotificationSuspensionBehaviorDeliverImmediately); } static void notifyCallback(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo) { uint64_t state; notify_get_state(notifyToken, &state); NSLog(@"notifyCallback(): %d", (int)state); }

Entonces, como ve, usa dos métodos diferentes para registrarse para el mismo evento personalizado. Arranco esta aplicación, dejo que se registre para el evento y luego la guardo en segundo plano (presione el botón de inicio).

Luego, la aplicación de productor, que me permite generar el evento con un botón, presiona:

double delayInSeconds = 0.001; dispatch_queue_t q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0l); dispatch_async(q, ^(void) { notify_set_state(notifyToken, 2); notify_post(EVENT); }); dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); dispatch_after(popTime, q, ^(void){ notify_set_state(notifyToken, 3); notify_post(EVENT); });

Resultados

Luego ejecuté la aplicación de productor, generando manualmente un par de eventos cada dos segundos. Como puede ver, el productor publica rápidamente el evento con un estado de 2 , y luego publica inmediatamente otro evento con un estado de 3 . Entonces, el consumidor debería imprimir 2 luego 3 , para ambos métodos de devolución de llamada, si esto funciona perfectamente. No (como temiste):

Feb 13 21:46:12 iPhone5 MyApp[1971] <Warning>: notifyCallback(): 3 Feb 13 21:46:12 iPhone5 MyApp[1971] <Warning>: notify_register_dispatch() : 3 Feb 13 21:46:12 iPhone5 MyApp[1971] <Warning>: notify_register_dispatch() : 3 Feb 13 21:46:12 iPhone5 MyApp[1971] <Warning>: notifyCallback(): 3 Feb 13 21:46:18 iPhone5 MyApp[1971] <Warning>: notifyCallback(): 3 Feb 13 21:46:18 iPhone5 MyApp[1971] <Warning>: notify_register_dispatch() : 3 Feb 13 21:46:18 iPhone5 MyApp[1971] <Warning>: notify_register_dispatch() : 3 Feb 13 21:46:18 iPhone5 MyApp[1971] <Warning>: notifyCallback(): 3 Feb 13 21:46:22 iPhone5 MyApp[1971] <Warning>: notifyCallback(): 3 Feb 13 21:46:22 iPhone5 MyApp[1971] <Warning>: notify_register_dispatch() : 3 Feb 13 21:46:22 iPhone5 MyApp[1971] <Warning>: notify_register_dispatch() : 3 Feb 13 21:46:22 iPhone5 MyApp[1971] <Warning>: notifyCallback(): 3 Feb 13 21:46:26 iPhone5 MyApp[1971] <Warning>: notifyCallback(): 2 Feb 13 21:46:26 iPhone5 MyApp[1971] <Warning>: notify_register_dispatch() : 3 Feb 13 21:46:26 iPhone5 MyApp[1971] <Warning>: notify_register_dispatch() : 3 Feb 13 21:46:26 iPhone5 MyApp[1971] <Warning>: notifyCallback(): 3 Feb 13 21:46:30 iPhone5 MyApp[1971] <Warning>: notifyCallback(): 3 Feb 13 21:46:30 iPhone5 MyApp[1971] <Warning>: notify_register_dispatch() : 3 Feb 13 21:46:30 iPhone5 MyApp[1971] <Warning>: notify_register_dispatch() : 3 Feb 13 21:46:30 iPhone5 MyApp[1971] <Warning>: notifyCallback(): 3 Feb 13 21:46:33 iPhone5 MyApp[1971] <Warning>: notifyCallback(): 3 Feb 13 21:46:33 iPhone5 MyApp[1971] <Warning>: notify_register_dispatch() : 3 Feb 13 21:46:33 iPhone5 MyApp[1971] <Warning>: notify_register_dispatch() : 3 Feb 13 21:46:33 iPhone5 MyApp[1971] <Warning>: notifyCallback(): 3 Feb 13 21:46:36 iPhone5 MyApp[1971] <Warning>: notifyCallback(): 2 Feb 13 21:46:36 iPhone5 MyApp[1971] <Warning>: notify_register_dispatch() : 3 Feb 13 21:46:36 iPhone5 MyApp[1971] <Warning>: notify_register_dispatch() : 3 Feb 13 21:46:36 iPhone5 MyApp[1971] <Warning>: notifyCallback(): 3

Intenté cambiar un método de registro de consumidor para usar CFNotificationSuspensionBehaviorCoalesce (en lugar de entregarlo de inmediato). Resultados:

Feb 13 21:48:17 iPhone5 MyApp[1990] <Warning>: notifyCallback(): 3 Feb 13 21:48:17 iPhone5 MyApp[1990] <Warning>: notify_register_dispatch() : 3 Feb 13 21:48:17 iPhone5 MyApp[1990] <Warning>: notify_register_dispatch() : 3 Feb 13 21:48:17 iPhone5 MyApp[1990] <Warning>: notifyCallback(): 3 Feb 13 21:48:20 iPhone5 MyApp[1990] <Warning>: notifyCallback(): 2 Feb 13 21:48:20 iPhone5 MyApp[1990] <Warning>: notify_register_dispatch() : 3 Feb 13 21:48:20 iPhone5 MyApp[1990] <Warning>: notify_register_dispatch() : 3 Feb 13 21:48:20 iPhone5 MyApp[1990] <Warning>: notifyCallback(): 3 Feb 13 21:48:24 iPhone5 MyApp[1990] <Warning>: notifyCallback(): 2 Feb 13 21:48:24 iPhone5 MyApp[1990] <Warning>: notify_register_dispatch() : 2 Feb 13 21:48:24 iPhone5 MyApp[1990] <Warning>: notify_register_dispatch() : 3 Feb 13 21:48:24 iPhone5 MyApp[1990] <Warning>: notifyCallback(): 3 Feb 13 21:48:29 iPhone5 MyApp[1990] <Warning>: notifyCallback(): 2 Feb 13 21:48:29 iPhone5 MyApp[1990] <Warning>: notify_register_dispatch() : 3 Feb 13 21:48:29 iPhone5 MyApp[1990] <Warning>: notify_register_dispatch() : 3 Feb 13 21:48:29 iPhone5 MyApp[1990] <Warning>: notifyCallback(): 3 Feb 13 21:48:32 iPhone5 MyApp[1990] <Warning>: notifyCallback(): 2 Feb 13 21:48:32 iPhone5 MyApp[1990] <Warning>: notify_register_dispatch() : 2 Feb 13 21:48:32 iPhone5 MyApp[1990] <Warning>: notify_register_dispatch() : 3 Feb 13 21:48:32 iPhone5 MyApp[1990] <Warning>: notifyCallback(): 3 Feb 13 21:48:35 iPhone5 MyApp[1990] <Warning>: notifyCallback(): 2 Feb 13 21:48:35 iPhone5 MyApp[1990] <Warning>: notify_register_dispatch() : 2 Feb 13 21:48:35 iPhone5 MyApp[1990] <Warning>: notify_register_dispatch() : 3 Feb 13 21:48:35 iPhone5 MyApp[1990] <Warning>: notifyCallback(): 3 Feb 13 21:48:38 iPhone5 MyApp[1990] <Warning>: notifyCallback(): 2 Feb 13 21:48:38 iPhone5 MyApp[1990] <Warning>: notify_register_dispatch() : 2 Feb 13 21:48:38 iPhone5 MyApp[1990] <Warning>: notify_register_dispatch() : 3 Feb 13 21:48:38 iPhone5 MyApp[1990] <Warning>: notifyCallback(): 3 Feb 13 21:48:39 iPhone5 MyApp[1990] <Warning>: notifyCallback(): 2 Feb 13 21:48:39 iPhone5 MyApp[1990] <Warning>: notify_register_dispatch() : 3 Feb 13 21:48:39 iPhone5 MyApp[1990] <Warning>: notify_register_dispatch() : 3 Feb 13 21:48:39 iPhone5 MyApp[1990] <Warning>: notifyCallback(): 3

Luego intenté cambiar la prioridad de colas del notify_register_dispatch() a high , en lugar de prioridad de fondo . Resultados:

Feb 13 21:49:51 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 3 Feb 13 21:49:51 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 3 Feb 13 21:49:51 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 3 Feb 13 21:49:51 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 3 Feb 13 21:49:53 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 2 Feb 13 21:49:53 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 2 Feb 13 21:49:53 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 3 Feb 13 21:49:53 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 3 Feb 13 21:49:55 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 2 Feb 13 21:49:55 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 2 Feb 13 21:49:55 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 3 Feb 13 21:49:55 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 3 Feb 13 21:49:59 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 2 Feb 13 21:49:59 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 2 Feb 13 21:49:59 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 3 Feb 13 21:49:59 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 3 Feb 13 21:50:01 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 2 Feb 13 21:50:01 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 3 Feb 13 21:50:01 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 3 Feb 13 21:50:01 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 3 Feb 13 21:50:04 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 2 Feb 13 21:50:04 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 2 Feb 13 21:50:04 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 3 Feb 13 21:50:04 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 3 Feb 13 21:50:06 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 2 Feb 13 21:50:06 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 2 Feb 13 21:50:06 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 3 Feb 13 21:50:06 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 3 Feb 13 21:50:09 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 2 Feb 13 21:50:09 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 2 Feb 13 21:50:09 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 3 Feb 13 21:50:09 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 3 Feb 13 21:50:10 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 2 Feb 13 21:50:10 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 3 Feb 13 21:50:10 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 3 Feb 13 21:50:10 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 3

Conclusiones (?)

  • Existe un problema como sospechaba, y no solo con la llamada SBGetScreenLockStatus . A veces, el consumidor nunca vio el estado establecido en 2 .
  • Si aumenté la demora del productor a 5 mseg, nunca vi el problema. Entonces, esto solo puede ser un problema para eventos realmente cercanos en el tiempo. Bloqueo / desbloqueo de pantalla probablemente no es gran cosa. Obviamente, los teléfonos lentos (iPhone <5) responderán de manera diferente.
  • El notifyCallback() static notifyCallback() parecía ser llamado primero , a menos que el bloque de devolución de GCD se pusiera en la cola de alta prioridad. Incluso entonces, generalmente la función de devolución de llamada estática se llamaba primero. Muchas veces, el primer método devuelto obtuvo el estado correcto ( 2 ), mientras que el segundo no. Por lo tanto, si tiene que vivir con el problema, puede elegir el mecanismo de devolución de llamada que pareció funcionar mejor (o al menos, hacer un prototipo, dentro de su aplicación).
  • No puedo decir que el parámetro suspensionBehavior hizo mucha diferencia. Dicho esto, dependiendo de cómo iOS publique los eventos, es posible que estén usando una llamada como CFNotificationCenterPostNotification que puede ignorar la solicitud de comportamiento de los consumidores.
  • Si miras este documento de Apple , verás dos cosas.

    1. Primero, notify_set_state no era parte de la API muy original
    2. En segundo lugar, el primer párrafo en ese documento dice

Referencia de la API de notificaciones de Darwin

Estas rutinas permiten que los procesos intercambien eventos de notificación sin estado .

Entonces, tal vez solo estamos tratando de hacer algo que no sea consistente con el diseño original :(

  • Si también observa el ejemplo NotificationPoster de Apple , verá que no usan notify_get_state y notify_set_state para transmitir el estado. Lo pasan con la notificación como un diccionario de información del usuario. Obviamente, si está observando los eventos iOS de Apple, no tiene control sobre cómo se publican los eventos. Pero, en aplicaciones donde puedes crear el productor y el consumidor, me mantendría alejado de notify_set_state .