Commit 437a6678 authored by steve's avatar steve

完成-适配 android 11 储存空间

parent 204240a5
......@@ -2,8 +2,10 @@ package com.go.hookNotify
import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.go.hookNotify.databinding.ActivityMainBinding
import com.go.hookNotify.units.HookTestUnit
class MainActivity : AppCompatActivity(), View.OnClickListener {
......@@ -18,11 +20,12 @@ class MainActivity : AppCompatActivity(), View.OnClickListener {
override fun onClick(view: View?) {
when (view) {
mBinding.btnChange -> {
mBinding.tvTitle.text = "OK2"
if (HookTestUnit.toMessage())
Toast.makeText(this, getString(R.string.share_hook_suc), Toast.LENGTH_SHORT).show()
else
Toast.makeText(this, getString(R.string.share_hook_failure), Toast.LENGTH_SHORT).show()
mBinding.tvTitle.text = "OK"
}
}
}
......
package com.go.hookNotify.units
import java.text.SimpleDateFormat
import java.util.*
object DateUnit {
fun getNowTime(sTimeFormat: String = "yyyy-MM-dd HH-mm-ss"): String {
val sdf = SimpleDateFormat(sTimeFormat, Locale.getDefault())
val calendar = Calendar.getInstance()
val data = calendar.time
return sdf.format(data)
}
}
\ No newline at end of file
package com.go.hookNotify.units
import android.annotation.SuppressLint
import android.content.ContentValues
import android.content.Context
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.provider.MediaStore
import android.text.TextUtils
import android.util.Log
import java.io.*
import java.lang.StringBuilder
import java.text.SimpleDateFormat
import java.util.*
object LogSaveUnit {
fun saveLogFile(documentPath: String, sContent: String) {
val sdf = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault())
val calendar = Calendar.getInstance()
val data = calendar.time
val sTime = sdf.format(data)
val TAG = LogSaveUnit::class.java.simpleName.toString()
val fileName = "$sTime.txt"
fun saveLogFile(context: Context, documentPath: String, fileName: String, sContent: String) {
XPoseDebugUnit.xPoseLogDebug("$TAG-start saveLogFile")
when (Build.VERSION.SDK_INT) {
in 0..28 -> {
val destFile = File(
"${Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)}/$documentPath/",
fileName
)
save(destFile, sContent)
}
29 -> {
val destFile = File(
"${Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)}/$documentPath/",
fileName
)
}
else -> {
//val isHaveFile = checkFileHigherAndroidQ(context, fileName, sContent) ?: false
//if (!isHaveFile) saveFileHigherAndroidQ(context, documentPath, fileName, sContent)
saveFileHigherAndroidQ(context, documentPath, fileName, sContent)
}
}
}
save(destFile, sContent)
/**
* android 版本 0..28(小於29)
* 檢查文件是否存在
*/
private fun checkFileLTAndroidQ(context: Context, saveFileName: String): String {
var getSaveContent = ""
val destFile = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), saveFileName)
val uri: Uri?
if (destFile.exists()) uri = Uri.parse("file://" + destFile.absoluteFile) else return ""
try {
val fileDescriptor = uri?.let { context.contentResolver.openFileDescriptor(it, "r", null) }
val inputStream = FileInputStream(fileDescriptor?.fileDescriptor)
getSaveContent = inputStreamToString(inputStream)
} catch (e: FileNotFoundException) {
e.printStackTrace()
}
return getSaveContent
}
// /**
// * android 版本 0..28(小於29)
// * 檢查文件是否存在
// */
// private fun checkFileLTAndroidQ(context: Context, saveFileName: String): String {
// var getSaveContent = ""
// val destFile = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), saveFileName)
// val uri: Uri?
// if (destFile.exists()) uri = Uri.parse("file://" + destFile.absoluteFile) else return ""
// try {
// val fileDescriptor = uri?.let { context.contentResolver.openFileDescriptor(it, "r", null) }
// val inputStream = FileInputStream(fileDescriptor?.fileDescriptor)
// getSaveContent = inputStreamToString(inputStream)
// } catch (e: FileNotFoundException) {
// e.printStackTrace()
// }
// return getSaveContent
// }
// /**
// * android 版本 29
// * 檢查文件是否存在
// */
// private fun checkFileAndroidQ(context: Context, saveFileName: String): String {
// val imageUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
// val projection: Array<String> = arrayOf(MediaStore.Images.Media.DISPLAY_NAME, MediaStore.Images.Media._ID)
//
// //查詢
// val contentResolver = context.contentResolver
//
// //添加篩選條件
// val selection = "${MediaStore.Images.Media.DISPLAY_NAME}='$saveFileName'"
// val cursor = contentResolver.query(imageUri, projection, selection, null, null)
//
// var getSaveContent = ""
// if (cursor != null) {
// while (cursor.moveToNext()) {
// val fileIdIndex = cursor.getColumnIndex(MediaStore.Images.Media._ID)
// val thumbPath = MediaStore.Images.Media.EXTERNAL_CONTENT_URI.buildUpon().appendPath(cursor.getInt(fileIdIndex).toString()).build().toString()
// val fileUri = Uri.parse(thumbPath)
//
// try {
// val fileDescriptor = contentResolver.openAssetFileDescriptor(fileUri, "r", null)
// val inputStream = FileInputStream(fileDescriptor?.fileDescriptor)
// getSaveContent = inputStreamToString(inputStream)
// } catch (e: FileNotFoundException) {
// e.printStackTrace()
// }
//
// //只有在得到的唯一標示符
// if (!TextUtils.isEmpty(getSaveContent)) {
// break
// }
// }
// cursor.close()
// }
// return getSaveContent
// }
//
// /**
// * android 版本 大於 29
// * 檢查文件是否存在
// */
// @SuppressLint("Range")
// private fun checkFileGtAndroidQ(context: Context, saveFileName: String): String? {
// val cr = context.contentResolver
// val contentUri = MediaStore.Files.getContentUri("external")
// val projection: Array<String> = arrayOf(MediaStore.Files.FileColumns._ID, MediaStore.Files.FileColumns.DISPLAY_NAME)
// val selection = "${MediaStore.Files.FileColumns.MEDIA_TYPE}=${MediaStore.Files.FileColumns.MEDIA_TYPE_NONE}"
// val selectionArgs: Array<String>? = null
// val sortOrder = null
// val cursor = cr.query(contentUri, projection, selection, selectionArgs, sortOrder)
// var uri: Uri? = null
// val columnIndexID: Int
// if (cursor?.count == 0) {
// Log.d(TAG, "no file")
// return null
// } else {
// columnIndexID = cursor?.getColumnIndexOrThrow(MediaStore.Files.FileColumns._ID)!!
// while (cursor.moveToNext()) {
// val fileID = cursor.getLong(columnIndexID)
// val fileName = cursor.getString(cursor.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME))
// if (fileName == saveFileName) {
// uri = Uri.withAppendedPath(contentUri, fileID.toString())
// break
// }
// }
// if (uri == null) {
// Log.d(TAG, "no uri")
// return null
// } else {
// return try {
// val fieldException = cr.openAssetFileDescriptor(uri, "r", null)
/**
* android 版本 大於 29
* 檢查文件是否存在
*/
@SuppressLint("Range")
private fun checkFileHigherAndroidQ(context: Context, saveFileName: String, content: String): Boolean? {
val cr = context.contentResolver
val contentUri = MediaStore.Files.getContentUri("external")
val cursor = cr.query(contentUri, null, null, null, null)
var uri: Uri? = null
val columnIndexID: Int
if (cursor?.count == 0) {
XPoseDebugUnit.xPoseLogDebug("$TAG-no file")
return null
} else {
columnIndexID = cursor?.getColumnIndexOrThrow(MediaStore.Files.FileColumns._ID)!!
while (cursor.moveToNext()) {
val fileID = cursor.getLong(columnIndexID)
val fileName = cursor.getString(cursor.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME))
XPoseDebugUnit.xPoseLogDebug("$TAG-searchFileName=$fileName")
XPoseDebugUnit.xPoseLogDebug("$TAG-saveFileName=$saveFileName")
if (fileName == saveFileName) {
uri = Uri.withAppendedPath(contentUri, fileID.toString())
break
}
}
if (uri == null) {
XPoseDebugUnit.xPoseLogDebug("$TAG-no uri")
return null
} else {
var outputStream: FileOutputStream? = null
return try {
val fieldException = cr.openAssetFileDescriptor(uri, "r", null)
outputStream = FileOutputStream(fieldException?.fileDescriptor)
val byte = content.toByteArray()
outputStream.write(byte)
outputStream.flush()
// val inputStream = FileInputStream(fieldException?.fileDescriptor)
// val sUUID = inputStreamToString(inputStream)
// inputStream.close()
// sUUID
// } catch (e: IOException) {
// Log.d(TAG, e.message.toString())
// null
// } finally {
// cursor.close()
// }
// }
// }
// }
//
// /**
// * 流 轉為 字符串
// * @param inputStream 流
// * @return 轉換完成的字符串
// */
// private fun inputStreamToString(inputStream: InputStream): String {
// val reader = BufferedReader(InputStreamReader(inputStream))
// val sb = StringBuilder()
// try {
// reader.use { r -> r.lineSequence().forEach { sb.append(it) } }
// } catch (e: IOException) {
// e.printStackTrace()
// } finally {
// try {
// inputStream.close()
// } catch (e: IOException) {
// e.printStackTrace()
// }
// }
// return sb.toString()
// }
true
} catch (e: IOException) {
XPoseDebugUnit.xPoseLogDebug("$TAG-${e.printStackTrace()}")
null
} finally {
cursor.close()
outputStream?.let { closeOutputStream(it) }
}
}
}
}
private fun closeOutputStream(vararg os: OutputStream) {
for (i in os.indices) {
try {
os[i].close()
} catch (e: IOException) {
e.printStackTrace()
}
}
}
/**
* android 0..28 版本
* 储存文件
*/
private fun save(file: File, content: String): Boolean {
if (!createFile(file, true)) {
Log.e("FileSaveUtil", "create or delete file <\$file> failed.")
......@@ -185,6 +152,63 @@ object LogSaveUnit {
return ret
}
/**
* android 版本大于29
* 储存 文件
*/
private fun saveFileHigherAndroidQ(context: Context, documentPath: String, fileName: String, content: String): Boolean {
XPoseDebugUnit.xPoseLogDebug("$TAG-init")
val resolver = context.contentResolver
val value = ContentValues()
value.put(MediaStore.MediaColumns.DISPLAY_NAME, fileName)
value.put(MediaStore.MediaColumns.MIME_TYPE, "text/plain")
value.put(MediaStore.MediaColumns.RELATIVE_PATH, "${Environment.DIRECTORY_DOCUMENTS}/$documentPath/")
val uri = resolver.insert(MediaStore.Files.getContentUri("external"), value)
var outputStream: OutputStream? = null
XPoseDebugUnit.xPoseLogDebug("$TAG-start")
return try {
outputStream = uri?.let { context.contentResolver.openOutputStream(it) }
outputStream?.let {
it.write(content.toByteArray())
it.flush()
it.close()
}
XPoseDebugUnit.xPoseLogDebug("$TAG-true")
true
} catch (e: Exception) {
XPoseDebugUnit.xPoseLogDebug("$TAG-${e.message.toString()}")
false
}
}
/**
* 流 轉為 字符串
* @param inputStream 流
* @return 轉換完成的字符串
*/
private fun inputStreamToString(inputStream: InputStream): String {
val reader = BufferedReader(InputStreamReader(inputStream))
val sb = StringBuilder()
try {
reader.use { r -> r.lineSequence().forEach { sb.append(it) } }
} catch (e: IOException) {
e.printStackTrace()
} finally {
try {
inputStream.close()
} catch (e: IOException) {
e.printStackTrace()
}
}
return sb.toString()
}
private fun createFile(file: File?, isDeleteOldFile: Boolean): Boolean {
if (file == null) return false
if (file.exists()) {
......
......@@ -4,7 +4,6 @@ import com.go.hookNotify.units.XPoseDebugUnit
import com.go.hookNotify.xp.code.HookGlobalNotify
import com.go.hookNotify.xp.code.HookSimBoxNotify
import de.robv.android.xposed.IXposedHookLoadPackage
import de.robv.android.xposed.XposedBridge
import de.robv.android.xposed.callbacks.XC_LoadPackage
class Hook : IXposedHookLoadPackage {
......@@ -17,7 +16,7 @@ class Hook : IXposedHookLoadPackage {
xPosedHook(packageName, lpparam)
}
private fun androidSystemHook(loadPackageParam: XC_LoadPackage.LoadPackageParam?){
private fun androidSystemHook(loadPackageParam: XC_LoadPackage.LoadPackageParam?) {
XPoseDebugUnit.xPoseLogDebug("android system start")
HookGlobalNotify().handleLoadPackage(loadPackageParam)
}
......
......@@ -5,13 +5,18 @@ import android.app.Notification;
import android.content.Context;
import android.os.Bundle;
import com.go.hookNotify.units.DateUnit;
import com.go.hookNotify.units.LogSaveUnit;
import com.go.hookNotify.units.XPoseDebugUnit;
import java.util.Objects;
import java.util.Set;
import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.IXposedHookZygoteInit;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XSharedPreferences;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.callbacks.XC_LoadPackage;
......@@ -48,9 +53,10 @@ public class HookGlobalNotify implements IXposedHookLoadPackage {
Notification notification = (Notification) param.args[2];
StringBuilder sb = new StringBuilder();
sb.append("\n").append("==============================================================================").append("\n");
sb.append(DateUnit.INSTANCE.getNowTime("yyyy-MM-dd HH-mm-ss"));
sb.append("\n").append("object notification: ").append(notification.toString()).append("\n");
sb.append("object bundle:").append("\n");
Bundle bundle = notification.extras;
if (bundle != null) {
Set<String> keys = bundle.keySet();
......@@ -63,8 +69,11 @@ public class HookGlobalNotify implements IXposedHookLoadPackage {
String title = notification.extras.getString("android.title", "no Title");
// String content = notification.extras.getString("android.text", "");
String documentName = "log/" + title;
// LogSaveUnit.INSTANCE.saveLogFile(documentName, sb.toString());
String documentPath = "logGlobal/" + title;
String fileName = "GlobalLog_" + DateUnit.INSTANCE.getNowTime("yyyy-MM-dd HH-mm-ss")+".txt";
LogSaveUnit.INSTANCE.saveLogFile(mContext, documentPath, fileName, sb.toString());
}
});
}
......
package com.go.hookNotify.xp.code;
import android.app.AndroidAppHelper;
import android.app.Notification;
import android.content.ContentValues;
import android.content.Context;
import android.os.Bundle;
import com.go.hookNotify.units.DateUnit;
import com.go.hookNotify.units.LogSaveUnit;
import com.go.hookNotify.units.XPoseDebugUnit;
import com.go.hookNotify.xp.Constants;
import java.util.Map;
import java.util.Set;
import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.XC_MethodHook;
......@@ -34,10 +39,11 @@ public class HookSimBoxNotify implements IXposedHookLoadPackage {
private final String mCountryCode = "countryCode";
private final String mNormalizedNumber = "normalizedNumber";
private Context mContext = null;
@Override
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {
if (!lpparam.processName.equals(Constants.SIMBOX_NAME)) return;
XPoseDebugUnit.xPoseLogInfo("simBox start init!!");
......@@ -46,6 +52,9 @@ public class HookSimBoxNotify implements IXposedHookLoadPackage {
ContentValues.class, String.class, Map.class, String.class, boolean.class, boolean.class, boolean.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
mContext = AndroidAppHelper.currentApplication();
XPoseDebugUnit.xPoseLogInfo("Success Hook Method");
ContentValues contentValues = (ContentValues) param.args[0];
......@@ -79,12 +88,13 @@ public class HookSimBoxNotify implements IXposedHookLoadPackage {
mExt + ": " + ext + "\n" +
mCountryCode + ": " + countryCode + "\n" +
mNormalizedNumber + ": " + normalizedNumber + "\n";
XPoseDebugUnit.xPoseLogInfo(sMsg);
String documentName = "log2/" + number + "-" + imsi;
LogSaveUnit.INSTANCE.saveLogFile(documentName, sMsg); //存在手机的外部空间
super.beforeHookedMethod(param);
String documentPath = "logSimbox/" + number + "-" + imsi;
String fileName = "SimBoxLog_" + DateUnit.INSTANCE.getNowTime("yyyy-MM-dd HH-mm-ss") + ".txt";
LogSaveUnit.INSTANCE.saveLogFile(mContext, documentPath, fileName, sMsg); //存在手机外部空间
super.beforeHookedMethod(param);
}
});
}
......
<resources>
<string name="app_name">HookNotify</string>
<!--share-->
<string name="share_hook_test">Hook 測試</string>
<string name="share_hook_suc">Hook 成功</string>
<string name="share_hook_failure">Hook 失敗</string>
</resources>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment