1. 程式人生 > >Android 如何對/dev/log路徑裝置節點進行讀寫

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進行讀寫了,而且重啟後文件不會消失~