/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进行读写了,而且重启后文件不会消失~