读取miscdata分区的简要分析
一. 简单使用,使用AIDL的方式调用读写的方法
1. 添加aidl接口,注意保持包名类名的一致
package com.sprd.engineermode;
interface IPhaseCheck {
boolean writeOffsetString(int offset, in byte[] value);
String readOffsetString(int offset, int length);
boolean writeChargeSwitch(int value);
}
在AndroidManifest.xml声明Service
<service android:name=".PhaseCheckService">
<intent-filter>
<action android:name="com.sr.intent.action.PHASE_CHECK"/>
</intent-filter>
</service>
在Android.bp编译控制文件中引入src
srcs: ["src/**/*.java", "src/**/*.kt", "src/**/*.aidl"]
2. 绑定服务,调用方法
//导包
import com.sprd.engineermode.IPhaseCheck
class MainActivity : AppCompatActivity() {
var phaseCheckService: IPhaseCheck? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//绑定服务
bindService(
Intent("com.sr.intent.action.PHASE_CHECK").setPackage("com.sprd.engineermode"),
object : ServiceConnection {
override fun onServiceConnected(name: ComponentName, service: IBinder) {
phaseCheckService = IPhaseCheck.Stub.asInterface(service)
}
override fun onServiceDisconnected(name: ComponentName) {}
},
Context.BIND_AUTO_CREATE
)
var offset = 806 * 1024 + 1
var dataLength = miscdata.text.length
if (phaseCheckService != null) {
miscdata.text = phaseCheckService!!.readOffsetString(offset, dataLength)
println("miscdataValue === " + phaseCheckService!!.readOffsetString(offset, dataLength))
}
//写miscdata
btn_submit.setOnClickListener {
if ("" != edt_offset.text.toString().trim()) {
offset = edt_offset.text.toString().toInt() * 1024 + 1
}
val miscdataValue = miscdata_input.text.toString().toByteArray()
dataLength = miscdata.text.length
if (phaseCheckService != null) {
phaseCheckService!!.writeOffsetString(offset, miscdataValue)
miscdata.text = phaseCheckService!!.readOffsetString(offset, dataLength)
}
}
//读miscdata
btn_getMiscdataValue.setOnClickListener {
if ("" != edt_offset.text.toString().trim()) {
offset = edt_offset.text.toString().toInt() * 1024 + 1
}
dataLength = if ("" != edt_lengthData.text.toString().trim()) {
edt_lengthData.text.toString().toInt()
} else {
20
}
if (phaseCheckService != null) {
miscdata.text = phaseCheckService!!.readOffsetString(offset, dataLength)
}
}
}
}
完成以上操作就可以完成miscdata的读取操作了,接下来探究一下这个aidl的背后做了那些事情
二. Service中的具体实现(在EngineerMode此apk中)
1. 定义IPhaseCheck.aidl接口
package com.sprd.engineermode;
interface IPhaseCheck {
boolean writeOffsetString(int offset, in byte[] value);
String readOffsetString(int offset, int length);
boolean writeChargeSwitch(int value);
}
2. 定义Service,实现方法
public class PhaseCheckService extends Service {
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
private IBinder mBinder = new IPhaseCheck.Stub() {
@Override
public boolean writeOffsetString(int offset, byte[] value) throws RemoteException {
boolean isSuc = new PhaseCheckParse().writeOffsetString(offset, value);
return isSuc;
}
@Override
public String readOffsetString(int offset, int length) throws RemoteException {
String result = new PhaseCheckParse().readOffsetString(offset, length);
return result;
}
@Override
public boolean writeChargeSwitch(int value) {
boolean isSuc = new PhaseCheckParse().writeChargeSwitch(value);
return isSuc;
}
};
}
3. 以上Service的方法实现主要在PhaseCheckParse中,接下来看看PhaseCheckParse里面做了那些操作
public class PhaseCheckParse {
private static String TAG = "PhaseCheckParse";
private static int BUF_SIZE = 4096;
private byte[] stream = new byte[300];
private AdaptBinder binder;
//初始化binder
public PhaseCheckParse() {
if (!checkPhaseCheck()) {
stream = null;
}
binder = new AdaptBinder();
Log.e(TAG, "Get The service connect!");
}
static class AdaptBinder {
private AdaptParcel mAdpt;
private static final String SOCKET_NAME = "phasecheck_srv";
AdaptBinder() {
mAdpt = new AdaptParcel();
mAdpt.data = new byte[BUF_SIZE];
mAdpt.code = 0;
mAdpt.dataSize = 0;
mAdpt.replySize = 0;
}
synchronized void sendCmdAndRecResult(AdaptParcel adpt) {
Log.d(TAG, "send cmd");
byte[] buf = new byte[BUF_SIZE];
int2byte(buf, 0, adpt.code);
int2byte(buf, 4, adpt.dataSize);
int2byte(buf, 8, adpt.replySize);
System.arraycopy(adpt.data, 0, buf, 12, adpt.dataSize+adpt.replySize);
Log.d(TAG, "code = "+adpt.code);
Log.d(TAG, "dataSize = "+adpt.dataSize);
Log.d(TAG, "replySize = "+adpt.replySize);
if (!SocketUtils.sendCmd(SOCKET_NAME, buf, buf)) {
Log.e(TAG, "send command failed");
return;
}
adpt.code = byte2Int(buf, 0);
adpt.dataSize = byte2Int(buf, 4);
adpt.replySize = byte2Int(buf, 8);
Log.d(TAG, "code = " + adpt.code);
Log.d(TAG, "dataSize = " + adpt.dataSize);
Log.d(TAG, "replySize = "+ adpt.replySize);
System.arraycopy(buf, 12, adpt.data, 0, adpt.dataSize+adpt.replySize);
}
private void convertParcel(AdaptParcel adpt, int code, Parcel data, Parcel reply) {
data.setDataPosition(0);
reply.setDataPosition(0);
data.writeByteArrayInternal(adpt.data, 0, adpt.dataSize);
reply.writeByteArrayInternal(adpt.data, adpt.dataSize, adpt.replySize);
Log.e(TAG, "convertParcel: dataSize = "+data.dataSize()+", replySize = "+ reply.dataSize());
data.setDataPosition(0);
reply.setDataPosition(0);
}
private void convertAdaptParcel(int code, Parcel data, Parcel reply) {
if(mAdpt == null){
Log.e(TAG, "convertAdaptParcel2: mAdpt == null!");
return;
}
mAdpt.code = code;
data.setDataPosition(0);
reply.setDataPosition(0);
data.logArray();
byte[] bData = new byte[data.dataSize()];
data.readByteArray(bData);
for(int i = 0; i < data.dataSize(); i++){
mAdpt.data[i] = bData[i];
}
byte[] bReply = new byte[reply.dataSize()];
reply.readByteArray(bReply);
for(int i = 0; i < reply.dataSize(); i++){
mAdpt.data[i+data.dataSize()] = bReply[i];
}
mAdpt.dataSize = data.dataSize();
mAdpt.replySize = reply.dataSize();
Log.e(TAG, "convertAdaptParcel2: dataSize = "+data.dataSize()+", replySize = "+ reply.dataSize());
data.setDataPosition(0);
reply.setDataPosition(0);
}
//此方法为进行具体操作的方法,传入code和数据调用相应的方法
void transact(int code, Parcel data, Parcel reply, int flags) {
Log.e(TAG, "transact start....");
//填充数据
convertAdaptParcel(code, data, reply);
//发送指令
sendCmdAndRecResult(mAdpt);
//序列化返回数据
convertParcel(mAdpt, code, data, reply);
Log.e(TAG, "transact end....");
}
}
private static class AdaptParcel {
int code;
int dataSize;
int replySize;
byte[] data;
}
/**
*以上就是初始化的一些操作,实现方法中通过调用binder.transact(code,data,reply,flags)进行相关的操作
* 填充数据
* 1. convertAdaptParcel(code, data, reply);
* 发送指令
* 2. sendCmdAndRecResult(mAdpt);
* 序列化返回数据
* 3. convertParcel(mAdpt, code, data, reply);
**/
//两个主要的方法实现:读写操作
public String readOffsetString(int offset, int length) {
String value = "";
try{
Parcel data = new Parcel();
Parcel reply = new Parcel();
data.writeInt(offset);
data.writeInt(length);
binder.transact(TYPE_READ_OFFSET, data, reply, 0);
value = reply.readString();
Log.e(TAG, "readOffsetValue value = "+value);
data.recycle();
reply.recycle();
}catch (Exception ex) {
Log.e(TAG, "Exception " + ex.getMessage());
ex.printStackTrace();
}
return value;
}
public boolean writeOffsetString(int offset, byte[] value) {
try{
Parcel data = new Parcel();
Parcel reply = new Parcel();
data.writeInt(offset);
data.writeInt(value.length);
for (int i = 0; i < value.length; i++) {
data.writeByte(value[i]);
}
binder.transact(TYPE_WRITE_OFFSET, data, reply, 0);
data.recycle();
return true;
}catch (Exception ex) {
Log.e(TAG, "Exception " + ex.getMessage());
ex.printStackTrace();
return false;
}
}
synchronized void sendCmdAndRecResult(AdaptParcel adpt) {
Log.d(TAG, "send cmd");
byte[] buf = new byte[BUF_SIZE];
int2byte(buf, 0, adpt.code);
int2byte(buf, 4, adpt.dataSize);
int2byte(buf, 8, adpt.replySize);
System.arraycopy(adpt.data, 0, buf, 12, adpt.dataSize+adpt.replySize);
Log.d(TAG, "code = "+adpt.code);
Log.d(TAG, "dataSize = "+adpt.dataSize);
Log.d(TAG, "replySize = "+adpt.replySize);
if (!SocketUtils.sendCmd(SOCKET_NAME, buf, buf)) {
Log.e(TAG, "send command failed");
return;
}
adpt.code = byte2Int(buf, 0);
adpt.dataSize = byte2Int(buf, 4);
adpt.replySize = byte2Int(buf, 8);
Log.d(TAG, "code = " + adpt.code);
Log.d(TAG, "dataSize = " + adpt.dataSize);
Log.d(TAG, "replySize = "+ adpt.replySize);
System.arraycopy(buf, 12, adpt.data, 0, adpt.dataSize+adpt.replySize);
}
private void convertParcel(AdaptParcel adpt, int code, Parcel data, Parcel reply) {
data.setDataPosition(0);
reply.setDataPosition(0);
data.writeByteArrayInternal(adpt.data, 0, adpt.dataSize);
reply.writeByteArrayInternal(adpt.data, adpt.dataSize, adpt.replySize);
Log.e(TAG, "convertParcel: dataSize = "+data.dataSize()+", replySize = "+ reply.dataSize());
data.setDataPosition(0);
reply.setDataPosition(0);
}
private void convertAdaptParcel(int code, Parcel data, Parcel reply) {
if(mAdpt == null){
Log.e(TAG, "convertAdaptParcel2: mAdpt == null!");
return;
}
mAdpt.code = code;
data.setDataPosition(0);
reply.setDataPosition(0);
data.logArray();
byte[] bData = new byte[data.dataSize()];
data.readByteArray(bData);
for(int i = 0; i < data.dataSize(); i++){
mAdpt.data[i] = bData[i];
}
byte[] bReply = new byte[reply.dataSize()];
reply.readByteArray(bReply);
for(int i = 0; i < reply.dataSize(); i++){
mAdpt.data[i+data.dataSize()] = bReply[i];
}
mAdpt.dataSize = data.dataSize();
mAdpt.replySize = reply.dataSize();
Log.e(TAG, "convertAdaptParcel2: dataSize = "+data.dataSize()+", replySize = "+ reply.dataSize());
data.setDataPosition(0);
reply.setDataPosition(0);
}
}
private class Parcel {
private int mDataSize;
private int mPos;
private byte[] mData;
private Parcel() {
mData = new byte[BUF_SIZE];
mPos = 0;
mDataSize = 0;
}
void writeByteArrayInternal(byte[] b, int offset, int len) {
if (len == 0) {
return;
}
System.arraycopy(b, offset, mData, mPos, len);
mPos += len;
mDataSize += len;
}
void readByteArray(byte[] val) {
System.arraycopy(mData, mPos, val, 0, val.length);
mPos += val.length;
}
int dataSize() {
return mDataSize;
}
public byte readByte() {
byte b = mData[mPos];
mPos += 1;
return b;
}
public void writeByte(byte b) {
Log.d(TAG, "ningbiao writeByte b="+b);
mData[mPos] = b;
mPos += 1;
mDataSize += 1;
}
void writeInt(int i) {
Log.d(TAG, "writeInt i="+i);
mData[mPos+3] = (byte)(i >> 24 & 0xff);
mData[mPos+2] = (byte)(i >> 16 & 0xff);
mData[mPos+1] = (byte)(i >> 8 & 0xff);
mData[mPos] = (byte)(i & 0xff);
mPos += 4;
mDataSize += 4;
}
int readInt() {
int b0 = mData[mPos] & 0xFF;
int b1 = mData[mPos + 1] & 0xFF;
int b2 = mData[mPos + 2] & 0xFF;
int b3 = mData[mPos + 3] & 0xFF;
mPos += 4;
return b0 | (b1 << 8) | (b2 << 16) | (b3 << 24);
}
void setDataPosition(int i) {
mPos = i;
}
String readString() {
int nNum = readInt();
byte[] b = new byte[nNum];
Log.d(TAG, "readString num = "+nNum);
readByteArray(b);
return new String(b, StandardCharsets.UTF_8);
}
void recycle() {
reset();
}
void reset() {
mPos = 0;
mDataSize = 0;
}
}
}
稍微解析一下这两个方法
例如存入一个String A,ASCII码为65,一个int为4byte一个byte为8bit,存储的机器码为反码,有32位即为000…01000001
然后做一个移位操作与上一个0xff,0xff默认为int型0x000000ff,& 0xff操作即为高位清零,截取第25-32bit位保存到mData[mPos+3],就是用四个byte位表示一个int
然后读取就是一个反序列化的操作
void writeInt(int i) {
Log.d(TAG, "writeInt i="+i);
mData[mPos+3] = (byte)(i >> 24 & 0xff);
mData[mPos+2] = (byte)(i >> 16 & 0xff);
mData[mPos+1] = (byte)(i >> 8 & 0xff);
mData[mPos] = (byte)(i & 0xff);
mPos += 4;
mDataSize += 4;
}
int readInt() {
int b0 = mData[mPos] & 0xFF;
int b1 = mData[mPos + 1] & 0xFF;
int b2 = mData[mPos + 2] & 0xFF;
int b3 = mData[mPos + 3] & 0xFF;
mPos += 4;
return b0 | (b1 << 8) | (b2 << 16) | (b3 << 24);
}
接下来看看sendCmdAndRecResult(mAdpt)中的主要操作,主要是执行了sendCmd()方法
- 建立socket连接
- ops.write(cmd); ops.flush();传入cmd
- int count = ins.read(result);返回result
public static boolean sendCmd(String socketName, byte[] cmd, byte[] result ) {
Log.d(TAG, socketName + " send byte cmd");
LocalSocket socketClient = new LocalSocket();
LocalSocketAddress mSocketAddress =
new LocalSocketAddress(socketName, Namespace.ABSTRACT);
try {
socketClient.connect(mSocketAddress);
} catch (IOException e) {
Log.e(TAG, "connect to " + socketName + " failed", e);
try {
socketClient.close();
} catch (IOException ignored) {
}
return false;
}
Watchdog wd = null;
try (OutputStream ops = socketClient.getOutputStream();
InputStream ins = socketClient.getInputStream()){
Log.i(TAG, "connect " + socketName + " success");
ops.write(cmd);
ops.flush();
Log.d(TAG, "write cmd and flush done");
wd = new Watchdog(socketName, "byte command");
wd.setTimeoutCallback(SocketUtils::timeout, socketClient);
wd.wantEat();
int count = ins.read(result);
wd.feedFood();
Log.d(TAG, "result read done, count=" + count);
} catch (IOException e) {
e.printStackTrace();
return false;
} finally {
if (wd != null) {
wd.feedFood();
}
try {
socketClient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
Log.d(TAG, "cmd over and result len: " + result.length);
return true;
}
再来找找建立socket通信后面的服务端,做了那些操作
- 启动服务,加入Service管理,通过binder机制来进行处理
- 建立连接,进行通信
int main(int arg, char** argv)
{
ENG_LOG("phasecheck_sprd Nativeserver - main() begin\n");
#ifdef CHANNEL_SOCKET
phConnect();
#else
ProcessState::initWithDriver("/dev/vndbinder");
ALOGE("phasecheck_sprd Nativeserver - main() begin /dev/vndbinder\n");
sp<ProcessState> proc(ProcessState::self());
sp<IServiceManager> sm = defaultServiceManager();
//LOGI("ServiceManager: %p\n", sm.get());
ENG_LOG("phasecheck_sprd new server - serviceManager: %p\n", sm.get());
//int ret = NativeService::Instance();
int ret = defaultServiceManager()->addService(
String16("phasechecknative"), new NativeService());
ENG_LOG("phasecheck_sprd new ..server - NativeService::Instance return %d\n", ret);
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
#endif
return 0;
}
phConnect();
//creat services socket, bind, listen...
service_fd = socket_local_server(PHASECHECK_SERVICE,ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
connect_fd = accept(service_fd, (struct sockaddr *)&client_address, &client_len)
...
//对数据进行处理
ns.onTransact(code, data, &reply, 0);
...
close(connect_fd);
...
close(service_fd);
...
onTransact():
int NativeService::onTransact(uint32_t code,
Parcel& data,
Parcel* reply,
uint32_t flags)
{
ENG_LOG("phasecheck_sprd nativeservice onTransact code:%d",code);
//对相应的code码进行相应的处理
switch(code)
{
case TYPE_WRITE_OFFSET:
{
//write offset value
jint offset = data.readInt32();
jint write_count = data.readInt32();
char *value = (char *)malloc(write_count);
data.read(value,write_count);
ALOGD("phasecheck_sprd nativeservice write offset=%d,write_count=%d",offset, write_count);
int ret = eng_writeOffset(offset, write_count, value);
ALOGD("phasecheck_sprd nativeservice write ret=%d",ret);
free(value);
return NO_ERROR;
}
case TYPE_READ_OFFSET:
{
//read byte array with offset
jint offset = data.readInt32();
jint read_count = data.readInt32();
char *value = (char *)malloc(read_count);
ALOGD("phasecheck_sprd nativeservice read offset=%d,read_count=%d",offset,read_count);
int ret = eng_readOffset(offset, read_count, value);
ALOGD("phasecheck_sprd nativeservice read offset is 0x%x, ret = %d", value, ret);
reply->write(value, ret);
free(value);
return NO_ERROR;
}
}
}
再来看看获取对应code后的具体实现,主要就是以下两个方法:
主要就是读写设备文件节点
jint eng_writeOffset(int offset, int write_count, char* value)
{
int len;
ENG_LOG("eng_writeOffset: offset = %d, write_count = %d", offset, write_count);
char buf[PROPERTY_VALUE_MAX];
len = property_get(PHASE_CHECK_MISCDATA_PATH, buf, "");
char* phasecheckPath = strcat(buf, PHASE_CHECK_MISCDATA);
ENG_LOG("phasecheck_sprd : phasecheck Path:%s\n", phasecheckPath);
int fd = open(phasecheckPath,O_RDWR);
if (fd >= 0)
{
ENG_LOG("%s open Ok phasecheckPath = %s \n",__FUNCTION__ , phasecheckPath);
int ret = lseek(fd, offset, SEEK_SET);
if (ret < 0){
close(fd);
ENG_LOG("%s lseek fail phasecheckPath = %s \n",__FUNCTION__ , phasecheckPath);
return -1;
}
len = write(fd, value, write_count);
ENG_LOG("write: %d", len);
fsync(fd);
close(fd);
if (len <= 0){
ENG_LOG("%s read fail phasecheckPath = %s \n",__FUNCTION__ , phasecheckPath);
return -1;
}
} else {
ENG_LOG("%s open fail phasecheckPath = %s \n",__FUNCTION__ , phasecheckPath);
return -1;
}
return len;
}
int eng_readOffset(int offset,int read_count, char *value)
{
int len = 0;
ENG_LOG("eng_readOffset: offset = %d, read_count= %d", offset, read_count);
char buf[PROPERTY_VALUE_MAX];
len = property_get(PHASE_CHECK_MISCDATA_PATH, buf, "");
char* phasecheckPath = strcat(buf, PHASE_CHECK_MISCDATA);
int fd = open(phasecheckPath,O_RDWR);
if (fd >= 0)
{
ENG_LOG("%s open Ok phasecheckPath = %s \n",__FUNCTION__ , phasecheckPath);
//lseek(fd, offset, SEEK_SET);
int ret = lseek(fd, offset, SEEK_SET);
if (ret < 0){
close(fd);
ENG_LOG("%s lseek fail phasecheckPath = %s \n",__FUNCTION__ , phasecheckPath);
return -1;
}
len = read(fd, value, read_count);
ENG_LOG("read: %d", len);
close(fd);
if (len <= 0){
ENG_LOG("%s read fail phasecheckPath = %s \n",__FUNCTION__ , phasecheckPath);
return -1;
}
} else {
ENG_LOG("%s open fail phasecheckPath = %s \n",__FUNCTION__ , phasecheckPath);
return -1;
}
return len;
}
三. 总结
以上就是对miscdata分区数据读写的一个简要分析了
- 以一个C/S结构的思想进行一个解读,主要就是对文件系统的读取操作
- 通过AIDL对其进行简单的使用
- 使用socket进行通信(有待完善)
- 使用binder对服务进行一个注册管理(有待完善)