Android之APP层使用adb命令抓取系统日志

本文介绍了如何在Android应用层通过adb命令抓取系统日志,并将其保存到U盘。关键步骤包括修改相关代码如build.gradle、AndroidManifest.xml等,以及使用系统签名对APK进行签名,因为未签名的APP只能获取到/dev/log/main下的无用日志,而签名后才能访问到包含错误信息的/dev/log/system路径下的日志。在RK3128上的Android 4.4系统测试中,抓取的日志范围约为3个多小时。
摘要由CSDN通过智能技术生成

Android之APP层使用adb命令抓取系统日志之保存到U盘

代码如下:
build.gradle

plugins {
    id 'com.android.application'
}

android {
    compileSdkVersion 28
    buildToolsVersion "29.0.2"

    defaultConfig {
        applicationId "com.yml.savelog"
        minSdkVersion 16
        targetSdkVersion 22
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    signingConfigs {
        lycoo_release {
            keyAlias 'lycoo'
            keyPassword 'lycoomylive'
            storeFile file('E:/Emylive/KeyStore/lycoo.keystore')
            storePassword 'lycoomylive'
        }
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
        debug {
            signingConfig signingConfigs.lycoo_release
        }
    }

    lintOptions {
        checkReleaseBuilds false
        // Or, if you prefer, you can continue to check for errors in release builds,
        // but continue the build even when errors are found:
        abortOnError false
    }

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }

    android.applicationVariants.all { variant ->
        variant.outputs.all { output ->
            def outputFile = output.outputFile
            if (outputFile != null && outputFile.name.endsWith('.apk')) {
                def date = new Date().format("yyyyMMdd", TimeZone.getTimeZone("GMT+08"))
                if (outputFile.name.contains("-release")) {
                    outputFileName = outputFile.name.replace("-release", "-${variant.versionName}-${date}")
                } else if (outputFile.name.contains("-debug")) {
                    outputFileName = outputFile.name.replace("-debug", "-${variant.versionName}-${date}-debug")
                }
            }
        }
    }
}

dependencies {

    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'com.google.android.material:material:1.1.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    implementation files('libs\\commons-lang3-3.5.jar')
    testImplementation 'junit:junit:4.+'
    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'

    // rxjava2
    implementation 'io.reactivex.rxjava2:rxjava:2.2.4'
    // rxandroid
    implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'
}

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.yml.savelog">

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
    <uses-permission android:name="android.permission.GET_TASKS" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
    <uses-feature android:name="android.hardware.usb.host"/>

    <uses-permission android:name="android.permission.DELETE_CACHE_FILES"
        tools:ignore="ProtectedPermissions" />
    <uses-permission android:name="android.permission.WRITE_MEDIA_STORAGE"
        tools:ignore="ProtectedPermissions" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_LOGS"
        tools:ignore="ProtectedPermissions"
        />
    <application
        android:name=".BaseApplication"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.YMLSaveLog">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <receiver
            android:name=".MediaReceiver"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="com.lycoo.action.MEDIA_MOUNTED" />
                <action android:name="android.intent.action.MEDIA_SCANNER_SCAN_FILE" />
                <action android:name="android.intent.action.MEDIA_MOUNTED" />
                <action android:name="android.intent.action.MEDIA_REMOVED" />
                <action android:name="android.intent.action.MEDIA_EJECT" />
                <!--
                <action android:name="android.intent.action.MEDIA_UNMOUNTED"/>
                <action android:name="android.intent.action.MEDIA_BAD_REMOVAL"/>
                -->
                <data android:scheme="file" />
            </intent-filter>
        </receiver>
    </application>

</manifest>

RxBus.java

package com.yml.savelog;

import java.util.HashMap;
import java.util.Map;

import io.reactivex.BackpressureStrategy;
import io.reactivex.Flowable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Consumer;
import io.reactivex.schedulers.Schedulers;
import io.reactivex.subjects.PublishSubject;
import io.reactivex.subjects.Subject;

/**
 * xxx
 *
 * Created by lancy on 2017/12/20
 */
public class RxBus {
    private static final String TAG = RxBus.class.getSimpleName();

    private Map<String, CompositeDisposable> mDisposableMap;
    private static volatile RxBus mRxBus;
    private final Subject<Object> mSubject;

    public static RxBus getInstance() {
        if (mRxBus == null) {
            synchronized (RxBus.class) {
                if (mRxBus == null) {
                    mRxBus = new RxBus();
                }
            }
        }
        return mRxBus;
    }

