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