Brief analysis of reading miscdata partition

Brief analysis of reading miscdata partition

I Simple to use, use AIDL to call read-write methods

1. Add aidl interface, and pay attention to keeping the package name consistent with the class name
[the external chain picture transfer fails, and the source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-M5ZOAXRP-1623748847856)(./images/1623398799791.png)]

package com.sprd.engineermode;

interface IPhaseCheck {
    boolean writeOffsetString(int offset, in byte[] value);
    String readOffsetString(int offset, int length);
    boolean writeChargeSwitch(int value);
}

2. Bind the service and call the method

//Guide Package
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)
//Binding service
 bindService(
            Intent("com.sagereal.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))
        }

//Write 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)
            }
        }
//Read 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)
            }
        }
    }
}

After completing the above operations, you can complete the reading operation of miscdata. Next, explore what is done behind the aidl

II Specific implementation in Service (in the apk of EngineerMode)

1. Define iphasecheck Aidl interface

package com.sprd.engineermode;

 interface IPhaseCheck {
     boolean writeOffsetString(int offset, in byte[] value);
     String readOffsetString(int offset, int length);
     boolean writeChargeSwitch(int value);
 }

2. Define Service and implementation method

 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. The above Service methods are mainly implemented in PhaseCheckParse. Next, let's look at the operations done in PhaseCheckParse

 public class PhaseCheckParse {
     private static String TAG = "PhaseCheckParse";
 
     private static int BUF_SIZE = 4096;
     private byte[] stream = new byte[300];
     private AdaptBinder binder;
 
 //Initialize 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);
          }
  
        //This method is a method for specific operations. The corresponding method is called by passing in code and data
  void transact(int code, Parcel data, Parcel reply, int flags) {
              Log.e(TAG, "transact start....");
              //Fill data
              convertAdaptParcel(code, data, reply);
              //Send instructions
              sendCmdAndRecResult(mAdpt);
              //Serialize return data
              convertParcel(mAdpt, code, data, reply);
  
              Log.e(TAG, "transact end....");
          }
      }
 
 
 private static class AdaptParcel {
          int code;
          int dataSize;
          int replySize;
          byte[] data;
      }
 
 /**
 *The above are some operations of initialization. In the implementation method, you can call binder Transact (code, data, reply, flags) for related operations
 * Fill data
 * 1. convertAdaptParcel(code, data, reply);
 * Send instructions
 * 2. sendCmdAndRecResult(mAdpt);
 * Serialize return data
 * 3. convertParcel(mAdpt, code, data, reply);
 **/
 
 

  //Two main methods are implemented: read-write operation
      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;
          }
      }
    }

Analyze these two methods a little
For example, if a string a is stored, the ASCII code is 65, an int is 4byte and a byte is 8bit, and the stored machine code is inverse code. If there are 32 bits, it is 000... 01000001
Then perform a shift operation with the previous 0xff. 0xff defaults to int type 0x000000ff, and the & 0xff operation is high-order zeroing. Intercept the 25th-32th bit and save it to mData[mPos+3], that is, use four byte bits to represent an int
Then reading is a deserialization operation

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);
          }

Next, let's take a look at the main operations in sendcmdandrecrecresult (madpt), mainly the execution of the sendCmd() method

  1. Establish socket connection
  2. ops.write(cmd); ops.flush(); Incoming CMD
  3. int count = ins.read(result); Return 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;
}

Let's look for the server behind the establishment of socket communication and do those operations

  1. Start the Service, join the Service management, and handle it through the binder mechanism
  2. Establish a connection and communicate
  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)
...
//Process data
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);
    //Process the corresponding code accordingly
          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;
              }
        }
    }

Let's take a look at the specific implementation after obtaining the corresponding code. There are mainly the following two methods:
This node is mainly used to read and write device files

 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;
      }
III summary

The above is a brief analysis of miscdata partition data reading and writing

  1. An interpretation based on the idea of a C/S structure is mainly the reading operation of the file system
  2. It is easy to use through AIDL
  3. Use socket for communication (to be improved)
  4. Use binder to register and manage the service (to be improved)

Added by serbestgezer on Sun, 30 Jan 2022 05:57:09 +0200