This commit is contained in:
Administrator 2025-07-30 16:45:15 +08:00
commit 2575c8fda3
51 changed files with 2093 additions and 0 deletions

16
.gitignore vendored Normal file
View File

@ -0,0 +1,16 @@
*.iml
.gradle
/.idea
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
local.properties

1
app/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/build

43
app/build.gradle Normal file
View File

@ -0,0 +1,43 @@
plugins {
alias(libs.plugins.android.application)
}
android {
namespace 'com.bjlx.armcloudaf'
compileSdk 36
defaultConfig {
applicationId "com.bjlx.armcloudaf"
minSdk 26
targetSdk 36
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
}
dependencies {
implementation libs.appcompat
implementation libs.material
implementation libs.activity
implementation libs.constraintlayout
testImplementation libs.junit
androidTestImplementation libs.ext.junit
androidTestImplementation libs.espresso.core
implementation("net.armcloud.xscore:xscore:1.0.0")
implementation("org.lsposed.hiddenapibypass:hiddenapibypass:4.3")
implementation 'com.alibaba:fastjson:1.2.68'
}

21
app/proguard-rules.pro vendored Normal file
View File

@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

Binary file not shown.

View File

@ -0,0 +1,37 @@
{
"version": 3,
"artifactType": {
"type": "APK",
"kind": "Directory"
},
"applicationId": "com.bjlx.armcloudaf",
"variantName": "release",
"elements": [
{
"type": "SINGLE",
"filters": [],
"attributes": [],
"versionCode": 1,
"versionName": "1.0",
"outputFile": "app-release.apk"
}
],
"elementType": "File",
"baselineProfiles": [
{
"minApi": 28,
"maxApi": 30,
"baselineProfiles": [
"baselineProfiles/1/app-release.dm"
]
},
{
"minApi": 31,
"maxApi": 2147483647,
"baselineProfiles": [
"baselineProfiles/0/app-release.dm"
]
}
],
"minSdkVersionForDexing": 26
}

View File

@ -0,0 +1,26 @@
package com.bjlx.armcloudaf;
import android.content.Context;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumented test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
assertEquals("com.bjlx.armcloudaf", appContext.getPackageName());
}
}

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application
android:allowBackup="true"
android:name=".App"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.ArmCloudAF">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@ -0,0 +1,32 @@
package androidx.app;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class Encode {
public static String md5(byte[] data) {
try {
// 创建 MessageDigest 实例指定算法为 MD5
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
// 更新 MessageDigest 以传入要加密的数据
messageDigest.update(data);
// 获取加密后的字节数组
byte[] digest = messageDigest.digest();
// 将字节数组转换为十六进制字符串
StringBuilder hexString = new StringBuilder();
for (byte b : digest) {
// 将每个字节转换为两位十六进制
String hex = Integer.toHexString(0xFF & b);
if (hex.length() == 1) {
hexString.append("0"); // 添加前导零
}
hexString.append(hex);
}
return hexString.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
}

View File

@ -0,0 +1,514 @@
package androidx.app;
import android.app.Application;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
import android.net.Uri;
import android.os.Process;
import android.text.TextUtils;
import android.util.Base64;
import android.util.Log;
import androidx.core.content.ContextCompat;
import com.alibaba.fastjson.JSON;
import com.android.core.XC_MethodHook;
import com.android.core.XSHelpers;
import com.bjlx.armcloudaf.ModifyData;
import com.bjlx.armcloudaf.MyLogger;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.ByteArrayInputStream;
import java.net.URLConnection;
import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.crypto.Cipher;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.lang.reflect.*;
import java.net.HttpURLConnection;
import java.net.ProtocolException;
import java.net.URL;
import java.security.Permission;
import java.util.List;
public class Entry {
public static final String TAG = "HKAPP(Appsflyer)";
private static final ExecutorService executor = Executors.newFixedThreadPool(5);
private static final Map<String,String> tmpMap=new HashMap<>();
/* public static void systemMain(ClassLoader classLoader, String pkg, String processName) {
Log.e(TAG,"systemMain pkg="+pkg);
doJavaHook(pkg, classLoader);
}
*/
public static void appMain(ClassLoader loader, Context context, String appClass, String pkg, String process) {
Log.d(TAG, "appClass=" + appClass + ", filesDir=" + context.getFilesDir() + ", pid=" + Process.myPid() + ", process=" + process);
if(process.contains("sandboxed_process")){
return;
}
try {
doJavaHook(pkg, loader);
}catch (Exception ex){
Log.d(TAG, Log.getStackTraceString(ex));
}
}
static String currentPackageName;
static String currentAppVersion;
private static void doJavaHook(String pkg, ClassLoader loader) {
try {
initData();
/* final AppsFlyerCollector collector = new AppsFlyerCollector(context);
currentPackageName = context.getPackageName();
currentAppVersion = context.getPackageManager().getPackageInfo(currentPackageName, 0).versionName;
Log.d(TAG,"currentPackageName="+currentPackageName+" currentAppVersion="+currentAppVersion);
*/
/* XSHelpers.findAndHookConstructor(JSONObject.class, Map.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
Map indata=(Map) param.args[0];
if (indata.containsKey("xdp")){
Log.i(TAG,"JSONObject map 找到xdp 修改前: xdp="+indata.get("xdp"));
indata.put("xdp",900);
}
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
Log.i(TAG, "#------- JSONObject Found -------#");
Log.i(TAG, "修改前:"+((JSONObject)param.thisObject).toString());
JSONObject jsonObject=(JSONObject)param.thisObject;
if (jsonObject.has("xdp")){
Log.i(TAG,"hook 找到xdp");
jsonObject.put("xdp","800");
}
if (jsonObject.has("ydp")){
Log.i(TAG,"hook 找到ydp");
}
if (jsonObject.has("x_px")){
Log.i(TAG,"hook 找到x_px");
}
if (jsonObject.has("y_px")){
Log.i(TAG,"hook 找到y_px");
}
if (jsonObject.has("d_dpi")){
Log.i(TAG,"hook 找到d_dpi");
}
if (jsonObject.has("size")){
Log.i(TAG,"hook 找到size");
}
if (jsonObject.has("arch")){
Log.i(TAG,"hook 找到arch");
}
if (jsonObject.has("cpu_abi")){
Log.i(TAG,"hook 找到cpu_abi");
}
if (jsonObject.has("cpu_abi2")){
Log.i(TAG,"hook 找到cpu_abi2");
}
if (jsonObject.has("btl")){
Log.i(TAG,"hook 找到btl");
}
if (jsonObject.has("btch")){
Log.i(TAG,"hook 找到btch");
}
if (jsonObject.has("disk")){
Log.i(TAG,"hook 找到disk");
}
if (jsonObject.has("sdk")){
Log.i(TAG,"hook 找到sdk");
}
if (jsonObject.has("network")){
Log.i(TAG,"hook 找到network");
}
if (jsonObject.has("api_ver")){
Log.i(TAG,"hook 找到api_ver");
}
if (jsonObject.has("api_ver_name")){
Log.i(TAG,"hook 找到api_ver_name");
}
if (jsonObject.has("carrier")){
Log.i(TAG,"hook 找到carrier");
}
if (jsonObject.has("product")){
Log.i(TAG,"hook 找到product");
}
if (jsonObject.has("last_boot_time")){
Log.i(TAG,"hook 找到last_boot_time");
}
if (jsonObject.has("initiating_package")){
Log.i(TAG,"hook 找到initiating_package");
}
if (jsonObject.has("advertiserId")){
Log.i(TAG,"hook 找到advertiserId");
}
logLong(TAG, "修改后:"+((JSONObject)param.thisObject).toString());
//collector.checkAndParse(param);
}
});*/
XSHelpers.findAndHookMethod(URL.class, "openConnection", new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
java.net.URL url = (java.net.URL) param.thisObject;
URLConnection origConn = (URLConnection) param.getResult();
String urlStr = url.toString();
Log.d(TAG, "afterHookedMethod: "+urlStr);
if (urlStr.contains("appsflyer") ) {
//Log.i(TAG, "#------- Found AF -------#");
MyLogger.i(TAG, "openConnection urlStr="+urlStr);
param.setResult(new XHttpURLConnection(url, (HttpURLConnection) origConn, new HttpRequestListener() {
@Override
public void onAfterRequest(XHttpURLConnection conn, byte[] b, int off, int len) {
try {
byte[] bytes = new byte[len-8-16];
System.arraycopy(b, off, bytes, 0, len-8-16);
String md5 = Encode.md5(bytes);
String afdata = tmpMap.get(md5);
Uri uri=Uri.parse(urlStr);
String app_id = uri.getQueryParameter("app_id");
String buildnumber=uri.getQueryParameter("buildnumber");
// if (urlStr.contains("conversions") || urlStr.contains("gcd") || urlStr.contains("inapps")){
//
// }
logLong(TAG,"发送数据:afdata="+afdata);
MyLogger.e(TAG,"发送数据:afdata="+afdata);
MyLogger.e(TAG,"发送数据:app_id="+app_id);
}
catch (Throwable e) {
Log.e(TAG,"发送数据异常:"+e.getMessage());
e.printStackTrace();
}
}
}));
}
}
});
XSHelpers.findAndHookMethod(Cipher.class, "doFinal", byte[].class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
byte[] bytes = (byte[]) param.args[0];
String dataStr=new String(bytes);
if (!TextUtils.isEmpty(dataStr)){
com.alibaba.fastjson.JSONObject jsonObj=convertToJson(dataStr);
if (jsonObj!=null&&jsonObj.containsKey("appsflyerKey")&&jsonObj.containsKey("af_timestamp")){
logLong(TAG,"doFinal 修改前="+jsonObj.toJSONString());
jsonObj=ModifyData.modifyData(jsonObj);
logLong(TAG,"doFinal 修改后="+jsonObj.toJSONString());
param.args[0]=jsonObj.toJSONString().getBytes();
}
}
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
byte[] bytes = (byte[]) param.args[0];
String dataStr=new String(bytes);
//Log.e(AFHookTools,"Cipher doFinal data="+dataStr);
if (!TextUtils.isEmpty(dataStr)){
com.alibaba.fastjson.JSONObject jsonObj=convertToJson(dataStr);
if (jsonObj!=null&&jsonObj.containsKey("appsflyerKey")&&jsonObj.containsKey("af_timestamp")){
byte[] result = (byte[])param.getResult();
String md5 = Encode.md5(result);
//Log.e(AFHookTools,"md5="+md5+" encodeData="+dataStr);
tmpMap.put(md5,dataStr);
}
}
}
});
} catch (Throwable e) {
e.printStackTrace();
}
}
private static void broadcast(Context context, JSONObject collectedData)
{
new Thread(() -> {
try {
Intent intent = new Intent("com.titan.HOOK_BROADCAST");
intent.setClassName("com.titan.taskpolling", "com.titan.taskpolling.HookBroadcastReceiver");
intent.putExtra("extractType", "AF");
intent.putExtra("packageName", context.getPackageName());
intent.putExtra("collectedData", collectedData.toString());
context.sendBroadcast(intent);
} catch (Throwable e) {
e.printStackTrace();
}
}).start();
}
public static byte[] decrypt(byte[] data, String key) throws Exception {
final byte[] salt = new byte[]{0, 0, 0, 0, 0, 0, 0, 0};
if (data != null && data.length > 24) {
byte[] iv = new byte[16];
System.arraycopy(data, data.length - 8 - 16, iv, 0, 16);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKeySpec seckey = new SecretKeySpec(SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1").generateSecret(new PBEKeySpec(key.toCharArray(), salt, 10000, 0x80)).getEncoded(), "AES");
cipher.init(Cipher.DECRYPT_MODE, seckey, new IvParameterSpec(iv));
return cipher.doFinal(data, 0, data.length - 8 - 16);
}
return null;
}
public static class HttpRequestListener extends Object {
public XHttpURLConnection Owner;
public void onBeforeRequest(XHttpURLConnection conn, byte[] b, int off, int len) { }
public void onAfterRequest(XHttpURLConnection conn, byte[] b, int off, int len) { }
}
public final static class XOutputStream extends OutputStream {
private HttpRequestListener _listener;
public OutputStream Out;
protected XOutputStream(HttpRequestListener listener){ super(); _listener = listener; }
@Override
public void write(byte[] b, int off, int len) throws IOException {
if (_listener != null) { _listener.onBeforeRequest(_listener.Owner, b, off, len); }
Out.write(b, off, len);
if (_listener != null) { _listener.onAfterRequest(_listener.Owner, b, off, len); }
}
@Override
public void write(int b) throws IOException { Out.write(b); }
@Override
public void write(byte[] b) throws IOException { Out.write(b); }
@Override
public void flush() throws IOException { Out.flush(); }
@Override
public void close() throws IOException { Out.close(); }
@Override
public int hashCode() { return Out.hashCode(); }
@Override
public boolean equals(Object obj) {return Out.equals(obj); }
@Override
public String toString() { return Out.toString(); }
}
public final static class XHttpURLConnection extends HttpURLConnection {
private final URL _orgUrl;
private final HttpURLConnection _orgConn;
private final HttpRequestListener _listener;
private XOutputStream _outStream;
protected XHttpURLConnection(URL url, HttpURLConnection conn, HttpRequestListener listener) {
super(url);
_orgUrl = url;
_orgConn = conn;
_listener = listener;
_listener.Owner = this;
_outStream = new XOutputStream(_listener);
}
@Override
public OutputStream getOutputStream() throws IOException {
_outStream.Out = _orgConn.getOutputStream();
return _outStream;
}
@Override
public void connect() throws IOException {
if (_listener != null){ _listener.onBeforeRequest(this, new byte[0], 0, 0); }
_orgConn.connect();
if (_listener != null){ _listener.onAfterRequest(this, new byte[0], 0, 0); }
}
@Override
public String getHeaderFieldKey(int n) { return _orgConn.getHeaderFieldKey(n); }
@Override
public void setFixedLengthStreamingMode(int contentLength) { _orgConn.setFixedLengthStreamingMode(contentLength); }
@Override
public void setFixedLengthStreamingMode(long contentLength) { _orgConn.setFixedLengthStreamingMode(contentLength); }
@Override
public void setChunkedStreamingMode(int chunklen) { _orgConn.setChunkedStreamingMode(chunklen); }
@Override
public String getHeaderField(int n) { return _orgConn.getHeaderField(n); }
@Override
public void setInstanceFollowRedirects(boolean followRedirects) { _orgConn.setInstanceFollowRedirects(followRedirects); }
@Override
public boolean getInstanceFollowRedirects() { return _orgConn.getInstanceFollowRedirects(); }
@Override
public void setRequestMethod(String method) throws ProtocolException { _orgConn.setRequestMethod(method); }
@Override
public String getRequestMethod() { return _orgConn.getRequestMethod(); }
@Override
public int getResponseCode() throws IOException { return _orgConn.getResponseCode(); }
@Override
public String getResponseMessage() throws IOException { return _orgConn.getResponseMessage(); }
@Override
public long getHeaderFieldDate(String name, long Default) { return _orgConn.getHeaderFieldDate(name, Default); }
@Override
public Permission getPermission() throws IOException { return _orgConn.getPermission(); }
@Override
public InputStream getErrorStream() { return _orgConn.getErrorStream(); }
@Override
public void setConnectTimeout(int timeout) { _orgConn.setConnectTimeout(timeout); }
@Override
public int getConnectTimeout() { return _orgConn.getConnectTimeout(); }
@Override
public void setReadTimeout(int timeout) { _orgConn.setReadTimeout(timeout); }
@Override
public int getReadTimeout() { return _orgConn.getReadTimeout(); }
@Override
public URL getURL() { return _orgConn.getURL(); }
@Override
public int getContentLength() { return _orgConn.getContentLength(); }
@Override
public long getContentLengthLong() { return _orgConn.getContentLengthLong(); }
@Override
public String getContentType() { return _orgConn.getContentType(); }
@Override
public String getContentEncoding() { return _orgConn.getContentEncoding(); }
@Override
public long getExpiration() { return _orgConn.getExpiration(); }
@Override
public long getDate() { return _orgConn.getDate(); }
@Override
public long getLastModified() { return _orgConn.getLastModified(); }
@Override
public String getHeaderField(String name) { return _orgConn.getHeaderField(name); }
@Override
public Map<String, List<String>> getHeaderFields() { return _orgConn.getHeaderFields(); }
@Override
public int getHeaderFieldInt(String name, int Default) { return _orgConn.getHeaderFieldInt(name, Default); }
@Override
public long getHeaderFieldLong(String name, long Default) { return _orgConn.getHeaderFieldLong(name, Default); }
@Override
public Object getContent() throws IOException { return _orgConn.getContent(); }
@Override
public Object getContent(Class[] classes) throws IOException { return _orgConn.getContent(classes); }
@Override
public InputStream getInputStream() throws IOException { return _orgConn.getInputStream(); }
@Override
public String toString() { return _orgConn.toString(); }
@Override
public void setDoInput(boolean doinput) { _orgConn.setDoInput(doinput); }
@Override
public boolean getDoInput() { return _orgConn.getDoInput(); }
@Override
public void setDoOutput(boolean dooutput) { _orgConn.setDoOutput(dooutput); }
@Override
public boolean getDoOutput() { return _orgConn.getDoOutput(); }
@Override
public void setAllowUserInteraction(boolean allowuserinteraction) { _orgConn.setAllowUserInteraction(allowuserinteraction); }
@Override
public boolean getAllowUserInteraction() { return _orgConn.getAllowUserInteraction(); }
@Override
public void setUseCaches(boolean usecaches) { _orgConn.setUseCaches(usecaches); }
@Override
public boolean getUseCaches() { return _orgConn.getUseCaches(); }
@Override
public void setIfModifiedSince(long ifmodifiedsince) { _orgConn.setIfModifiedSince(ifmodifiedsince); }
@Override
public long getIfModifiedSince() { return _orgConn.getIfModifiedSince(); }
@Override
public boolean getDefaultUseCaches() { return _orgConn.getDefaultUseCaches(); }
@Override
public void setDefaultUseCaches(boolean defaultusecaches) { _orgConn.setDefaultUseCaches(defaultusecaches); }
@Override
public void setRequestProperty(String key, String value) { _orgConn.setRequestProperty(key, value); }
@Override
public void addRequestProperty(String key, String value) { _orgConn.addRequestProperty(key, value); }
@Override
public String getRequestProperty(String key) { return _orgConn.getRequestProperty(key); }
@Override
public Map<String, List<String>> getRequestProperties() { return _orgConn.getRequestProperties(); }
@Override
public void disconnect() { _orgConn.disconnect(); }
@Override
public int hashCode() { return _orgConn.hashCode(); }
@Override
public boolean equals(Object obj) { return _orgConn.equals(obj); }
@Override
public boolean usingProxy() { return _orgConn.usingProxy(); }
}
public static com.alibaba.fastjson.JSONObject convertToJson(String str)
{
try {
return JSON.parseObject(str);
} catch (Exception e) {
}
return null;
}
public static void initData(){
/*XSHelpers.findAndHookMethod(Application.class, "attach",
Context.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
Log.e(TAG,"Application attach call============");
SocketClient socketClient=new SocketClient();
socketClient.connect(new SocketClient.MessageListener() {
@Override
public void onMessage(String message) {
Log.e(TAG,"socket 收到消息:"+message);
}
});
}
});*/
XSHelpers.findAndHookMethod(Application.class, "onCreate", new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
Application application=(Application) param.thisObject;
String dataDir=application.getDataDir().getAbsolutePath();
MyLogger.init(application.getDataDir());
Log.e(TAG,"Application onCreate call dataDir="+dataDir);
ModifyData.initNewData(application.getDataDir());
}
});
}
public static void logLong(String tag, String message) {
int maxLogSize = 4000;
if (message == null) return;
int length = message.length();
int chunkCount = (int) Math.ceil((double) length / maxLogSize);
for (int i = 0; i < chunkCount; i++) {
int start = i * maxLogSize;
int end = Math.min(start + maxLogSize, length);
Log.e(tag, "参数:"+message.substring(start, end) + " _" + i);
}
}
}

