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 = 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 = (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
<activity
android:name="com.android.systemui.CustomDialog"
android:launchMode="singleInstance"
android:theme="@style/Transparent" >
</activity>
9,將CustomDialog顯示時所依賴的theme和引用的字元定義一下,
frameworks/base/packages/SystemUI/res/values/strings.xml
<string name="mia_title">警告</string>
<string name="mia_message">資訊記錄已經超過10000條,是否清除記錄?</string>
<string name="mia_yes">確定</string>
<string name="mia_no">取消</string>
還有frameworks/base/packages/SystemUI/res/values/styles.xml
<style name="Transparent" parent="android:style/Theme.Dialog">
<item name="android:windowNoTitle">true</item>
<item name="android:windowIsTranslucent">false</item>
<item name="android:windowIsFloating">true</item>
<item name="android:backgroundDimEnabled">false</item>
</style>
現在自己寫的三方應用,就可以直接對/dev/log進行讀寫了,而且重啟後文件不會消失~