    private RxBus() {
        mSubject = PublishSubject.create().toSerialized();
    }

    public void post(Object o) {
        mSubject.onNext(o);
    }

    public boolean hasObservers() {
        return mSubject.hasObservers();
    }

    public void addDisposable(Object obj, Disposable disposable) {
        if (mDisposableMap == null) {
            mDisposableMap = new HashMap<>();
        }
        String key = obj.getClass().getName();
        if (mDisposableMap.get(key) != null) {
            mDisposableMap.get(key).add(disposable);
        } else {
            //一次性容器,可以持有多个并提供 添加和移除。
            CompositeDisposable disposables = new CompositeDisposable();
            disposables.add(disposable);
            mDisposableMap.put(key, disposables);
        }
    }

    public <T> Flowable<T> getObservable(Class<T> type) {
        return mSubject
                .toFlowable(BackpressureStrategy.BUFFER)
                .ofType(type);
    }

    public <T> Disposable registerSubscribe(Class<T> type, Consumer<T> next, Consumer<Throwable> error) {
        return getObservable(type)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(next, error);
    }

    public void unRegisterSubscribe(Object o) {
        if (mDisposableMap == null) {
            return;
        }

        String key = o.getClass().getName();
        if (!mDisposableMap.containsKey(key)) {
            return;
        }
        if (mDisposableMap.get(key) != null) {
            mDisposableMap.get(key).dispose();
        }

        mDisposableMap.remove(key);
    }

}

RootCmd.java

package com.yml.savelog;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import android.util.Log;

/**
 * Android运行linux命令
 */
public final class RootCmd {
    private static final String TAG = "RootCmd";
    private static boolean mHaveRoot = false;
    /**
     *   判断机器Android是否已经root,即是否获取root权限
     */
    public static boolean haveRoot() {
        if (!mHaveRoot) {
            int ret = execRootCmdSilent("echo test"); // 通过执行测试命令来检测
            if (ret != -1) {
                Log.i(TAG, "have root!");
                mHaveRoot = true;
            } else {
                Log.i(TAG, "not root!");
            }
        } else {
            Log.i(TAG, "mHaveRoot = true, have root!");
        }
        return mHaveRoot;
    }

    /**
     * 执行命令并且输出结果
     */
    public static String execRootCmd(String cmd) {
        String result = "";
        DataOutputStream dos = null;
        DataInputStream dis = null;

        try {
            Process p = Runtime.getRuntime().exec("su");// 经过Root处理的android系统即有su命令
            dos = new DataOutputStream(p.getOutputStream());
            dis = new DataInputStream(p.getInputStream());

//            Log.i(TAG, cmd);
            dos.writeBytes(cmd + "\n");
            dos.flush();
            dos.writeBytes("exit\n");
            dos.flush();
            String line = null;
            while ((line = dis.readLine()) != null) {
//                Log.d("result", line);
                result += line;
            }
            p.waitFor();
        } catch (Exception e) {
//            System.out.println("liu::::    报错");
            e.printStackTrace();
        } finally {
            if (dos != null) {
                try {
                    dos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (dis != null) {
                try {
                    dis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return result;
    }

    /**
     * 执行命令但不关注结果输出
     */
    public static int execRootCmdSilent(String cmd) {
        int result = -1;
        DataOutputStream dos = null;

        try {
            Process p = Runtime.getRuntime().exec("su");
            dos = new DataOutputStream(p.getOutputStream());

            Log.i(TAG, cmd);
            dos.writeBytes(cmd + "\n");
            dos.flush();
            dos.writeBytes("exit\n");
            dos.flush();
            p.waitFor();
            result = p.exitValue();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (dos != null) {
                try {
                    dos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return result;
    }
}

MediaReceiver.java

package com.yml.savelog;

import android.annotation.SuppressLint;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.media.AudioManager;

import com.yml.savelog.util.ApplicationUtils;
import com.yml.savelog.util.LogUtils;
import com.yml.savelog.util.SystemPropertiesUtils;

import org.apache.commons.lang3.StringUtils;

/**
 * 外设挂载卸载广播接受者
 */
public class MediaReceiver extends BroadcastReceiver {
    private static final String TAG = MediaReceiver.class.getSimpleName();

    @SuppressWarnings("unchecked")
    @SuppressLint("WrongConstant")
    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        String device = intent.getDataString();
        LogUtils.debug(TAG, "action : " + action + ", device : " + device);
        System.out.println("MediaReceiver      device="+device);
        if (StringUtils.isEmpty(action) || StringU
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值