View File

@ -0,0 +1,122 @@
package androidx.app;
import android.util.Log;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import org.json.JSONException;
import org.json.JSONObject;
/* loaded from: classes5.dex */
public class HttpClient {
private static final String TAG = "HttpClient";
public interface IDownloadListener {
void onError(Exception exc);
void onFinished(String str);
void onProgress(float f);
}
public static void requestPost(String urlString, JSONObject params) throws IOException {
URL url = new URL(urlString);
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setRequestMethod("POST");
con.setRequestProperty("Content-Type", "application/json");
con.setDoOutput(true);
con.setDoInput(true);
con.setConnectTimeout(30000);
con.setReadTimeout(30000);
OutputStream os = con.getOutputStream();
try {
byte[] input = params.toString().getBytes("utf-8");
os.write(input, 0, input.length);
if (os != null) {
os.close();
}
BufferedReader br = new BufferedReader(new InputStreamReader(con.getInputStream(), "utf-8"));
try {
StringBuilder response = new StringBuilder();
while (true) {
String responseLine = br.readLine();
if (responseLine != null) {
response.append(responseLine.trim());
} else {
br.close();
Log.i(TAG, "send finish");
return;
}
}
} catch (Throwable th) {
try {
br.close();
} catch (Throwable th2) {
th.addSuppressed(th2);
}
throw th;
}
} catch (Throwable th3) {
if (os != null) {
try {
os.close();
} catch (Throwable th4) {
th3.addSuppressed(th4);
}
}
throw th3;
}
}
public static JSONObject requestGet(String urlString) throws IOException, JSONException {
URL url = new URL(urlString);
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setRequestMethod("GET");
con.setRequestProperty("Content-Type", "application/json");
con.setDoOutput(true);
con.setDoInput(true);
con.setConnectTimeout(30000);
con.setReadTimeout(30000);
BufferedReader br = new BufferedReader(new InputStreamReader(con.getInputStream(), "utf-8"));
try {
StringBuilder response = new StringBuilder();
while (true) {
String responseLine = br.readLine();
if (responseLine != null) {
response.append(responseLine.trim());
} else {
JSONObject jSONObject = new JSONObject(response.toString());
br.close();
return jSONObject;
}
}
} catch (Throwable th) {
try {
br.close();
} catch (Throwable th2) {
th.addSuppressed(th2);
}
throw th;
}
}
public static JSONObject convertToJson(String str) {
try {
return new JSONObject(str);
} catch (JSONException e) {
return null;
}
}
}

