Android 如何对/dev/log路径设备节点进行读写

/dev/log这个路径直接操作是无法进行读写的,所以我在framework层对这个路径进行关联,然后三方应用就是对这个路径进行读写了.闲话少说,直接上代码.首先自定义service,这个流程前面文章有所介绍,这里我就只贴出service相关的代码,
首先是frameworks/base/core/java/android/app/customized/ICustomizedService.aidl文件

package android.app.customized; interface ICustomizedService{ void shutDown (); void setCustomlog(String log , boolean isTest); void setCustomSerialNumber(); String getCustomBuildNumber(); }

然后是frameworks/base/core/java/android/app/customized/CustomizedManager.java文件
package android.app.customized; import android.util.Log; import android.content.Context; import android.content.Intent; import android.os.Binder; import android.os.RemoteException; import android.provider.Settings; import java.io.IOException; import android.os.ServiceManager; import android.os.IBinder; import java.util.List; import android.app.ActivityManager; import android.graphics.Bitmap; public class CustomizedManager{ private static final String TAG="CustomizedManager"; private static final boolean DBG=true; private static ICustomizedService mService; private final Context mContext; /* device|time|which1~6|result|notes */ public static String UNLOCK = "001"; public static String M_TIME = "002"; public static String REBOOT = "003"; public static String UPGRADE = "004"; public static String INSTALL = "005"; public static String UNINSTALL = "006"; public static String DEF_DISCRIBE = "NONE"; public static String DEF_SPLIT = "|"; public static String RESULT_NO = "0"; public static String RESULT_YES = "1"; public CustomizedManager(Context context){ mContext = context; mService = ICustomizedService.Stub.asInterface( ServiceManager.getService("customized")); } private static ICustomizedService getService(){ if (mService != null) { return mService; }IBinder b = ServiceManager.getService("customized"); mService = ICustomizedService.Stub.asInterface(b); return mService; } public void shutDown () { ICustomizedService service = getService(); try { service.shutDown(); } catch (Exception e) {} }public String getCustomBuildNumber(){ try { return getService().getCustomBuildNumber(); } catch (Exception e){} return "CUSTOMGJDW1"; }public void setCustomSerialNumber() { try { getService().setCustomSerialNumber(); } catch (Exception e) {} }public void setCustomlog(String log , boolean isTest) { Log.d("lei","CustomizedManager setCustomlog"); try { getService().setCustomlog(log ,isTest); } catch (Exception e) {} } }

最后是frameworks/base/services/core/java/com/android/server/customized/CustomizedService.java
package com.android.server.customized; import android.os.IBinder; import android.os.ServiceManager; import android.content.Context; import android.content.Intent; import android.os.Binder; import android.app.customized.ICustomizedService; import android.content.BroadcastReceiver; public class CustomizedService extends ICustomizedService.Stub { private static final String TAG = "CustomizedService "; private Context mContext; public static class Lifecycle extends SystemService { private CustomizedService mService; public Lifecycle(Context context) { super(context); } @Override public void onStart() { mService = new CustomizedService (getContext()); publishBinderService(Context.CUSTOMIZED, mService); } @Override public void onBootPhase(int phase) { } @Override public void onUnlockUser(int userHandle) { } } public CustomizedService (Context context) { mContext = context; } /** *1 add interface shutDown() */ public void shutDown () { sentControl("custom_shutdown", "null",true); } private void sentControl(String action, String key, boolean iscontrol) { long jh = Binder.clearCallingIdentity(); Intent i = new Intent(); i.setAction(action); if (iscontrol) { i.putExtra(key, true); } else { i.putExtra(key, false); } mContext.sendBroadcast(i); sentControl (action,iscontrol); Binder.restoreCallingIdentity(jh); } private void sentControl(String action, boolean iscontrol) { long jh = Binder.clearCallingIdentity(); int key; if (iscontrol) { key = 0; } else { key = 1; } Log.i ("custom",",action "+ action + "; key"+key); Settings.Secure.putInt(mContext.getContentResolver(),action,key); Binder.restoreCallingIdentity(jh); }public String getCustomBuildNumber(){ Binder.clearCallingIdentity(); try { return android.os.SystemProperties.get("ro.lenovosn2","unknown"); } catch (Exception e) { return "CUSTOMGJDW1"; } finally{ Binder.restoreCallingIdentity(DUMP_TRANSACTION); } }public void setCustomSerialNumber() { Binder.clearCallingIdentity(); try { new CustomSetSn(); } catch (Exception e) { Log.d("lei","setCustomSerialNumber Exception" + e); } Binder.restoreCallingIdentity(DUMP_TRANSACTION); }public void setCustomlog(String log , boolean isTest) { Log.d("lei","CustomizedService = " + log +" ; isTest="+isTest); Binder.clearCallingIdentity(); WatchCat.getInstance(mContext).customPerpetual(log,isTest); Binder.restoreCallingIdentity(DUMP_TRANSACTION); } }

