# MyAgent **Repository Path**: neo-android/my-agent ## Basic Information - **Project Name**: MyAgent - **Description**: 安卓android实现开机自启、远程关机、远程重启demo - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 7 - **Forks**: 6 - **Created**: 2022-08-25 - **Last Updated**: 2025-03-14 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 使用websocket实现android在线状态监控,远程实现关机、重启 # 项目结构 ``` └── com └── tiny └── myagent ├── broadcast // 接收开机广播,实现开机自启动 ├── enums // 关机、重启枚举 ├── MainActivity.java // 主类 ├── util // 工具类 └── websocket // web-socket连接 ``` # 一、web-socket ## 1、web-socket依赖 安卓端使用`Java-WebSocket:1.4.0`依赖,创建web-socket连接 ```yml dependencies { implementation 'androidx.appcompat:appcompat:1.3.0' implementation 'com.google.android.material:material:1.4.0' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' implementation "org.java-websocket:Java-WebSocket:1.4.0" testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.3' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' } ``` ## 2、创建web-socket客户端 ```java package com.tiny.myagent.im; import android.util.Log; import org.java_websocket.client.WebSocketClient; import org.java_websocket.drafts.Draft_6455; import org.java_websocket.handshake.ServerHandshake; import java.net.URI; public class JWebSocketClient extends WebSocketClient { public JWebSocketClient(URI serverUri) { super(serverUri, new Draft_6455()); } @Override public void onOpen(ServerHandshake handshakedata) { Log.e("JWebSocketClient", "onOpen()"); } @Override public void onMessage(String message) { Log.e("JWebSocketClient", "onMessage()"); } @Override public void onClose(int code, String reason, boolean remote) { Log.e("JWebSocketClient", "onClose()"); } @Override public void onError(Exception ex) { Log.e("JWebSocketClient", "onError()" + ex.getMessage()); } } ``` ## 3、创建JWebSocketClientService继承Service ```java /** * 初始化websocket连接 */ private void initSocketClient() { URI uri = URI.create(Util.ws); client = new JWebSocketClient(uri) { @Override public void onMessage(String message) { Log.e("JWebSocketClientService", "收到的消息:" + message); BootAction action = BootAction.getReboot(message); if (action != null) { BootUtil.downAndReboot(action); } Intent intent = new Intent(); intent.setAction("com.tiny.myagent"); intent.putExtra("message", message); sendBroadcast(intent); } @Override public void onOpen(ServerHandshake handshakedata) { super.onOpen(handshakedata); Log.e("JWebSocketClientService", "websocket连接成功"); } }; connect(); } ``` ## 4、创建线程检测心跳 ​ ```java private static final long HEART_BEAT_RATE = 10 * 1000;//每隔10秒进行一次对长连接的心跳检测 private Handler mHandler = new Handler(); private Runnable heartBeatRunnable = new Runnable() { @Override public void run() { Log.e("JWebSocketClientService", "心跳包检测websocket连接状态"); try { sendMsg("这里是客户端MAC:==>" + NetWorkUtil.getDeviceMacAddress()); } catch (Exception e) { Log.e("获取mac地址异常", e.toString()); } if (client != null) { if (client.isClosed()) { reconnectWs(); } } else { //如果client已为空,重新初始化连接 client = null; initSocketClient(); } //每隔一定的时间,对长连接进行一次心跳检测 mHandler.postDelayed(this, HEART_BEAT_RATE); } }; /** * 开启重连 */ private void reconnectWs() { mHandler.removeCallbacks(heartBeatRunnable); new Thread() { @Override public void run() { try { Log.e("JWebSocketClientService", "开启重连"); client.reconnectBlocking(); } catch (InterruptedException e) { e.printStackTrace(); } } }.start(); } ``` ## 5、配置websocket地址 目前`websocket`的服务地址在util/Util类中,可以配置自己的服务端地址(可以使用Java的springboot快速实现服务) ```java public class Util { //websocket测试地址 public static final String ws = "ws://ip:port/websocket/neo"; public static void showToast(Context ctx, String msg) { Toast.makeText(ctx, msg, Toast.LENGTH_LONG).show(); } } ``` # 二、开机自启实现 ## 1、添加权限 在`AndroidManifest.xml`中添加权限 ```xml ``` ## 2、实现静态广播 接收广播 ```java package com.tiny.myagent.broadcast; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.util.Log; public class BootCompleteReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Log.i("开机自启==", "我接收到广播啦"); PackageManager packageManager = context.getPackageManager(); intent = packageManager.getLaunchIntentForPackage("com.tiny.myagent"); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(intent); /*Intent it = new Intent(context, MainActivity.class); it.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(it);*/ switch (intent.getAction()) { case Intent.ACTION_BOOT_COMPLETED: Log.i("开机自启==", "手机开机了"); break; case Intent.ACTION_SHUTDOWN: Log.i("开机自启==", "手机关机了"); break; case Intent.ACTION_SCREEN_ON: Log.i("开机自启==", "亮屏"); break; case Intent.ACTION_SCREEN_OFF: Log.i("开机自启==", "息屏"); break; case Intent.ACTION_USER_PRESENT: Log.i("开机自启==", "手机解锁"); break; } } } ``` # 三、实现结果 目前测试了`android 7.1.1 `、`android 9.0`、`android 10.0`三个版本,均可实现开机自启、关机、重启🚗🚗🚗 ## 1、提前准备SDK 由于之前没写过Android代码,经过测试需要先下载好对应版本的SDK,目前还不知道具体原因。 ![image-20220830221907686](doc/img/1.png) ## 2、配置plateform签名密钥对 由于实现关机和重启需要系统权限,所以需要在`AndroidManifest.xml`中配置系统权限 ![image-20220830222656858](doc/img/2.png) 配置该权限后需要使用`android`的密钥对进行签名,目前使用的是原生安卓的密钥对,如果使用厂商的板子,需要厂商提供该密钥对。可以在该地址获取[target/product/security - platform/build - Git at Google (googlesource.com)](https://android.googlesource.com/platform/build/+/refs/heads/nougat-release/target/product/security/) ![image-20220830223156248](doc/img/3.png) ## 3、生成密钥 首先下载工具https://github.com/getfatday/keytool-importkeypair.git 生成命令 ```shell sh keytool-importkeypair -k giftedcat.jks -p 123456 -pk8 platform.pk8 -cert platform.x509.pem -alias agent #解释 sh keytool-importkeypair -k 密钥名称 -p 密码 -pk8 platform.pk8 -cert platform.x509.pem -alias 别名 ``` ## 4、配置密钥 编辑`app`下的`build.gradle`文件 ```yaml signingConfigs { debug { storeFile file('../key/giftedcat.jks') storePassword '123456' keyAlias 'agent' keyPassword '123456' } release { storeFile file('../key/giftedcat.jks') storePassword '123456' keyAlias 'agent' keyPassword '123456' } } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' signingConfig signingConfigs.release } debug { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' signingConfig signingConfigs.debug } } ``` # 四、测试 ## 1、心跳检测 ![image-20220830224011176](doc/img/4.png) ## 2、服务端收到消息 ![image-20220830224050646](doc/img/5.png) ## 3、远程控制--发送消息 ![image-20220830224215011](doc/img/6.png) ## 4、客户端收到消息后执行关机 ![image-20220830224145817](doc/img/7.png) # 五、对现有的apk进行签名 使用`sign-util`下的工具可以对现有的apk安装包进行签名 ```shell java -jar signapk.jar platform.x509.pem platform.pk8 签名前.apk 签名后.apk ``` # 六、致谢 本代码参考:https://gitee.com/richardxiong/web-socket.git