View File

@ -0,0 +1,112 @@
package androidx.app;
import android.util.Log;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
public class SocketClient {
private String serverIp = "127.0.0.1";
private int port = 8888;
private Socket socket;
private PrintWriter writer;
private boolean connected = false;
private MessageListener messageListener;
public interface MessageListener {
void onMessage(String message);
}
public SocketClient() {
}
public SocketClient(String serverIp, int port) {
this.serverIp = serverIp;
this.port = port;
}
public void connect(MessageListener listener) {
this.messageListener = listener;
new Thread(() -> {
try {
socket = new Socket(serverIp, port);
writer = new PrintWriter(socket.getOutputStream(), true);
connected = true;
Log.d("SocketClient", "已连接到服务器");
if (messageListener != null) {
messageListener.onMessage("已连接到服务器");
}
startReceiving();
} catch (Exception e) {
Log.e("SocketClient", "连接失败", e);
if (messageListener != null) {
messageListener.onMessage("连接失败: " + e.getMessage());
}
}
}).start();
}
private void startReceiving() {
new Thread(() -> {
try {
BufferedReader reader = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
while (connected) {
String message = reader.readLine();
if (message == null) break;
if (messageListener != null) {
messageListener.onMessage(message);
}
}
} catch (Exception e) {
Log.e("SocketClient", "接收消息失败", e);
if (messageListener != null) {
messageListener.onMessage("接收失败: " + e.getMessage());
}
} finally {
disconnect();
}
}).start();
}
public void sendMessage(String message) {
if (!connected) return;
new Thread(() -> {
try {
if (writer != null) {
writer.println(message);
Log.d("SocketClient", "已发送消息: " + message);
}
} catch (Exception e) {
Log.e("SocketClient", "发送消息失败", e);
if (messageListener != null) {
messageListener.onMessage("发送失败: " + e.getMessage());
}
}
}).start();
}
public void disconnect() {
connected = false;
try {
if (writer != null) writer.close();
if (socket != null) socket.close();
} catch (Exception e) {
Log.e("SocketClient", "断开连接错误", e);
}
if (messageListener != null) {
messageListener.onMessage("已断开连接");
}
}
public boolean isConnected() {
return connected;
}
}

