二、实现步骤

Auto.js 和 AutoJs Web Control 联合起来可以实现免root的Android云控。 但是在实际使用还需要解决以下几个问题:

  • app开机自动启动和自动解锁
  • app免root自动获取无障碍权限
  • app和server通讯支持心跳,断线重连

二、实现步骤

app开机自动启动和自动解锁

注册一个静态广播去监听开机启动完毕的广播,然后在监听到开机广播完毕后启动 Auto.js的主Activity或者Service;

首先需要定义一个继承自BroadcastReceiver的开机广播接收者BaseBroadcastReceiver,然后在AndroidManifest.xml文件中增加对应的权限;

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

幸运的是,原作者已经有了这部分代码,我们只需要增加对开机启动广播的响应即可:

BaseBroadcastReceiver.java
    private static final String LOG_TAG = "BaseBroadcastReceiver";
	// 启动
    private final String ACTION_BOOT = "android.intent.action.BOOT_COMPLETED";

    @SuppressLint("CheckResult")
    public void onReceive(Context context, Intent intent) {
        Log.d(LOG_TAG, "onReceive: intent = " + intent + ", this = " + this);
        try {
            TimedTaskManager.getInstance().getIntentTaskOfAction(intent.getAction())
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(intentTask -> runTask(context, intent, intentTask), Throwable::printStackTrace);

            // add fenfei 开机启动后台服务
            // BaseBroadcastReceiver: onReceive: intent = Intent { act=android.intent.action.BOOT_COMPLETED
            if (ACTION_BOOT.equals(intent.getAction()))
            {
                Log.d("fenfei onReceive:", "unLock");

                // 获取电源管理器对象
                PowerManager pm = (PowerManager) context
                        .getSystemService(Context.POWER_SERVICE);
                // 获取PowerManager.WakeLock对象,后面的参数|表示同时传入两个值,最后的是调试用的Tag
                PowerManager.WakeLock wl = pm.newWakeLock(
                        PowerManager.ACQUIRE_CAUSES_WAKEUP
                                | PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "bright");
                // 点亮屏幕
                wl.acquire(10000);
                // 释放
                wl.release();

                // 得到键盘锁管理器对象
                KeyguardManager km = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
                KeyguardManager.KeyguardLock kl = km.newKeyguardLock("unLock");
                // 解锁
                kl.disableKeyguard();
                // end


                Log.d("fenfei onReceive:", "Boot system Begin");
				// 启动开始屏幕
                Intent startIntent = new Intent(context, SplashActivity.class);
                startIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                context.startActivity(startIntent);
                Log.d("fenfei onReceive:", "Boot system Finish");

                // Intent i = new Intent(context, AlarmRunningService.class);
                // context.startService(i);
            }
            // end

        } catch (Exception e) {
            GlobalAppContext.toast(e.getMessage());
        }
    }

app免root自动获取无障碍权限(需adb授权,重启手机不失效)

在src/main/java/org/calibur/stars/autojs/AutoJs.java 和 src/main/java/org/autojs/autojs/ui/main/MainActivity.java 两个文件中增加openAccessibilityByAdb 函数

autojs.java
public boolean openAccessibilityByAdb() {
        try {
            String enabledService = Settings.Secure.getString(GlobalAppContext.get().getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
            String service = enabledService + ":org.calibur.stars/com.stardust.autojs.core.accessibility.AccessibilityService";
            Settings.Secure.putString(GlobalAppContext.get().getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, service);
            Settings.Secure.putString(GlobalAppContext.get().getContentResolver(), Settings.Secure.ACCESSIBILITY_ENABLED, "1");
            return true;
        } catch (Exception e) {
            //adb shell pm grant org.calibur.stars android.permission.WRITE_SECURE_SETTINGS
            return false;
        }
    }

把手机设置成开发者模式,连上电脑运行

adb shell pm grant org.calibur.stars android.permission.WRITE_SECURE_SETTINGS

这样就可以自动获取无障碍模式了,重启之后依然有效。 记得运行完之后把开发者模式关掉。

app和server通讯支持心跳,断线重连

原版app WebSocket不是很稳定,经常会掉线,所以这里我们增加一个定时心跳,每分钟向服务器sayHello,服务器收到之后返回Hello,如果2分钟都没有收到服务器返回的hello,说明连接有问题就自动重连。

src/main/java/org/calibur/stars/pluginclient/DevPluginService.java
    public void sayHelloToServer() {
        if (mSocket != null) {
            Log.i("fenfei", "sayHelloToServer");

            String format = "HH:mm:ss";
            DateTime curData = DateTime.now();

            if(RcDate != null) {
                long minutes = Minutes.minutesBetween(RcDate,curData).getMinutes() ;
                Log.i("fenfei", "curData_"+ curData.toString(format) + " - RcDate_" + RcDate.toString(format) +" = " + minutes);

                // 2分钟以上 没有得到服务器的回复心跳就说明掉线了, 开始重连
                if(minutes > 2){
                    // restartScreenAndApp();
                    if (mSocket != null) {
                        mSocket.close();
                        mSocket = null;
                    }

                    // mConnectionState.onNext(new State(State.DISCONNECTED, new java.io.EOFException()));

                    // 重新连接
                    String host = Pref.getServerAddressOrDefault(WifiTool.getRouterIp(GlobalAppContext.get()));

                    // 2分钟服务器都没有响应, 直接重新连接
                    Log.i("fenfei", "mSocket timeOut, sayHelloToServer, connectToServer: "+ host);

                    this.connectToServer(host)
                            .subscribe(Observers.emptyConsumer(), this::onSocketError);

                    return;
                }
            }

            try {
                int iCount = AutoJs.getInstance().getScriptEngineService().getCount();
                writeMap(mSocket, TYPE_HELLO, new MapBuilder<String, Object>()
                        .put("device_name", Build.BRAND + " " + Build.MODEL)
                        .put("client_version", CLIENT_VERSION)
                        .put("app_version", BuildConfig.VERSION_NAME)
                        .put("app_version_code", BuildConfig.VERSION_CODE)
                        .put("run_script_count",iCount)
                        .build());
            } catch (Exception e) {
                this.onSocketError(e);
                // e.printStackTrace();
            }
        }
        else
        {
            // restartScreenAndApp();
            // 重新连接
            String host = Pref.getServerAddressOrDefault(WifiTool.getRouterIp(GlobalAppContext.get()));
            Log.i("fenfei", "mSocket == null, sayHelloToServer, connectToServer: "+ host);
            this.connectToServer(host)
                    .subscribe(Observers.emptyConsumer(), this::onSocketError);
            // mConnectionState.onNext(new State(State.DISCONNECTED, new java.io.EOFException()));
        }

    }

本文涉及到的代码项目可以去 奋飞的朋友们 知识星球自取,欢迎加入知识星球一起学习探讨技术。有问题可以加我wx: fenfei331 讨论下。

100

关注微信公众号,最新技术干货实时推送

100