web-dev-qa-db-fra.com

Android: Prendre le contrôle complet du téléphone (mode kiosque), est-ce possible? Comment?

Nous avons un programme que nous installons sur les téléphones et prêtons les téléphones aux utilisateurs pendant une certaine période. Nous aimerions que les téléphones soient utilisés uniquement pour exécuter notre application (pas d'appels téléphoniques, pas de jeux, rien du tout). Les téléphones seront enracinés.

Donc, les choses dont nous avons besoin:

  • Exécuter en plein écran, rien d'autre ne sera visible
  • Le bouton d'accueil et les autres boutons de l'appareil ne fonctionneront pas
  • Notre application s'exécutera automatiquement au démarrage

Il ne doit pas être "à l'épreuve des pirates", mais devrait être suffisant pour empêcher l'utilisateur moyen de jouer avec l'appareil.

Est-ce possible? J'ai fait des choses similaires sur Symbian et Windows Mobile mais je n'ai pas beaucoup d'expérience avec ce genre de choses sur Android. Comment cela peut-il être réalisé?

MISE À JOUR 2015: Si cela ne vous dérange pas de limiter votre application à un seul fournisseur de téléphone, Samsung a introduit le SDK KNOX qui vous permet d'accéder au mode kiosque et bien plus encore facilement sans enraciner le téléphone. Voir les détails sur: https://seap.samsung.com/developer/sdk/knox-standard-Android

33
Caner

Oui c'est possible mais vous ne pouvez pas contrôler le comportement de Home key et end call key.

pour le plein écran, ajoutez Android:theme="@Android:style/Theme.NoTitleBar.Fullscreen" à la balise d'activité dans le fichier manifeste.

Pour désactiver les appels entrants, vous devez écouter les appels téléphoniques:

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() {

    }
}

Remarque: Lors de l'arrêt du service, ne pas oublier de supprimer l'écouteur et d'ajouter ces autorisations à votre fichier manifeste:

<uses-permission Android:name="Android.permission.READ_PHONE_STATE" />

et déconnectez l'appel par programmation:

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());
}

Remarque: Ajoutez ce fichier pour déconnecter l'appel: http://dl.dropbox.com/u/31740476/ITelephony.aidl

Pour désactiver les clés que vous devez remplacer:

@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;
}

Sur la touche Accueil, l'écran d'accueil viendra, donc pour surmonter cela, vous devez implémenter un service et là, vous devez implémenter un fil infini pour relancer votre application comme ceci:

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) { }
            }
        }
    }
}

[~ # ~] modifier [~ # ~]

Vous devez ajouter l'autorisation suivante pour pouvoir mettre fin aux appels:

<uses-permission Android:name="Android.permission.CALL_PHONE" />

Et vous pouvez utiliser le fichier AIDL suivant:

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();
}
47
Vineet Shukla

La solution de Vineet fonctionne. Cependant, je pense que cela nécessite deux autorisations supplémentaires que j'ai découvertes ici

Les autorisations nécessaires sont donc

Android.permission.READ_PHONE_STATE, Android.permission.MODIFY_PHONE_STATE, Android.permission.CALL_PHONE

Même si ça a marché pour moi comme ça

<uses-permission Android:name="Android.permission.READ_PHONE_STATE"/>

<uses-permission Android:name="Android.permission.CALL_PHONE"/>

5
Zaki Choudhury

@Vineet Shukla: Le READ_PHONE_STATE l'autorisation n'est pas suffisante pour le faire fonctionner. Vous avez également besoin du CALL_PHONE autorisation de mettre fin à l'appel.

<uses-permission Android:name="Android.permission.READ_PHONE_STATE"/>
<uses-permission Android:name="Android.permission.CALL_PHONE"/>

Le MODIFY_PHONE_STATE (comme l'a dit Zaki Choudhury) est une autorisation système et ne peut être utilisé que sur des appareils enracinés et n'est requis par aucune partie du code.

1
MDTech.us_MAN

vous pouvez également y jeter un œil: https://github.com/ligi/Setec-Astronomy désolé pour l'auto-plug sans honte mais nous avons des problèmes similaires là-bas ;-)

1
ligi