View File

@ -0,0 +1,80 @@
package com.bjlx.armcloudaf;
import java.io.ByteArrayOutputStream;
import java.util.Arrays;
import javax.crypto.Cipher;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
public class AESPatchHelper {
private static final byte[] SALT = new byte[8]; // 全零 salt
private static final int PADDING_LEN = 8;
private static final int IV_LEN = 16;
public static byte[] extractIV(byte[] data) {
int ivPos = data.length - PADDING_LEN - IV_LEN;
return Arrays.copyOfRange(data, ivPos, ivPos + IV_LEN);
}
public static byte[] extractPadding(byte[] data) {
int padPos = data.length - PADDING_LEN - IV_LEN - PADDING_LEN;
if (padPos < 0) padPos = data.length - PADDING_LEN - IV_LEN;
return Arrays.copyOfRange(data, data.length - PADDING_LEN - IV_LEN, data.length - IV_LEN);
}
public static byte[] extractCiphertext(byte[] data) {
int ctLen = data.length - PADDING_LEN - IV_LEN;
return Arrays.copyOfRange(data, 0, ctLen);
}
private static SecretKeySpec deriveKey(String password) throws Exception {
return new SecretKeySpec(
SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1")
.generateSecret(new PBEKeySpec(password.toCharArray(), SALT, 10000, 128))
.getEncoded(),
"AES"
);
}
public static byte[] decryptRaw(byte[] ciphertext, byte[] iv, String password) throws Exception {
SecretKeySpec key = deriveKey(password);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
return cipher.doFinal(ciphertext);
}
public static byte[] encryptRaw(byte[] plaintext, byte[] iv, String password) throws Exception {
SecretKeySpec key = deriveKey(password);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));
return cipher.doFinal(plaintext);
}
/**
* 入口函数
* 提取 data IV + padding + 密文 解密 加密 再拼装成结构化数据
*/
public static byte[] patchAndReEncrypt(byte[] data, String password) throws Exception {
if (data == null || data.length <= (PADDING_LEN + IV_LEN)) return null;
byte[] iv = extractIV(data);
byte[] padding = extractPadding(data);
byte[] ciphertext = extractCiphertext(data);
// 解密
byte[] plaintext = decryptRaw(ciphertext, iv, password);
// 可在此修改 plaintext如篡改某些字段
// 再加密
byte[] newCiphertext = encryptRaw(plaintext, iv, password);
// 拼装为结构[newCiphertext] + [padding] + [iv]
ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write(newCiphertext);
out.write(padding);
out.write(iv);
return out.toByteArray();
}
}

