实现IP冲突检测功能,首先想到的命令是
arping -f -D -w 1 -I eth0 xx.xx.xx.xx
如果直接在APP端运行这个命令,会发现运行失败,原因是没有root权限,因此首先要解决的是root权限的问题。有两种方式解决这个问题:
1、使用ps -ef |grep root命令找到一个具有root权限的厂商自定义服务,在此服务中开放一个运行命令的接口,APP通过绑定这个服务达到运行root命令的目的。(实际开发中在vendor下面找了一个服务,已具备root权限,发现依然运行不了arping命令,猜测可能是root权限不彻底导致)
2、自定义一个系统服务
(1)在android/external/下新增cmdserver文件夹
code/android/external/cmdserver/Android.bp 0 → 100644
cc_binary {
name: "cmdserver",
cflags: ["-Werror"],
local_include_dirs: [
"./",
],
srcs: [
"CmdServer.cpp",
"ICmdManager.cpp",
],
shared_libs: [
"libcutils",
"liblog",
"libc",
"libandroid",
"libutils",
"libbinder",
],
}
\ No newline at end of file
code/android/external/cmdserver/CmdServer.cpp 0 → 100755
#include "ICmdManager.h"
#include <android/log.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <string.h>
#include <sys/wait.h>
#define UNUSED(x) (void)(x)
#define TAG "cmdserver"
#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__))
#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__))
int super_system(const char * cmd, char *retmsg, int msg_len, int isNoBlock);
namespace android
{
class BnCmdManager : public BnInterface<ICmdManager>
{
public:
virtual status_t
onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0);
virtual int runCmd(const char* str, char *retmsg, int msg_len, int isNoBlock);
};
status_t BnCmdManager::onTransact(uint_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
UNUSED(flags);
char retmsg[2048] = {0};
switch (code)
{
case RUNCMD:
{
CHECK_INTERFACE(ICloudManager, data, reply);
String16 str = data.readString16();
int isNoBlock = data.readInt32();
String8 str8 = String8(str);
int ret = runCmd(str8.string(), retmsg, sizeof(retmsg), isNoBlock);
reply -> writeInt32(ret);
//LOGI("result is %s, %d\n", retmsg, ret);
reply->writeString16(String16(retmsg));
return NO_ERROR;
}
break;
default:
break;
}
return NO_ERROR;
}
int BnCmdManager::runCmd(const char* str, char *retmsg, int msg_len, int isNoBlock) {
//LOGI("Now server receive requset from client: [call runCmd]->%s.", str);
return super_system(str, retmsg, msg_len, isNoBlock);
}
}
//execute shell command
int super_system(const char * cmd, char *retmsg, int msg_len, int isNoBlock)
{
//LOGI("shexec, cmd: %s\n", cmd);
FILE *pp = popen(cmd, "r");
if(!pp) {
LOGE("error, cannot popen cmd: %s\n", cmd);
return -1;
}
int i = 0;
int len = 0;
char tmp[LINE_LENT];
LOGI("isnoblock:%d \n", isNoBlock);
while(!isNoBlock && fgets(tmp, sizeof(tmp), pp) != NULL) {
LOGI("%d.get return results: %s\n", i, tmp);
len = strlen(tmp);
if((i+len) <= (msg_len-2)) {
memcpy(retmsg+i, tmp, len);
i += len;
}
}
retmsg[i] = '\0';
int rv = pclose(pp);
LOGI("ifexited: %d\n", WIFEXITED(rv));
if (WIFEXITED(rv))
{
LOGI("subprocess exited, exit code: %d\n", WEXITSTATUS(rv));
}
return WEXITSTATUS(rv);
}
int main() {
LOGI("to start cmdserver");
sp<ProcessState> proc(ProcessState::self());
sp<IServiceManager> sm = defaultServiceManager();
sm->addService(String16("cmdserver"), new BnCmdManager());
LOGI("Native binder cmdserver starts to work.");
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
return 0;
}
code/android/external/cmdserver/ICmdManager.cpp 0 → 100755
#include "ICmdManager.h"
namespace android
{
IMPLEMENT_META_INTERFACE(CmdManager, "com.test.ICmdManager");
}
code/android/external/cmdserver/ICmdManager.h 0 → 100755
#ifndef ICMDMANAGER_H_H
#define ICMDMANAGER_H_H
#include <stdio.h>
#include <binder/IInterface.h>
#include <binder/Parcel.h>
#include <binder/IBinder.h>
#include <binder/Binder.h>
#include <binder/ProcessState.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#define LINE_TOUNT 100
#define LINE_LENT 512
using namespace android;
namespace android
{
class ICmdManager : public IInterface
{
public:
DECLARE_META_INTERFACE(CmdManager); // declare macro
virtual int runCmd(const char* str, char *retmsg, int msg_len, int isNoBlock)=0;
};
enum
{
RUNCMD = IBinder::FIRST_CALL_TRANSACTION+1,
};
class BpCmdManager: public BpInterface<ICmdManager> {
public:
BpCmdManager(const sp<IBinder>& impl);
virtual int runCmd(const char* str, char *retmsg, int msg_len, int isNoBlock);
};
}
#endif
如果碰到binder的编译问题:
code/android/frameworks/native/libs/binder/include/binder/IInterface.h
@@ -292,6 +292,7 @@ constexpr const char* const kManualInterfaces[] = {
"IAAudioClient",
"IAAudioService",
"VtsFuzzer",
"com.test.ICmdManager",
nullptr,
};
(2)把服务新增到编译device.mk
PRODUCT_PACKAGES += \
cmdserver
(3) 在init.rc中新增
#cmdserver
on property:sys.boot_completed=1
start cmdserver
service cmdserver /system/bin/cmdserver
user root
class main
oneshot
seclabel u:r:cmdserver:s0
至此服务端便可启动。
(4)实现客户端
java/app/src/main/java/com/test/setting/app/SettingApplication.java
@@ -4,6 +4,7 @@ import android.app.Application;
import android.util.DisplayMetrics;
import com.tencent.mars.xlog.Xlog;
import com.test.setting.manager.SysManager;
import com.test.setting.utils.MLog;
import com.test.setting.utils.SharedPreUtils;
import com.test.setting.utils.ToastHelper;
@@ -24,6 +25,7 @@ public class SettingApplication extends Application {
MLog.init(Xlog.LEVEL_VERBOSE, logPath, logPath);
SharedPreUtils.init(getApplicationContext(), "SettingConfig");
ToastHelper.getInstance().init(this);
SysManager.initInstance(this);
// 修改全局的Density
DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
java/app/src/main/java/com/test/setting/constants/EventKey.java
@@ -25,4 +25,10 @@ public class EventKey {
*/
public static final int MSG_ID_OPEN_SETTING = MSG_ID_REFRESH_HOME_APP + 1;
/**
* IP冲突
*/
public static final int MSG_ID_IP_CONFLICT = MSG_ID_OPEN_SETTING + 1;
}
java/app/src/main/java/com/test/setting/manager/CmdManagerProxy.java 0 → 100644
package com.test.setting.manager;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
public class CmdManagerProxy implements ICmdManager {
private IBinder mRemote;
public CmdManagerProxy(IBinder remote) {
mRemote = remote;
}
public String getInterfaceDescriptor() {
return DESCRIPTOR;
}
@Override
public IBinder asBinder() {
return mRemote;
}
@Override
public String runCmd(String str, int isBusy) throws RemoteException {
android.os.Parcel data = android.os.Parcel.obtain();
android.os.Parcel reply = android.os.Parcel.obtain();
StringBuilder result = new StringBuilder();
int ret = 0;
if (null == mRemote) {
Log.e("CmdManagerProxy", "invalid mRemote");
return null;
}
try {
data.writeInterfaceToken(DESCRIPTOR);
data.writeString(str);
data.writeInt(isBusy);
if (isBusy == 1) {
Log.i("CmdManagerProxy", "isBusy: " + isBusy);
}
mRemote.transact(TRANSACTION_run, data, reply, 0);
//reply.readException();
if (null != reply) {
ret = reply.readInt();
result.append(reply.readString());
result.append("#").append(ret).append("%");
}
//Log.i("CmdManagerProxy", "int ret=" + ret);
} finally {
if (null != reply) {
reply.recycle();
}
data.recycle();
}
return result.toString();
}
}
java/app/src/main/java/com/test/setting/manager/ICmdManager.java 0 → 100644
package com.test.setting.manager;
import android.os.IInterface;
import android.os.RemoteException;
public interface ICmdManager extends IInterface {
static final String DESCRIPTOR = "com.test.ICmdManager";
String runCmd(String str,int isBusy) throws RemoteException;
static final int TRANSACTION_run = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
java/app/src/main/java/com/test/setting/manager/SysManager.java 0 → 100644
package com.test.setting.manager;
import android.annotation.SuppressLint;
import android.content.Context;
import android.os.IBinder;
import com.test.setting.constants.EventKey;
import com.test.setting.rxbus.RxBus;
import com.test.setting.utils.MLog;
import com.test.setting.utils.NetworkUtil;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;
import io.reactivex.Observable;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Consumer;
import io.reactivex.schedulers.Schedulers;
/**
* 系统设置管理类
*
* @author Eric li 2018-6-23
*/
public class SysManager {
private final String TAG = "SysManager";
private static SysManager singleton;
private Context mContext;
private CmdManagerProxy mCmdManagerProxy;
private Disposable mArPingDisposable;
public SysManager(Context context) {
mContext = context;
}
public static void initInstance(Context context) {
singleton = new SysManager(context);
}
public static SysManager getInstance() {
if (singleton == null) {
throw new IllegalArgumentException("do you forgot to init SysSettingManager first");
}
return singleton;
}
/**
* 运行需要root权限的命令
*
* @param cmd
*/
private String runCmd(String cmd, int isBusy) {
//Log.i(TAG, "rec cmd = " + cmd);
String ret = null;
try {
if (mCmdManagerProxy == null) {
//获得ServiceManager类
Class<?> ServiceManager = Class.forName("android.os.ServiceManager");
//获得ServiceManager的getService方法
Method getService = ServiceManager.getMethod("getService", java.lang.String.class);
IBinder binder = (IBinder) getService.invoke(null, "cmdserver");
//Log.i(TAG, "binder = " + binder);
mCmdManagerProxy = new CmdManagerProxy(binder);
}
ret = mCmdManagerProxy.runCmd(cmd, isBusy);
//Log.i(TAG, "ret = " + ret);
} catch (Exception e) {
e.printStackTrace();
}
return ret;
}
/**
* 网络冲突检测
*
* @return
*/
@SuppressLint("CheckResult")
public void arpPing() {
if (mArPingDisposable != null) {
mArPingDisposable.dispose();
mArPingDisposable = null;
}
mArPingDisposable = Observable
.interval(10, 30, TimeUnit.SECONDS)
.observeOn(Schedulers.io())
.subscribe(new Consumer<Long>() {
@Override
public void accept(Long aLong) {
String ip = NetworkUtil.getLocalIpAddress();
if ("".equals(ip)) {
return;
}
MLog.i(TAG, "ar ping ip:" + ip);
try {
String string = runCmd("arping -f -D -w 1 -I eth0 " + ip, 0);
string = string.replace("\n", " ");
if (!string.contains("Received 0 response(s)")) {
MLog.d(TAG, "ip conflict!");
RxBus.getInstance().send(EventKey.MSG_ID_IP_CONFLICT);
}
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}
至此,完美解决root权限问题