Android, obteniendo la señal de intensidad (PhoneStateListener) mientras el dispositivo está en modo de suspensión

Tengo problema y después de alguna búsqueda no he encontrado ninguna solución positiva. Después de la investigación tengo la idea de que no hay aplicación para mi problema, pero esta pregunta puede ser es mi última oportunidad.

¿Qué necesito obtener?

Hay aplicación que obtiene información sobre la señal de intensidad de red móvil. Lo hago por PhoneStateListener . Por supuesto, funciona muy bien, pero cuando mi dispositivo pasa al modo de suspensión, el oyente no funciona:

Https://code.google.com/p/android/issues/detail?id=10931 https://code.google.com/p/android/issues/detail?id=7592

WakeLock soluciona el problema sólo en caso de que el dispositivo se apague por tiempo de espera. En caso de que cuando presione el botón de encendido, el dispositivo obtendrá el modo de suspensión también. No podemos anular la acción del botón de encendido.

Mi meta es conseguir la señal de la fuerza siempre cuando mi dispositivo está permitido. No importa cuál sea el modo. Todo el tiempo debe recolectar datos.

Pregunta:

¿Hay alguna idea? ¿Cómo lograrlo? ¿Hay maneras de hacer esto o puede ser que hay algunos hacks? Todas las soluciones son bienvenidas. Si usted tuvo alguna experiencia útil, por favor comparta esto.

Gracias a todos por la ayuda! Espero que este tema obtenga información completa sobre este problema.

Administrador de alarmas es el camino a seguir – la parte difícil es mantener el teléfono despierto después de que el receptor del administrador de alarmas regrese. Asi que

  • Configurar una alarma (observe que también debe registrar un receptor "On Boot completed" para configurar la alarma después de un reinicio – sus alarmas no sobreviven a un reinicio):

     Intent monitoringIntent = new Intent(context, YourReceiver.class); monitoringIntent.setAction("your action"); PendingIntent pi = PendingIntent.getBroadcast(context, NOT_USED, monitoringIntent, PendingIntent.FLAG_UPDATE_CURRENT); AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); // here is the alarm set up am.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + INITIAL_DELAY, INTERVAL_BETWEEN_ALARMS, pi); 
  • Recibirlo – el receptor tiene un WakeLock en su onReceive() que nunca falla:

     public abstract class YourReceiver extends BroadcastReceiver { @Override final public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); if ("your action".equals(action)) { // monitoring - got broadcast from ALARM try { d("SS : " + new Signal().getSignalStrength(context)); } catch (InterruptedException e) { e.printStackTrace(); } // Actu8ally the lines above will ANR // I did it with WakefulIntentService : // WakefulIntentService.sendWakefulWork( // context, YourWakefulService.class); // Will be posting it asap } else { w("Received bogus intent : " + intent); return; } } } 

    Si tienes suerte (yourRetrieveSignal () es lo suficientemente rápido) esto funcionará, de lo contrario necesitarás un patrón (Wakeful) IntentService en tu receptor.
    El WakefulIntentService se encargará del bloqueo de la vigilia (si desea evitar una dependencia eche un vistazo aquí ) – EDIT: tenga en cuenta que no puede definir los oyentes en un servicio de intención – vea aquí .

Si el receptor ANRs en ti, tienes que probar el patrón WakefulIntentService. En cualquier caso, puede utilizar esto :

Esto demostró ser la parte más difícil en realidad:

 class Signal { static volatile CountDownLatch latch; //volatile is an overkill quite probably static int asu; private final static String TAG = Signal.class.getName(); int getSignalStrength(Context ctx) throws InterruptedException { Intent i = new Intent(TAG + ".SIGNAL_ACTION", Uri.EMPTY, ctx, SignalListenerService.class); latch = new CountDownLatch(1); asu = -1; ctx.startService(i); Log.d(TAG, "I wait"); latch.await(); ctx.stopService(i); return asu; } } 

dónde :

 public class SignalListenerService extends Service { private TelephonyManager Tel; private SignalListener listener; private final static String TAG = SignalListenerService.class.getName(); private static class SignalListener extends PhoneStateListener { private volatile CountDownLatch latch; private SignalListener(CountDownLatch la) { Log.w(this.getClass().getName(), "CSTOR"); this.latch = la; } @Override public void onSignalStrengthChanged(int asu) { Signal.asu = asu; latch.countDown(); } } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.w(TAG, "Received : " + intent.getAction()); Tel = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); listener = new SignalListener(Signal.latch); @SuppressWarnings("deprecation") final int listenSs = PhoneStateListener.LISTEN_SIGNAL_STRENGTH; Tel.listen(listener, listenSs); return START_STICKY; } @Override public void onDestroy() { Log.w(TAG, "onDestroy"); Tel.listen(listener, PhoneStateListener.LISTEN_NONE); super.onDestroy(); } @Override public IBinder onBind(Intent intent) { return null; } } 

Este es el código de trabajo (pero no el pináculo de la elegancia de hecho – comentarios / correcciones bienvenido). No olvide registrar sus servicios en el manifiesto y adquirir permisos.
EDITAR 2013.07.23: No onReceive el onReceive – si lo usas será ANR – este es el código de trabajo si usas WakefulIntentService en onReceive y ahí llamas SignalListenerService .

Desde mi comprensión de PhoneStateListener no se puede hacer esto mientras la CPU de la aplicación está en modo de suspensión. Puede mantener el dispositivo despierto, lo que arruinaría la vida de la batería. Alternativamente, puede utilizar una alarma (consulte AlarmManager ) para activar el dispositivo en intervalos, de modo que pueda recopilar los datos (impacta la duración de la batería).

Algunas muestras de AlarmManager se pueden encontrar aquí

El ejemplo de polling de la ubicación de CommonsWare es realmente bueno acerca de despertar el teléfono y ponerlo de nuevo a dormir. Creo que podría ayudar a echar un vistazo: https://github.com/commonsguy/cwac-locpoll

Una de las soluciones posibles del problema android 10931 es enviar la intención android.intent.action.SCREEN_ON al proceso 'phone' después de que la pantalla se apagara.

  1. Crear y registrar BroadcastReceiver para escuchar las notificaciones cuando la pantalla se apaga

     start(Context context) { IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_SCREEN_OFF); context.registerReceiver(mScreenReceiver, filter); } final BroadcastReceiver mScreenReceiver = new BroadcastReceiver() { @Override public void onReceive(final Context context, final Intent intent) { if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) { Log.v(LOGTAG, "Screen is off. Running workaround"); new Thread(mReportScreenIsOnRunnable).start(); } } }; 
  2. Envíe la intención SCREEN_ON al proceso del teléfono solamente.

     public final Runnable mReportScreenIsOnRunnable = new Runnable() { @Override public void run() { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } try { Runtime.getRuntime().exec(new String[] { "su", "-c", "am broadcast -a android.intent.action.SCREEN_ON com.android.phone" }); } catch (IOException e) { e.printStackTrace(); } } }; 

Después de recibir este intento el proceso del teléfono reanudaría el envío de las actualizaciones de la localización de la célula.

Se requieren privilegios raíz.

Esta solución es un poco hacky, peligroso y no funciona en todos los teléfonos. Puede conducir a un mayor consumo de energía, pero no mucho más que si mantiene la pantalla encendida.

FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.