View File

@ -0,0 +1,24 @@
package com.bjlx.armcloudaf;
import android.app.Application;
public class App extends Application {
@Override
public void onCreate() {
super.onCreate();
test2();
}
private void test2(){
ClipboardUtils.pasteText(this);
/* ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
if (clipboard.hasPrimaryClip()) {
ClipData clipData = clipboard.getPrimaryClip();
if (clipData != null && clipData.getItemCount() > 0) {
CharSequence text = clipData.getItemAt(0).coerceToText(this);
Log.d("Clipboard", "剪贴板内容是:" + text);
}
}*/
}
}

View File

@ -0,0 +1,66 @@
package com.bjlx.armcloudaf;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.text.TextUtils;
import android.util.Log;
public class ClipboardUtils {
private static final String TAG = "ClipboardUtils";
public static String pasteText(Context context) {
try {
// 获取剪贴板服务
ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
if (clipboard == null) {
Log.w(TAG, "Clipboard service not available");
return null;
}
// 检查剪贴板是否有内容
if (!clipboard.hasPrimaryClip()) {
Log.d(TAG, "Clipboard is empty");
return null;
}
// 获取剪贴板内容
ClipData clipData = clipboard.getPrimaryClip();
if (clipData == null) {
Log.w(TAG, "ClipData is null");
return null;
}
// 检查项目数量
if (clipData.getItemCount() == 0) {
Log.d(TAG, "ClipData has no items");
return null;
}
// 获取第一项内容
ClipData.Item item = clipData.getItemAt(0);
// 尝试获取文本内容
CharSequence text = item.getText();
if (text != null) {
String result = text.toString();
if (!TextUtils.isEmpty(result)) {
Log.d(TAG, "Successfully pasted text: " + result.substring(0, Math.min(20, result.length())) + "...");
return result;
}
}
Log.d(TAG, "Clipboard item has no text content");
return null;
} catch (SecurityException e) {
Log.e(TAG, "Security exception when accessing clipboard", e);
return null;
} catch (Exception e) {
Log.e(TAG, "Error pasting text from clipboard", e);
return null;
}
}
}

