Android: ¿Tomando el control completo del teléfono (modo kiosco), es posible? ¿Cómo?

Tenemos un progtwig que instalamos en los teléfonos y prestamos los teléfonos a los usuarios durante un cierto período. Nos gustaría que los teléfonos se utilicen únicamente para ejecutar nuestra aplicación (sin llamadas telefónicas, sin juegos, sin nada). Los teléfonos serán rooteados.

Entonces las cosas que necesitamos:

  • Ejecutar en pantalla completa, nada más será visible
  • El botón de inicio y otros botones del dispositivo no funcionarán
  • Nuestra aplicación se ejecutará automáticamente al inicio

No tiene que ser “a prueba de hackers”, pero debería ser suficiente para evitar que el usuario medio juegue con el dispositivo.

es posible? He hecho cosas similares en Symbian y Windows Mobile, pero no tengo mucha experiencia con esto en Android. ¿Cómo se puede lograr esto?

ACTUALIZACIÓN 2015: si no le importa limitar su aplicación a un único proveedor de telefonía, Samsung ha presentado KNOX SDK que le permite alcanzar el modo kiosco y mucho más fácilmente sin rootear el teléfono. Ver detalles en: https://seap.samsung.com/developer/sdk/knox-standard-android

Sí, es posible, pero no puede controlar el comportamiento de la Home key de Home key y la Home key end call key .

para pantalla completa agrega android:theme="@android:style/Theme.NoTitleBar.Fullscreen" a la etiqueta de actividad en el archivo de manifiesto.

Para deshabilitar la llamada entrante, necesita escuchar llamadas telefónicas:

 import android.app.Service; import android.os.IBinder; import android.telephony.PhoneStateListener; import android.telephony.TelephonyManager; public class MyPhoneStateListener extends Service{ @Override public IBinder onBind(Intent arg0) { return null; } @Override public void onCreate() { super.onCreate(); StateListener phoneStateListener = new StateListener(); TelephonyManager telephonymanager = (TelephonyManager)getSystemService(TELEPHONY_SERVICE); telephonymanager.listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE); } class StateListener extends PhoneStateListener{ @Override public void onCallStateChanged(int state, String incomingNumber) { super.onCallStateChanged(state, incomingNumber); switch(state){ case TelephonyManager.CALL_STATE_RINGING: //Disconnect the call here... break; case TelephonyManager.CALL_STATE_OFFHOOK: break; case TelephonyManager.CALL_STATE_IDLE: break; } } }; @Override public void onDestroy() { } } 

Nota: Al detener el servicio, no intente eliminar el oyente y agregue estos permisos a su archivo de manifiesto:

  

y desconecta la llamada de forma programática:

 try{ TelephonyManager manager = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE); Class c = Class.forName(manager.getClass().getName()); Method m = c.getDeclaredMethod("getITelephony"); m.setAccessible(true); ITelephony telephony = (ITelephony)m.invoke(manager); telephony.endCall(); } catch(Exception e){ Log.d("",e.getMessage()); } 

Nota: Agregue este archivo para desconectar la llamada: http://dl.dropbox.com/u/31740476/ITelephony.aidl

Para deshabilitar las claves que necesita anular:

 @Override public boolean dispatchKeyEvent(KeyEvent event) { if(KeyEvent.KEYCODE_MENU == event.getKeyCode() || KeyEvent.KEYCODE_DPAD_LEFT==event.getKeyCode() || KeyEvent.KEYCODE_DPAD_DOWN==event.getKeyCode() || KeyEvent.KEYCODE_DPAD_RIGHT==event.getKeyCode() || KeyEvent.KEYCODE_DPAD_UP==event.getKeyCode() || KeyEvent.KEYCODE_DPAD_CENTER==event.getKeyCode() || KeyEvent.KEYCODE_BACK==event.getKeyCode()) { return false; } return true; } 

Al presionar la tecla de inicio, aparecerá la pantalla de inicio, por lo que debe implementar un servicio y allí debe implementar un hilo infinito para reiniciar su aplicación de esta manera:

 public class AppTrackingService extends Service { private RunnableThread thread; private Context ctx; @Override public IBinder onBind(Intent intent) { return null; } public void onCreate(){ super.onCreate(); ctx = AppTrackingService.this; thread = new RunnableThread(); } public void onStart(Intent intent, int startid) { try{ if(thread==null) thread = new RunnableThread(); thread.startThread(); }catch(Exception e){ } } class RunnableThread extends Thread { Handler back_handler = new Handler(); boolean isContinue = false; public RunnableThread(){ isContinue = false; } public void setIsContinue(boolean val){ this.isContinue = val; } public void startThread(){ isContinue = true; start(); } public void run(){ ActivityManager actMngr = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); while(isContinue){ try{ //Maintain a boolean "isyourapprunning" to know if your app was running or not.... if(isyourapprunning){ String runningPkg = actMngr.getRunningTasks(1).get(0).topActivity.getPackageName(); if (!runningPkg.equals(ctx.getPackageName())){ launchApp(ctx.getPackageName()); } Thread.sleep(2500); //2.5 secs }else{ isContinue = false; stopSelf(); } }catch(Exception e){ } }//end of while loop } protected void launchApp(String packageName) { Intent mIntent = getPackageManager().getLaunchIntentForPackage(packageName); mIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); mIntent.addFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); if (null != mIntent) { try { startActivity(mIntent); } catch(Exception e) { } } } } } 

EDITAR

Debería agregar el siguiente permiso para poder finalizar llamadas:

  

Y puede usar el siguiente archivo AIDL:

 package com.android.internal.telephony; /** * Interface used to interact with the phone. Mostly this is used by the * TelephonyManager class. A few places are still using this directly. * Please clean them up if possible and use TelephonyManager instead. * * {@hide} */ interface ITelephony { /** * End call if there is a call in progress, otherwise does nothing. * * @return whether it hung up */ boolean endCall(); /** * Silence the ringer if an incoming call is currently ringing. * (If vibrating, stop the vibrator also.) * * It's safe to call this if the ringer has already been silenced, or * even if there's no incoming call. (If so, this method will do nothing.) * * TODO: this should be a oneway call too (see above). * (Actually *all* the methods here that return void can * probably be oneway.) */ void silenceRinger(); } 

La solución de Vineet funciona. Sin embargo, creo que requiere dos permisos más que descubrí de aquí

Entonces los permisos necesarios son

android.permission.READ_PHONE_STATE, android.permission.MODIFY_PHONE_STATE, android.permission.CALL_PHONE

Aunque me funcionó así

También puedes echar un vistazo a esto: https://github.com/ligi/Setec-Astronomy, disculpa por el autoenchufe sin vergüenza, pero tenemos problemas similares allí 😉

@Vineet Shukla: El permiso READ_PHONE_STATE no es suficiente para que funcione. También necesita el permiso CALL_PHONE para finalizar la llamada.

   

El MODIFY_PHONE_STATE (según lo dicho por Zaki Choudhury) es un permiso del sistema y solo se puede usar en dispositivos rooteados y no es necesario para ninguna parte del código.