Android: Возможно ли получить полный контроль над телефоном (режим киоска)? Как? - PullRequest
33 голосов
/ 19 августа 2011

У нас есть программа, которую мы устанавливаем на телефоны и предоставляем телефоны пользователям на определенный срок. Мы бы хотели, чтобы телефоны использовались исключительно для запуска нашего приложения (без телефонных звонков, без игр, без ничего). Телефоны будут рутированы.

Итак, что нам нужно:

  • Запуск на весь экран, больше ничего не будет видно
  • Кнопка «Домой» и другие кнопки устройства не работают
  • Наше приложение будет автоматически запускаться при запуске

Это не должно быть «доказательством хакера», но должно быть достаточным, чтобы обычный пользователь не связывался с устройством.

Возможно ли это? Я делал подобные вещи на Symbian и Windows Mobile, но у меня мало опыта с этим на Android. Как этого достичь?

ОБНОВЛЕНИЕ 2015: Если вы не возражаете ограничивать свое приложение одним поставщиком телефонов, Samsung представила KNOX SDK, который позволяет вам переходить в режим киоска и намного проще, не рутируя телефон. Подробности см .: https://seap.samsung.com/developer/sdk/knox-standard-android

Ответы [ 4 ]

47 голосов
/ 19 августа 2011

Да, это возможно, но вы не можете контролировать поведение Home key и end call key.

для полноэкранного режима. Добавьте android:theme="@android:style/Theme.NoTitleBar.Fullscreen" в тег активности в файле манифеста.

Вчтобы отключить входящий вызов, вам нужно прослушать телефонные звонки:

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

    }
}

Примечание. При остановке службы не забудьте удалить слушателя и добавить эти разрешения в файл манифеста:

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

и программно отключите вызов:

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

Примечание. Добавьте этот файл для отключения вызова: http://dl.dropbox.com/u/31740476/ITelephony.aidl

Чтобы отключить клавиши, необходимо переопределить:

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

При нажатии клавиши «Домой» откроется главный экран, поэтому для преодоления этого вам нужно внедрить службу, и там вам нужно создать бесконечный поток для перезапуска вашего приложения, например:

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

РЕДАКТИРОВАТЬ

Для завершения вызовов вам необходимо добавить следующее разрешение:

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

И вы можете использовать следующий файл 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();
}
5 голосов
/ 08 июня 2012

Решение Vineet работает.Однако я думаю, что требуется еще два разрешения, которые я узнал из здесь

Так что необходимые разрешения

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

Хотя у меня это работало вот так

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

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

1 голос
/ 08 января 2014

@ Vineet Shukla: разрешения READ_PHONE_STATE недостаточно, чтобы заставить его работать. Вам также необходимо разрешение CALL_PHONE для завершения вызова.

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

MODIFY_PHONE_STATE (как сказал Заки Чоудхури) является системным разрешением и может использоваться только на корневых устройствах и не требуется ни в одной части кода.

1 голос
/ 03 февраля 2012

Вы также можете взглянуть на это: https://github.com/ligi/Setec-Astronomy извините за бесстыдную самоподключение, но у нас там схожие проблемы; -)

...