View File

@ -0,0 +1,81 @@
package com.bjlx.armcloudaf;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import androidx.activity.EdgeToEdge;
import androidx.app.Entry;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import java.util.Arrays;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_main);
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
return insets;
});
Entry.appMain(getClassLoader(), this, "", getPackageName(), "");
test2();
findViewById(R.id.btn_test).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
test2();
}
});
}
private void test2(){
ClipboardUtils.pasteText(this);
/* ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
if (clipboard.hasPrimaryClip()) {
ClipData clipData = clipboard.getPrimaryClip();
if (clipData != null && clipData.getItemCount() > 0) {
CharSequence text = clipData.getItemAt(0).coerceToText(this);
Log.d("Clipboard", "剪贴板内容是:" + text);
}
}*/
}
private void test(){
Uri uri = Uri.parse("content://com.android.grape.deviceinfo.provider/device_info");
Uri path=Uri.withAppendedPath(
uri,
"device/d1b3e7f8c9a04b8e9f2c1d5e6f7a8b9c"
);
//new String[]{"data"}
Cursor cursor = getContentResolver().query(path, null, null, null, null);
if (cursor != null) {
while (cursor.moveToNext()) {
// 获取字段名列名数组
String[] columnNames = cursor.getColumnNames();
// 遍历每一列按名字读取
for (String columnName : columnNames) {
int columnIndex = cursor.getColumnIndex(columnName);
String columnVale=cursor.getString(columnIndex);
Log.d("Cursor", columnName + ": " + columnVale);
}
Log.d("Cursor", "——— 一行结束 ———");
}
cursor.close();
}else {
Log.e("Cursor","cursor ======== is null");
}
}
}

View File