以上是service的三个文件,下面的修改才是真正的重头戏
1,首先新建frameworks/base/services/core/java/com/android/server/customized/CustomSetSn.java文件,这个文件是读取系统NV值.本质是通过读取NV值获取系统SN.
package com.android.server.customized; import android.os.Build; import android.os.SystemProperties; import android.os.ServiceManager; import android.os.Handler; import android.os.Message; import com.android.internal.telephony.ITelephony; import android.util.Log; public class CustomSetSn { private static final String TAG = "lei"; private static final int MESSAGE_READ_NV_START= 700; private static final int MESSAGE_READ_NV_DETECT= 800; private static int mNeedReadNVsCount = 0; private static int mNeedReadNVs[] = {6853,6854}; private final static int TIMEOUT = 1000; private static String mSnNumWfi = null; private static String mPnNumWfi = null; public CustomSetSn(){ if(Build.LCT_PROJECT_NAME.contains("lxf_p3590_b01") || Build.LCT_PROJECT_NAME.contains("lxf_p3590_b11") || Build.LCT_PROJECT_NAME.contains("lxf_p3590_b02") || Build.LCT_PROJECT_NAME.contains("lxf_p3588_b01") || Build.LCT_PROJECT_NAME.contains("lxf_p3588_b02") || Build.LCT_PROJECT_NAME.contains("lxf_p3588_b03")){ readWifiSN(); } else { readSN(); } }private void readWifiSN(){ Log.d(TAG,"readWifiSN"); handler.sendEmptyMessage(MESSAGE_READ_NV_START); }private String readSN(){ Log.d(TAG,"readSN"); if(Build.CUSTOM_NAME.contains("Lenovo")){ byte[] result = readNVItems(6854); String mSerial = new String(result); result=readNVItems(6853); //PN = new String(result); Log.d(TAG,"CustomSetSn mSerial="+ mSerial); SystemProperties.set("ro.lenovosn2", mSerial); return mSerial; } return Build.SERIAL; }private static byte[] readNVItems(int item) { return readNVItems(item, 0); }private static byte[] readNVItems(int item, int subs) { ITelephony tel = ITelephony.Stub.asInterface(ServiceManager.checkService("phone")); byte[] input = new byte[6]; byte[] output1, output2; int i; input[0] = 0x00; input[1] = 0x00; input[2] = (byte)(item & 0xff); input[3] = (byte)(item>>8 & 0xff); input[4] = (byte)(item>>16 & 0xff); input[5] = (byte)(item>>24 & 0xff); try { output1 = tel.lctOemCommand(input, subs); for (i = 0; i < output1.length; i++) { if (output1[i] == 0x00) break; } output2 = new byte[i]; System.arraycopy(output1, 0, output2, 0, i); return output2; } catch (Exception ex) { Log.e(TAG, "exception on readNVItems: ", ex); return null; } }Handler handler = new Handler() { public void handleMessage(Message msg) { switch (msg.what) { case MESSAGE_READ_NV_START: mNeedReadNVsCount = 0; Config.readNV(mNeedReadNVs[mNeedReadNVsCount]); handler.sendEmptyMessageDelayed(MESSAGE_READ_NV_DETECT, TIMEOUT); break; case MESSAGE_READ_NV_DETECT: if (Config.isProcessing()){ handler.sendEmptyMessageDelayed(MESSAGE_READ_NV_DETECT, TIMEOUT); } else { String nvValue = https://www.it610.com/article/Config.getNvValue(); switch(mNeedReadNVs[mNeedReadNVsCount]){ case 6854: if((nvValue != null) && (nvValue.toString().length() != 0)){ mSnNumWfi = nvValue; }else{ mSnNumWfi =" null"; } break; case 6853: if((nvValue != null) && (nvValue.toString().length() != 0)){ mPnNumWfi =nvValue; }else{ mPnNumWfi = ""; } break; default: break; } int count = mNeedReadNVs.length; if (mNeedReadNVsCount < (count-1)){ mNeedReadNVsCount++; Config.readNV(mNeedReadNVs[mNeedReadNVsCount]); handler.sendEmptyMessageDelayed(MESSAGE_READ_NV_DETECT, TIMEOUT); } else { handler.removeMessages(MESSAGE_READ_NV_DETECT); Log.d(TAG,"customsn="+"PN:"+ mPnNumWfi + "\nSN:"+mSnNumWfi); if((mPnNumWfi != null && !mPnNumWfi.equals("")) && (mSnNumWfi !=null && !mSnNumWfi.equals(""))){ SystemProperties.set("ro.lenovosn2", mSnNumWfi); } } } break; } } }; }

2,新建frameworks/base/services/core/java/com/android/server/customized/WatchCat.java文件,这个文件就是我们实现向dev/log路径下写文件的主要方法.
package com.android.server.customized; import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileWriter; import java.io.IOException; import java.io.OutputStreamWriter; import java.text.SimpleDateFormat; import java.util.Scanner; import android.content.Intent; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.os.Handler; import android.os.Message; import android.telephony.TelephonyManager; import android.util.Log; public class WatchCat { private Context mContext; public static String UNLOCK = "001"; public static String M_TIME = "002"; public static String REBOOT = "003"; public static String UPGRADE = "004"; public static String INSTALL = "005"; public static String UNINSTALL = "006"; public static String DEF_DISCRIBE = "NONE"; public static String DEF_SPLIT = "|"; public static String RESULT_NO = "0"; public static String RESULT_YES = "1"; private static WatchCat sWatchdog; private String gaodzInput; private String DeviceID; private final File mFile = new File("/data/misc/log/mdm"); private static Handler mHandler; public static WatchCat getInstance(Context context) { if (sWatchdog == null) { sWatchdog = new WatchCat(context); } return sWatchdog; } public WatchCat(Context context) { mContext = context; } public void customPerpetual(String input, boolean isTest) { if(isTest){ gaodzInput = input; }else{ gaodzInput = getTime() + getDevice() + input; } Log.d("gaodz", "gaodzInput = " + gaodzInput); Appendwrite(gaodzInput,isTest); checkFile(); } private void Appendwrite(String mInput, boolean isclean) { BufferedWriter out = null; try { if (isclean) { out = new BufferedWriter(new FileWriter(mFile)); } else { out = new BufferedWriter(new OutputStreamWriter( new FileOutputStream(mFile, true))); } out.write(mInput); out.newLine(); out.close(); } catch (Exception e) { e.printStackTrace(); Log.d("gaodz", "Appendwrite IOException+" + e); } } public void checkFile() { if (readFile() >= 10000) { Intent intent = new Intent(); intent.setClassName("com.android.systemui", "com.android.systemui.CustomDialog"); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP); mContext.startActivity(intent); } } private int readFile() { try { int count = 1; if (!mFile.exists()) return -1; FileInputStream fis = new FileInputStream(mFile); Scanner scanner = new Scanner(fis); while (scanner.hasNextLine()) { scanner.nextLine(); count++; } Log.d("gaodz", "count = " + count); return count; } catch (FileNotFoundException e) { Log.d("gaodz", "readFile FileNotFoundException"); e.printStackTrace(); } return -1; } private String getTime() { SimpleDateFormat sDateFormat = new SimpleDateFormat( "yyyy-MM-dd hh:mm:ss"); String date = sDateFormat.format(new java.util.Date()); return date + DEF_SPLIT; } private String getDevice() { if (DeviceID == null) { TelephonyManager telephonyManager = (TelephonyManager) mContext .getSystemService(Context.TELEPHONY_SERVICE); telephonyManager.getDeviceId(); DeviceID = telephonyManager.getDeviceId() + DEF_SPLIT; } return DeviceID; } }

3, 新建frameworks/base/services/core/java/com/android/server/customized/Config.java,这个文件时定义NV值的文件
package com.android.server.customized; import android.content.Context; import android.os.AsyncResult; import android.os.Build; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.util.Log; import com.android.internal.util.ReadWriteNV; public class Config {private static ReadWriteNV mReadWriteNV = null; private static final int EVENT_READ_NV_COMPLETE = 103; private static String nv_value; private static boolean mReadNVComplete = false; private static Context mContext; public Config() { mReadWriteNV = ReadWriteNV.getInstance(); }private static final Handler mHandler = new Handler() { AsyncResult ar; @Override public void handleMessage(Message msg) { switch (msg.what) { case EVENT_READ_NV_COMPLETE: ar = (AsyncResult)msg.obj; if ((ar.exception == null) && (ar.result != null)) { nv_value = https://www.it610.com/article/(String) ar.result; mReadNVComplete = true; } break; } } }; public static boolean isProcessing(){ return mReadWriteNV.isProcessing(); }public static String getNvValue(){ return nv_value; }public static void registerForReadNVRegistrants(){ mReadWriteNV.registerForReadWriteNVRegistrants(mHandler, EVENT_READ_NV_COMPLETE, null); }public static void unregisterForReadNVRegistrants(){ mReadWriteNV.unregisterForReadWriteNVRegistrants(mHandler); }public static void readNV(int item) { try { mReadWriteNV.readnv(item); }catch (Exception ex) {} } }


4.然后需要在init.rc中定义路径的关联,将/data/misc/log和/dev/log关联起来,关联之后两个路径就相当于是同一个路径,读写/dev/log和读写/data/misc/log结果是一样的,那么为什么要关联呢,直接对/dev/log进行读写就可以不是吗?答案是关联是因为/dev/log读写的文件在系统进行重启后会消失,而data/misc/log则不会,关联代码如下:
mkdir /data/misc/log/ 0770 root system//新建/data/misc/log/路径 chmod 0777 /data/misc/log/mdm//赋予可读可写可执行权限# add lei symlink /data/misc/log /dev/log//关联两个路径 chown system system /dev/log/mdm chmod 0777 /dev/log/mdm

4, 在system/sepolicy/platform_app.te对log_device进程进行权限赋予
allow platform_app log_device:dir { read write add_name open create search ioctl }; allow platform_app log_device:file { read write create append open };

5,修改system/sepolicy/system_app.te文件,对log_device进程进行权限赋予
allow system_app log_device:dir { read write add_name open create search ioctl }; allow system_app log_device:file { read write create append open };

6,在device/qcom/common/common64.mk中提前j将文件data/misc/log/mdm创建,系统会将这个文件提前编译进去
$(shell mkdir-p out/target/product/msm8953_64/data/misc/log/) $(shell touch out/target/product/msm8953_64/data/misc/log/mdm)

7,WatchCat中有定义检查mdm文件,如果写入超过1000条就会提示是否清除记录
首先新建frameworks/base/packages/SystemUI/src/com/android/systemui/CustomDialog.java
+package com.android.systemui; import android.app.Activity; import android.app.AlertDialog; import android.content.DialogInterface; import android.os.Bundle; import android.util.Log; import android.app.customized.CustomizedManager; public class CustomDialog extends Activity{ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); showDialog(); } private void showDialog() { AlertDialog.Builder builder = new AlertDialog.Builder(CustomDialog.this); builder.setTitle(R.string.custom_title).setMessage(R.string.custom_message) .setPositiveButton(R.string.custom_yes, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { try { CustomizedManager CM = new CustomizedManager(CustomDialog.this); CM.setCustomlog(" ",true); CustomDialog.this.finish(); } catch (Exception e) { Log.d("gaodz", "mFile.delete Exception"); } } }) .setNegativeButton(R.string.custom_no, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { CustomDialog.this.finish(); } }); builder.create(); builder.show(); } }

8,在frameworks/base/packages/SystemUI/AndroidManifest.xml中定义下这个activity

9,将CustomDialog显示时所依赖的theme和引用的字符定义一下,
frameworks/base/packages/SystemUI/res/values/strings.xml
警告 信息记录已经超过10000条,是否清除记录? 确定 取消

还有frameworks/base/packages/SystemUI/res/values/styles.xml
true false true false

【Android 如何对/dev/log路径设备节点进行读写】现在自己写的三方应用,就可以直接对/dev/log进行读写了,而且重启后文件不会消失~

    推荐阅读