Android:完全控制手机(Kiosk模式),可以吗?怎么样?

问题描述:

我们有一个程序,我们安装在手机上,并在一段时间内将手机借给用户。我们希望手机仅用于运行我们的应用程序(不需要电话,不用游戏,也不需要任何操作)。手机将被植根。Android:完全控制手机(Kiosk模式),可以吗?怎么样?

所以这个事情我们需要:在全屏

  • 运行,不出意外将是可见的
  • Home键和其他设备的按钮将无法工作
  • 我们的应用程序将在启动
  • 自动运行

它不必是“黑客证明”,但应足以防止普通用户弄乱设备。

这可能吗?我在Symbian的& Windows Mobile上做过类似的事情,但我在Android上没有太多经验。这可能如何实现?

UPDATE 2015年:如果你不介意限制你的应用单一的手机厂商,三星推出了KNOX SDK,可以让你实现kiosk模式和更容易生根没有手机。详情请参阅:https://seap.samsung.com/developer/sdk/knox-standard-android

是的,这是可能的,但您无法控制Home keyend 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() { 

    } 
} 

注:在停止服务不foget删除监听器和这些权限添加到您的清单文件:

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

注意:将此f ILE断开呼叫: 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; 
} 

在Home键按下主屏幕会来,所以要克服这一点,你需要实现一个服务有你需要实现一个无限的线程来重新启动您的应用程序是这样的:

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

我无法使用.aidl文件。有什么问题吗? –

+0

您需要将.aidl文件添加到您的项目中。您遇到了什么问题?请参阅此链接:http://developer.android.com/guide/developing/tools/aidl。html –

+2

我将该文件添加到我的源项目中,但它在ITelephony telephony =(ITelephony)m.invoke(manager)行中给出错误,而ITelephony未定义。 –

你也可以看看这个:https://github.com/ligi/Setec-Astronomy 对无羞耻的自我插件感到抱歉,但我们有类似的问题;-)

+1

+1。这对我有些帮助!谢谢叉。不断分享这些有用的信息。像你这样的开发者完全可以使开源成为现实。 – skygeek

+0

我有一个问题,假设如果我阻止谷歌市场,并有人正在玩任何比赛像赛车,并达到谷歌市场你写的代码是行之有效的,它退出到主发射器,但是当我重新打开游戏时,它显示最后的inapp购买屏幕当代码杀死的时候我离开了。你能帮我解决这个问题吗? – skygeek

Vineet的解决方案的作品。但是我认为,这需要两个权限,我从 here

发现那么所需的权限是

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"/>

@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的(如由扎基乔杜里所述)是一个系统权限,而且可以仅在根设备使用,并且不通过代码的任何部分需要的。