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"/>
幸运的是,原作者已经有了这部分代码,我们只需要增加对开机启动广播的响应即可:
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 函数
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,说明连接有问题就自动重连。
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 讨论下。

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