@ -0,0 +1,143 @@
package com.bjlx.armcloudaf;
import android.os.Build;
import android.util.Base64;
import android.util.Log;
import androidx.annotation.RequiresApi;
import androidx.app.Entry;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
public class ModifyData {
private static JSONObject newData;
private final static String fileName="/device.txt";
public static JSONObject modifyData(JSONObject jsonObj){
if (newData == null) return jsonObj;
if (jsonObj.containsKey("deviceData")){
com.alibaba.fastjson.JSONObject deviceData =jsonObj.getJSONObject("deviceData");
if (deviceData.containsKey("sensors")&&newData.containsKey("sensors")){
deviceData.put("sensors",newData.getJSONArray("sensors"));
}
if (deviceData.containsKey("arch")&&newData.containsKey("arch")){
deviceData.put("arch",newData.getString("arch"));
}
if (deviceData.containsKey("cpu_abi")&&newData.containsKey("cpu_abi")){
deviceData.put("cpu_abi",newData.getString("cpu_abi"));
}
if (deviceData.containsKey("cpu_abi2")&&newData.containsKey("cpu_abi2")){
deviceData.put("cpu_abi2",newData.getString("cpu_abi2"));
}
if (deviceData.containsKey("cpu_abi2")&&newData.containsKey("cpu_abi2")){
deviceData.put("cpu_abi2",newData.getString("cpu_abi2"));
}
if (deviceData!=null&&deviceData.containsKey("dim")){
com.alibaba.fastjson.JSONObject dim =deviceData.getJSONObject("dim");
if (dim!=null){
if (dim.containsKey("xdp")&&newData.containsKey("xdp")){
dim.put("xdp",newData.getString("xdp"));
}
if (dim.containsKey("ydp")&&newData.containsKey("ydp")){
dim.put("ydp",newData.getString("ydp"));
}
if (dim.containsKey("x_px")&&newData.containsKey("x_px")){
dim.put("x_px",newData.getString("x_px"));
}
if (dim.containsKey("y_px")&&newData.containsKey("y_px")){
dim.put("y_px",newData.getString("y_px"));
}
if (dim.containsKey("d_dpi")&&newData.containsKey("d_dpi")){
dim.put("d_dpi",newData.getString("d_dpi"));
}
if (dim.containsKey("size")&&newData.containsKey("size")){
dim.put("size",newData.getString("size"));
}
}
deviceData.put("dim",dim);
}
if (deviceData.containsKey("btch")&&newData.containsKey("btch")){
deviceData.put("btch",newData.getString("btch"));
}
if (deviceData.containsKey("btl")&&newData.containsKey("btl")){
deviceData.put("btl",newData.getString("btl"));
}
jsonObj.put("deviceData",deviceData);
}
if (jsonObj.containsKey("disk")&&newData.containsKey("disk")){
jsonObj.put("disk",newData.getString("disk"));
}
if (jsonObj.containsKey("sdk")&&newData.containsKey("sdk")){
jsonObj.put("sdk",newData.getString("sdk"));
}
if (jsonObj.containsKey("network")&&newData.containsKey("network")){
jsonObj.put("network",newData.getString("network"));
}
if (jsonObj.containsKey("referrers")){
JSONArray referrers =jsonObj.getJSONArray("referrers");
JSONArray newRefs=new JSONArray();
if (referrers!=null){
for (int i = 0; i < referrers.size(); i++) {
JSONObject objitem = referrers.getJSONObject(i);
if (objitem.containsKey("api_ver")&&newData.containsKey("api_ver")){
objitem.put("api_ver",newData.getIntValue("api_ver"));
}
if (objitem.containsKey("api_ver_name")&&newData.containsKey("api_ver_name")){
objitem.put("api_ver_name",newData.getString("api_ver_name"));
}
newRefs.add(i,objitem);
}
jsonObj.put("referrers",newRefs);
}
}
if (jsonObj.containsKey("carrier")&&newData.containsKey("carrier")){
jsonObj.put("carrier",newData.getString("carrier"));
}
if (jsonObj.containsKey("product")&&newData.containsKey("product")){
jsonObj.put("product",newData.getString("product"));
}
if (jsonObj.containsKey("last_boot_time")&&newData.containsKey("last_boot_time")){
jsonObj.put("last_boot_time",newData.getLong("last_boot_time"));
}
if (jsonObj.containsKey("advertiserId")&&newData.containsKey("advertiserId")){
jsonObj.put("advertiserId",newData.getString("advertiserId"));
}
if (jsonObj.containsKey("install_source_info")&&newData.containsKey("install_source_info")){
JSONObject install_source_info=jsonObj.getJSONObject("install_source_info");
if (install_source_info!=null){
install_source_info.put("initiating_package",newData.getString("install_source_info"));
install_source_info.put("installing_package",newData.getString("install_source_info"));
}
jsonObj.put("install_source_info",install_source_info);
}
return jsonObj;
}
public static void initNewData(File fileDir){
try {
byte[] content = readFile(new File(fileDir,fileName));
byte[] msgByte=Base64.decode(content,Base64.DEFAULT);
Log.e(Entry.TAG,"initNewData msgByte="+new String(msgByte));
newData=JSONObject.parseObject(new String(msgByte));
Log.e(Entry.TAG,"数据初始化完成==========");
} catch (Exception e) {
Log.d(Entry.TAG, "initNewData: "+e.getMessage());
}
}
public static byte[] readFile(File file) {
try {
return Files.readAllBytes(file.toPath());
} catch (IOException e) {
Log.e(Entry.TAG,"读文件异常:"+e.toString());
e.printStackTrace();
return null;
}
}
}

View File

@ -0,0 +1,51 @@
package com.bjlx.armcloudaf;
import android.util.Log;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
public class MyLogger {
private static final String LOG_TAG = "MyLogger";
private static final String LOG_FILE_NAME = "/log.txt";
private static File logFile;
public static void init(File filesDir) {
logFile = new File(filesDir, LOG_FILE_NAME);
}
private static void writeToFile(String level, String tag, String msg) {
if (logFile == null){
Log.e(LOG_TAG, "日志文件未初始化");
return;
}
try (FileWriter writer = new FileWriter(logFile, true)) {
String logLine = String.format("%s/%s: %s\n", level, tag, msg);
writer.write(logLine);
writer.flush();
Log.d(LOG_TAG, "写入日志成功");
} catch (IOException e) {
Log.e(LOG_TAG, "写入日志失败: " + e.getMessage());
}
}
public static void d(String tag, String msg) {
Log.d(tag, msg);
writeToFile("D", tag, msg);
}
public static void i(String tag, String msg) {
Log.i(tag, msg);
writeToFile("I", tag, msg);
}
public static void w(String tag, String msg) {
Log.w(tag, msg);
writeToFile("W", tag, msg);
}
public static void e(String tag, String msg) {
Log.e(tag, msg);
writeToFile("E", tag, msg);
}
}

View File

@ -0,0 +1,170 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
</vector>

View File

@ -0,0 +1,30 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:text="测试"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/btn_test"
tools:ignore="MissingConstraints" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 982 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

View File

@ -0,0 +1,7 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Base.Theme.ArmCloudAF" parent="Theme.Material3.DayNight.NoActionBar">
<!-- Customize your dark theme here. -->
<!-- <item name="colorPrimary">@color/my_dark_primary</item> -->
</style>
</resources>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
</resources>

View File

@ -0,0 +1,3 @@
<resources>
<string name="app_name">ArmCloudAF</string>
</resources>

View File

@ -0,0 +1,9 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Base.Theme.ArmCloudAF" parent="Theme.Material3.DayNight.NoActionBar">
<!-- Customize your light theme here. -->
<!-- <item name="colorPrimary">@color/my_light_primary</item> -->
</style>
<style name="Theme.ArmCloudAF" parent="Base.Theme.ArmCloudAF" />
</resources>

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?><!--
Sample backup rules file; uncomment and customize as necessary.
See https://developer.android.com/guide/topics/data/autobackup
for details.
Note: This file is ignored for devices older than API 31
See https://developer.android.com/about/versions/12/backup-restore
-->
<full-backup-content>
<!--
<include domain="sharedpref" path="."/>
<exclude domain="sharedpref" path="device.xml"/>
-->
</full-backup-content>

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?><!--
Sample data extraction rules file; uncomment and customize as necessary.
See https://developer.android.com/about/versions/12/backup-restore#xml-changes
for details.
-->
<data-extraction-rules>
<cloud-backup>
<!-- TODO: Use <include> and <exclude> to control what is backed up.
<include .../>
<exclude .../>
-->
</cloud-backup>
<!--
<device-transfer>
<include .../>
<exclude .../>
</device-transfer>
-->
</data-extraction-rules>

View File

@ -0,0 +1,17 @@
package com.bjlx.armcloudaf;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() {
assertEquals(4, 2 + 2);
}
}

4
build.gradle Normal file
View File

@ -0,0 +1,4 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
alias(libs.plugins.android.application) apply false
}

21
gradle.properties Normal file
View File

@ -0,0 +1,21 @@
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. For more details, visit
# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects
# org.gradle.parallel=true
# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app's APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true
# Enables namespacing of each library's R class so that its R class includes only the
# resources declared in the library itself and none from the library's dependencies,
# thereby reducing the size of the R class for that library
android.nonTransitiveRClass=true

22
gradle/libs.versions.toml Normal file
View File

@ -0,0 +1,22 @@
[versions]
agp = "8.10.1"
junit = "4.13.2"
junitVersion = "1.1.5"
espressoCore = "3.5.1"
appcompat = "1.6.1"
material = "1.10.0"
activity = "1.8.0"
constraintlayout = "2.1.4"
[libraries]
junit = { group = "junit", name = "junit", version.ref = "junit" }
ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
material = { group = "com.google.android.material", name = "material", version.ref = "material" }
activity = { group = "androidx.activity", name = "activity", version.ref = "activity" }
constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }

BIN
gradle/wrapper/gradle-wrapper.jar vendored Normal file

Binary file not shown.

View File

@ -0,0 +1,6 @@
#Fri Jul 11 09:30:14 GMT+08:00 2025
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https://mirrors.cloud.tencent.com/gradle/gradle-8.13-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

185
gradlew vendored Normal file
View File

@ -0,0 +1,185 @@
#!/usr/bin/env sh
#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=`expr $i + 1`
done
case $i in
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=`save "$@"`
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
exec "$JAVACMD" "$@"

89
gradlew.bat vendored Normal file
View File

@ -0,0 +1,89 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

BIN
key.jks Normal file

Binary file not shown.

BIN
release/ArmCloudAF_lo.apk Normal file

Binary file not shown.

View File

@ -0,0 +1,37 @@
{
"version": 3,
"artifactType": {
"type": "APK",
"kind": "Directory"
},
"applicationId": "com.bjlx.armcloudaf",
"variantName": "release",
"elements": [
{
"type": "SINGLE",
"filters": [],
"attributes": [],
"versionCode": 1,
"versionName": "1.0",
"outputFile": "app-release.apk"
}
],
"elementType": "File",
"baselineProfiles": [
{
"minApi": 28,
"maxApi": 30,
"baselineProfiles": [
"baselineProfiles/1/app-release.dm"
]
},
{
"minApi": 31,
"maxApi": 2147483647,
"baselineProfiles": [
"baselineProfiles/0/app-release.dm"
]
}
],
"minSdkVersionForDexing": 26
}

25
settings.gradle Normal file
View File

@ -0,0 +1,25 @@
pluginManagement {
repositories {
google {
content {
includeGroupByRegex("com\\.android.*")
includeGroupByRegex("com\\.google.*")
includeGroupByRegex("androidx.*")
}
}
mavenCentral()
gradlePluginPortal()
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
maven { url "https://maven.vmos.cn" }
}
}
rootProject.name = "ArmCloudAF"
include ':app'