diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a62fed5..c9d3f36 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -11,6 +11,14 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/assets/test_shell_commands.sh b/app/src/main/assets/test_shell_commands.sh new file mode 100644 index 0000000..c29a8c5 --- /dev/null +++ b/app/src/main/assets/test_shell_commands.sh @@ -0,0 +1,23 @@ +#!/system/bin/sh + +# Shell命令功能测试脚本 + +echo "=== Shell命令功能测试 ===" + +# 测试基本命令 +echo "1. 测试基本命令:" +which am +echo "am命令路径: $?" + +which pm +echo "pm命令路径: $?" + +# 测试应用启动命令 +echo "2. 测试应用启动命令:" +echo "准备启动应用..." + +# 获取当前包名 +echo "3. 当前应用信息:" +dumpsys window windows | grep -E 'mCurrentFocus|mFocusedApp' | head -5 + +echo "=== 测试完成 ===" \ No newline at end of file diff --git a/app/src/main/java/com/ouxuan/oxface/MainActivity.java b/app/src/main/java/com/ouxuan/oxface/MainActivity.java index 1a247b4..9fa8df1 100644 --- a/app/src/main/java/com/ouxuan/oxface/MainActivity.java +++ b/app/src/main/java/com/ouxuan/oxface/MainActivity.java @@ -3,12 +3,15 @@ package com.ouxuan.oxface; import androidx.appcompat.app.AppCompatActivity; import android.animation.ObjectAnimator; import android.app.Dialog; +import android.content.Context; +import android.content.Intent; import android.graphics.Color; import android.graphics.drawable.ColorDrawable; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.text.InputType; +import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.Window; @@ -34,6 +37,7 @@ import com.ouxuan.oxface.network.callback.NetworkCallback; import com.ouxuan.oxface.network.model.ApiResponse; import com.ouxuan.oxface.network.utils.NetworkUtils; import com.ouxuan.oxface.utils.KeepAliveManager; +import com.ouxuan.oxface.utils.AutoStartManager; import com.ouxuan.oxface.utils.LogManager; import com.ouxuan.oxface.utils.UtilCodeHelper; @@ -42,6 +46,8 @@ import org.json.JSONObject; public class MainActivity extends AppCompatActivity { + private static final String TAG = "MainActivity"; + private EditText editTextUsername; private EditText editTextPassword; private View buttonLogin; @@ -51,6 +57,7 @@ public class MainActivity extends AppCompatActivity { private LoginDataManager loginDataManager; // 登录数据管理器 private DeviceSelectDataManager deviceSelectDataManager; // 设备选择数据管理器 private KeepAliveManager keepAliveManager; // 保持活跃管理器 + private AutoStartManager autoStartManager; // 自启动管理器 @Override protected void onCreate(Bundle savedInstanceState) { @@ -78,9 +85,16 @@ public class MainActivity extends AppCompatActivity { keepAliveManager = KeepAliveManager.getInstance(this); keepAliveManager.startKeepAlive(this); + // 初始化自启动管理器并检查权限 + autoStartManager = AutoStartManager.getInstance(this); + autoStartManager.checkAndRequestAutoStartPermission(); + // 记录应用启动 LogManager.logOperation("MainActivity", "主界面已初始化,开始24小时无人值守模式"); + // 检查是否为自启动 + checkAutoStartStatus(); + // 检查是否可以自动登录 checkAutoLogin(); @@ -108,6 +122,40 @@ public class MainActivity extends AppCompatActivity { } /** + * 检查自启动状态 + */ + private void checkAutoStartStatus() { + Intent intent = getIntent(); + if (intent != null) { + boolean isAutoStart = intent.getBooleanExtra("auto_start", false); + if (isAutoStart) { + String bootReason = intent.getStringExtra("boot_reason"); + String startSource = intent.getStringExtra("start_source"); + long bootTime = intent.getLongExtra("boot_time", 0); + long serviceStartTime = intent.getLongExtra("service_start_time", 0); + + LogManager.logOperation("MainActivity", "检测到自动启动:" + + (bootReason != null ? bootReason : "无") + + ", 来源:" + (startSource != null ? startSource : "直接启动")); + + // 显示自启动提示 + if ("system_reboot".equals(bootReason)) { + showToast("系统重启后自动恢复,24小时无人值守模式已启动"); + } else { + showToast("应用自动启动成功"); + } + + // 清除Intent中的自启动标识,防止重复处理 + intent.removeExtra("auto_start"); + intent.removeExtra("boot_reason"); + intent.removeExtra("start_source"); + intent.removeExtra("boot_time"); + intent.removeExtra("service_start_time"); + } + } + } + + /** * 检查是否可以自动登录 */ private void checkAutoLogin() { @@ -714,11 +762,108 @@ public class MainActivity extends AppCompatActivity { buttonLogin.setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(View v) { - // showLogPathInfo(); - showToastU_api_response_data(); + // 显示自启动状态信息(调试功能) + showAutoStartStatusInfo(); return true; } }); + + // 添加双击测试功能 + setupDoubleTapTest(); + } + + /** + * 设置双击测试功能 + */ + private void setupDoubleTapTest() { + final long[] taps = new long[3]; // 增加到3个点击点 + final int[] tapCount = {0}; + + buttonLogin.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + long currentTime = System.currentTimeMillis(); + taps[tapCount[0] % 3] = currentTime; + tapCount[0]++; + + // 检查是否在300ms内三击 + if (tapCount[0] >= 3 && (currentTime - taps[(tapCount[0] - 3) % 3]) < 300) { + // 三击触发直接启动测试 + triggerDirectStartTest(); + tapCount[0] = 0; // 重置计数 + } + // 检查是否在300ms内双击 + else if (tapCount[0] >= 2 && (currentTime - taps[(tapCount[0] - 2) % 3]) < 300) { + // 双击触发自启动测试 + triggerAutoStartTest(); + tapCount[0] = 0; // 重置计数 + } else { + // 正常的登录按钮点击事件 + handleLoginButtonClick(); + } + } + }); + } + + /** + * 处理登录按钮点击事件 + */ + private void handleLoginButtonClick() { + String username = editTextUsername.getText().toString().trim(); + String password = editTextPassword.getText().toString().trim(); + + // 在测试环境下,如果用户名和密码都为空,自动填充默认账号 + if (isTestEnvironment() && username.isEmpty() && password.isEmpty()) { + username = "00167"; + password = "123456"; + + // 更新输入框显示 + editTextUsername.setText(username); + editTextPassword.setText(password); + + // 显示提示信息 + showToast("测试环境:已自动填充默认账号"); + + android.util.Log.d("MainActivity", "Test environment: Auto-filled default credentials"); + } + + if (validateInput(username, password)) { + performLogin(username, password); + } + } + + /** + * 触发自启动测试 + */ + private void triggerAutoStartTest() { + try { + // 使用反射调用测试辅助类 + Class testHelperClass = Class.forName("com.ouxuan.oxface.utils.BootSimulationHelper"); + java.lang.reflect.Method testMethod = testHelperClass.getMethod("simulateBootCompleted", android.content.Context.class); + testMethod.invoke(null, this); + + showToast("自启动测试已触发"); + } catch (Exception e) { + android.util.Log.e(TAG, "触发自启动测试失败", e); + showToast("触发自启动测试失败"); + } + } + + /** + * 直接启动应用测试 + */ + private void triggerDirectStartTest() { + try { + // 使用反射调用测试辅助类 + Class testHelperClass = Class.forName("com.ouxuan.oxface.utils.BootSimulationHelper"); + java.lang.reflect.Method testMethod = testHelperClass.getMethod("directStartApp", android.content.Context.class); + testMethod.invoke(null, this); + + showToast("直接启动测试已触发"); + } catch (Exception e) { + android.util.Log.e(TAG, "触发直接启动测试失败", e); + showToast("触发直接启动测试失败"); + } } /** @@ -793,4 +938,38 @@ public class MainActivity extends AppCompatActivity { // UtilCodeHelper.Toast.showShort(apiResponseDataJson); } + + /** + * 显示自启动状态信息(调试功能) + */ + private void showAutoStartStatusInfo() { + if (autoStartManager != null) { + String statusInfo = autoStartManager.getAutoStartStatusInfo(); + LogManager.logDebug(TAG, "自启动状态信息:\n" + statusInfo); + + // 创建弹框显示详细状态 + android.app.AlertDialog.Builder builder = new android.app.AlertDialog.Builder(this); + builder.setTitle("自启动功能状态(调试)"); + builder.setMessage(statusInfo); + builder.setPositiveButton("测试自启动", new android.content.DialogInterface.OnClickListener() { + @Override + public void onClick(android.content.DialogInterface dialog, int which) { + // 执行自启动功能测试 + autoStartManager.testAutoStartFunction(); + showToast("自启动测试已执行,请查看日志"); + } + }); + builder.setNegativeButton("关闭", null); + builder.setNeutralButton("重置统计", new android.content.DialogInterface.OnClickListener() { + @Override + public void onClick(android.content.DialogInterface dialog, int which) { + autoStartManager.resetAutoStartStats(); + showToast("自启动统计信息已重置"); + } + }); + builder.show(); + } else { + showToast("自启动管理器未初始化"); + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/ouxuan/oxface/debug/AlarmTestActivity.java b/app/src/main/java/com/ouxuan/oxface/debug/AlarmTestActivity.java new file mode 100644 index 0000000..489bf36 --- /dev/null +++ b/app/src/main/java/com/ouxuan/oxface/debug/AlarmTestActivity.java @@ -0,0 +1,257 @@ +package com.ouxuan.oxface.debug; + +import android.app.Activity; +import android.app.AlarmManager; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.ScrollView; +import android.widget.TextView; +import android.widget.Toast; + +import com.ouxuan.oxface.receiver.AlarmReceiver; +import com.ouxuan.oxface.utils.AutoStartManager; + +/** + * 闹钟机制测试Activity + */ +public class AlarmTestActivity extends Activity { + + private static final String TAG = "AlarmTest"; + private TextView textViewLog; + private ScrollView scrollViewLog; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // 创建测试界面 + createTestLayout(); + + Log.i(TAG, "闹钟机制测试Activity已启动"); + appendLog("闹钟机制测试Activity已启动"); + } + + private void createTestLayout() { + // 创建垂直布局 + LinearLayout layout = new LinearLayout(this); + layout.setOrientation(LinearLayout.VERTICAL); + layout.setPadding(50, 50, 50, 50); + + // 标题 + TextView title = new TextView(this); + title.setText("闹钟机制测试"); + title.setTextSize(18); + title.setPadding(0, 0, 0, 30); + layout.addView(title); + + // 测试按钮1:设置闹钟 + Button btnSetAlarm = new Button(this); + btnSetAlarm.setText("设置闹钟(5秒后)"); + btnSetAlarm.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + testSetAlarm(); + } + }); + layout.addView(btnSetAlarm); + + // 测试按钮2:立即触发闹钟 + Button btnTriggerAlarm = new Button(this); + btnTriggerAlarm.setText("立即触发闹钟"); + btnTriggerAlarm.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + testTriggerAlarm(); + } + }); + layout.addView(btnTriggerAlarm); + + // 测试按钮3:检查自启动状态 + Button btnCheckStatus = new Button(this); + btnCheckStatus.setText("检查自启动状态"); + btnCheckStatus.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + checkAutoStartStatus(); + } + }); + layout.addView(btnCheckStatus); + + // 测试按钮4:测试BootReceiver + Button btnTestBootReceiver = new Button(this); + btnTestBootReceiver.setText("测试BootReceiver"); + btnTestBootReceiver.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + testBootReceiver(); + } + }); + layout.addView(btnTestBootReceiver); + + // 测试按钮5:检查AlarmManager状态 + Button btnCheckAlarmManager = new Button(this); + btnCheckAlarmManager.setText("检查AlarmManager状态"); + btnCheckAlarmManager.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + checkAlarmManagerStatus(); + } + }); + layout.addView(btnCheckAlarmManager); + + // 日志显示区域 + scrollViewLog = new ScrollView(this); + textViewLog = new TextView(this); + textViewLog.setText("日志输出区域:\n"); + scrollViewLog.addView(textViewLog); + layout.addView(scrollViewLog); + + setContentView(layout); + } + + private void testSetAlarm() { + appendLog("=== 设置闹钟测试 ==="); + try { + // 获取AlarmManager + AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE); + + // 创建闹钟PendingIntent + android.app.PendingIntent pendingIntent = AlarmReceiver.createAlarmPendingIntent( + this, + true, + "alarm_test", + System.currentTimeMillis() + ); + + long triggerTime = System.currentTimeMillis() + 5000; // 5秒后触发 + + // 设置闹钟 + if (alarmManager != null) { + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { + alarmManager.setExactAndAllowWhileIdle( + AlarmManager.RTC_WAKEUP, + triggerTime, + pendingIntent + ); + } else if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) { + alarmManager.setExact( + AlarmManager.RTC_WAKEUP, + triggerTime, + pendingIntent + ); + } else { + alarmManager.set( + AlarmManager.RTC_WAKEUP, + triggerTime, + pendingIntent + ); + } + + appendLog("闹钟设置成功,将在5秒后触发"); + Toast.makeText(this, "闹钟设置成功,将在5秒后触发", Toast.LENGTH_SHORT).show(); + } else { + appendLog("AlarmManager不可用"); + Toast.makeText(this, "AlarmManager不可用", Toast.LENGTH_SHORT).show(); + } + } catch (Exception e) { + appendLog("设置闹钟失败: " + e.getMessage()); + Log.e(TAG, "设置闹钟失败", e); + Toast.makeText(this, "设置闹钟失败", Toast.LENGTH_SHORT).show(); + } + } + + private void testTriggerAlarm() { + appendLog("=== 立即触发闹钟测试 ==="); + try { + // 创建模拟的闹钟Intent + Intent alarmIntent = new Intent(this, AlarmReceiver.class); + alarmIntent.setAction(AlarmReceiver.ACTION_ALARM_TRIGGER); + alarmIntent.putExtra("auto_start", true); + alarmIntent.putExtra("boot_reason", "manual_test"); + alarmIntent.putExtra("boot_time", System.currentTimeMillis()); + + // 直接调用AlarmReceiver + AlarmReceiver alarmReceiver = new AlarmReceiver(); + alarmReceiver.onReceive(this, alarmIntent); + + appendLog("闹钟触发完成"); + Toast.makeText(this, "闹钟触发完成", Toast.LENGTH_SHORT).show(); + } catch (Exception e) { + appendLog("触发闹钟失败: " + e.getMessage()); + Log.e(TAG, "触发闹钟失败", e); + Toast.makeText(this, "触发闹钟失败", Toast.LENGTH_SHORT).show(); + } + } + + private void checkAutoStartStatus() { + appendLog("=== 自启动状态检查 ==="); + try { + AutoStartManager autoStartManager = AutoStartManager.getInstance(this); + String statusInfo = autoStartManager.getAutoStartStatusInfo(); + appendLog("自启动状态信息:\n" + statusInfo); + + Toast.makeText(this, "状态检查完成", Toast.LENGTH_SHORT).show(); + } catch (Exception e) { + appendLog("状态检查失败: " + e.getMessage()); + Log.e(TAG, "状态检查失败", e); + Toast.makeText(this, "状态检查失败", Toast.LENGTH_SHORT).show(); + } + } + + private void testBootReceiver() { + appendLog("=== 测试BootReceiver ==="); + try { + com.ouxuan.oxface.receiver.BootReceiver.testBootReceiver(this); + appendLog("BootReceiver测试完成"); + Toast.makeText(this, "BootReceiver测试完成", Toast.LENGTH_SHORT).show(); + } catch (Exception e) { + appendLog("BootReceiver测试失败: " + e.getMessage()); + Log.e(TAG, "BootReceiver测试失败", e); + Toast.makeText(this, "BootReceiver测试失败", Toast.LENGTH_SHORT).show(); + } + } + + private void checkAlarmManagerStatus() { + appendLog("=== AlarmManager状态检查 ==="); + try { + AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE); + if (alarmManager != null) { + appendLog("AlarmManager可用"); + + // 检查是否支持精确闹钟 + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { + appendLog("支持setExactAndAllowWhileIdle方法"); + } else if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) { + appendLog("支持setExact方法"); + } else { + appendLog("使用标准set方法"); + } + } else { + appendLog("AlarmManager不可用"); + } + } catch (Exception e) { + appendLog("AlarmManager状态检查失败: " + e.getMessage()); + Log.e(TAG, "AlarmManager状态检查失败", e); + } + } + + private void appendLog(String message) { + runOnUiThread(new Runnable() { + @Override + public void run() { + textViewLog.append(message + "\n"); + scrollViewLog.post(new Runnable() { + @Override + public void run() { + scrollViewLog.smoothScrollBy(0, textViewLog.getHeight()); + } + }); + } + }); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/ouxuan/oxface/debug/AutoStartComprehensiveTestActivity.java b/app/src/main/java/com/ouxuan/oxface/debug/AutoStartComprehensiveTestActivity.java new file mode 100644 index 0000000..3f91f33 --- /dev/null +++ b/app/src/main/java/com/ouxuan/oxface/debug/AutoStartComprehensiveTestActivity.java @@ -0,0 +1,183 @@ +package com.ouxuan.oxface.debug; + +import android.app.Activity; +import android.content.Context; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.ScrollView; +import android.widget.TextView; +import android.widget.Toast; + +import com.ouxuan.oxface.utils.AutoStartManager; +import com.ouxuan.oxface.utils.BootSimulationHelper; +import com.ouxuan.oxface.utils.ShellCommandManager; + +/** + * 自启动功能综合测试Activity + */ +public class AutoStartComprehensiveTestActivity extends Activity { + + private static final String TAG = "AutoStartComprehensiveTest"; + private TextView textViewLog; + private ScrollView scrollViewLog; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // 创建测试界面 + createTestLayout(); + + Log.i(TAG, "自启动功能综合测试Activity已启动"); + appendLog("自启动功能综合测试Activity已启动"); + } + + private void createTestLayout() { + // 创建垂直布局 + LinearLayout layout = new LinearLayout(this); + layout.setOrientation(LinearLayout.VERTICAL); + layout.setPadding(50, 50, 50, 50); + + // 标题 + TextView title = new TextView(this); + title.setText("自启动功能综合测试"); + title.setTextSize(18); + title.setPadding(0, 0, 0, 30); + layout.addView(title); + + // 测试按钮1:模拟系统启动 + Button btnSimulateBoot = new Button(this); + btnSimulateBoot.setText("模拟系统启动"); + btnSimulateBoot.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + testSimulateBoot(); + } + }); + layout.addView(btnSimulateBoot); + + // 测试按钮2:直接启动应用 + Button btnDirectStart = new Button(this); + btnDirectStart.setText("直接启动应用"); + btnDirectStart.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + testDirectStart(); + } + }); + layout.addView(btnDirectStart); + + // 测试按钮3:测试Shell命令 + Button btnTestShell = new Button(this); + btnTestShell.setText("测试Shell命令"); + btnTestShell.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + testShellCommands(); + } + }); + layout.addView(btnTestShell); + + // 测试按钮4:检查自启动状态 + Button btnCheckStatus = new Button(this); + btnCheckStatus.setText("检查自启动状态"); + btnCheckStatus.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + checkAutoStartStatus(); + } + }); + layout.addView(btnCheckStatus); + + // 日志显示区域 + scrollViewLog = new ScrollView(this); + textViewLog = new TextView(this); + textViewLog.setText("日志输出区域:\n"); + scrollViewLog.addView(textViewLog); + layout.addView(scrollViewLog); + + setContentView(layout); + } + + private void testSimulateBoot() { + appendLog("=== 模拟系统启动测试 ==="); + try { + BootSimulationHelper.simulateBootCompleted(this); + appendLog("模拟系统启动完成"); + Toast.makeText(this, "模拟系统启动完成", Toast.LENGTH_SHORT).show(); + } catch (Exception e) { + appendLog("模拟系统启动失败: " + e.getMessage()); + Log.e(TAG, "模拟系统启动失败", e); + Toast.makeText(this, "模拟系统启动失败", Toast.LENGTH_SHORT).show(); + } + } + + private void testDirectStart() { + appendLog("=== 直接启动应用测试 ==="); + try { + BootSimulationHelper.directStartApp(this); + appendLog("直接启动应用完成"); + Toast.makeText(this, "直接启动应用完成", Toast.LENGTH_SHORT).show(); + } catch (Exception e) { + appendLog("直接启动应用失败: " + e.getMessage()); + Log.e(TAG, "直接启动应用失败", e); + Toast.makeText(this, "直接启动应用失败", Toast.LENGTH_SHORT).show(); + } + } + + private void testShellCommands() { + appendLog("=== Shell命令测试 ==="); + try { + ShellCommandManager shellManager = ShellCommandManager.getInstance(this); + + // 检查ADB可用性 + boolean adbAvailable = shellManager.isAdbAvailable(); + appendLog("ADB可用性: " + (adbAvailable ? "可用" : "不可用")); + + if (adbAvailable) { + // 测试启动应用 + boolean result = shellManager.startCurrentApp("shell_test"); + appendLog("Shell启动应用: " + (result ? "成功" : "失败")); + } + + Toast.makeText(this, "Shell命令测试完成", Toast.LENGTH_SHORT).show(); + } catch (Exception e) { + appendLog("Shell命令测试失败: " + e.getMessage()); + Log.e(TAG, "Shell命令测试失败", e); + Toast.makeText(this, "Shell命令测试失败", Toast.LENGTH_SHORT).show(); + } + } + + private void checkAutoStartStatus() { + appendLog("=== 自启动状态检查 ==="); + try { + AutoStartManager autoStartManager = AutoStartManager.getInstance(this); + String statusInfo = autoStartManager.getAutoStartStatusInfo(); + appendLog("自启动状态信息:\n" + statusInfo); + + Toast.makeText(this, "状态检查完成", Toast.LENGTH_SHORT).show(); + } catch (Exception e) { + appendLog("状态检查失败: " + e.getMessage()); + Log.e(TAG, "状态检查失败", e); + Toast.makeText(this, "状态检查失败", Toast.LENGTH_SHORT).show(); + } + } + + private void appendLog(String message) { + runOnUiThread(new Runnable() { + @Override + public void run() { + textViewLog.append(message + "\n"); + scrollViewLog.post(new Runnable() { + @Override + public void run() { + scrollViewLog.smoothScrollBy(0, textViewLog.getHeight()); + } + }); + } + }); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/ouxuan/oxface/debug/DirectBootTestActivity.java b/app/src/main/java/com/ouxuan/oxface/debug/DirectBootTestActivity.java new file mode 100644 index 0000000..35bc5bd --- /dev/null +++ b/app/src/main/java/com/ouxuan/oxface/debug/DirectBootTestActivity.java @@ -0,0 +1,162 @@ +package com.ouxuan.oxface.debug; + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.ScrollView; +import android.widget.TextView; +import android.widget.Toast; + +import com.ouxuan.oxface.receiver.BootReceiver; +import com.ouxuan.oxface.utils.AutoStartManager; + +/** + * 直接测试BootReceiver的Activity + */ +public class DirectBootTestActivity extends Activity { + + private static final String TAG = "DirectBootTest"; + private TextView textViewLog; + private ScrollView scrollViewLog; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // 创建测试界面 + createTestLayout(); + + Log.i(TAG, "直接BootReceiver测试Activity已启动"); + appendLog("直接BootReceiver测试Activity已启动"); + } + + private void createTestLayout() { + // 创建垂直布局 + LinearLayout layout = new LinearLayout(this); + layout.setOrientation(LinearLayout.VERTICAL); + layout.setPadding(50, 50, 50, 50); + + // 标题 + TextView title = new TextView(this); + title.setText("直接BootReceiver测试"); + title.setTextSize(18); + title.setPadding(0, 0, 0, 30); + layout.addView(title); + + // 测试按钮1:直接调用BootReceiver + Button btnDirectBoot = new Button(this); + btnDirectBoot.setText("直接调用BootReceiver"); + btnDirectBoot.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + testDirectBootReceiver(); + } + }); + layout.addView(btnDirectBoot); + + // 测试按钮2:测试AlarmManager + Button btnTestAlarm = new Button(this); + btnTestAlarm.setText("测试AlarmManager"); + btnTestAlarm.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + testAlarmManager(); + } + }); + layout.addView(btnTestAlarm); + + // 测试按钮3:检查自启动状态 + Button btnCheckStatus = new Button(this); + btnCheckStatus.setText("检查自启动状态"); + btnCheckStatus.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + checkAutoStartStatus(); + } + }); + layout.addView(btnCheckStatus); + + // 日志显示区域 + scrollViewLog = new ScrollView(this); + textViewLog = new TextView(this); + textViewLog.setText("日志输出区域:\n"); + scrollViewLog.addView(textViewLog); + layout.addView(scrollViewLog); + + setContentView(layout); + } + + private void testDirectBootReceiver() { + appendLog("=== 直接调用BootReceiver测试 ==="); + try { + // 创建模拟的启动完成Intent + Intent bootIntent = new Intent(Intent.ACTION_BOOT_COMPLETED); + bootIntent.putExtra("direct_test", true); + bootIntent.putExtra("test_time", System.currentTimeMillis()); + + // 直接调用BootReceiver的onReceive方法 + BootReceiver bootReceiver = new BootReceiver(); + bootReceiver.onReceive(this, bootIntent); + + appendLog("直接调用BootReceiver完成"); + Toast.makeText(this, "直接调用BootReceiver完成", Toast.LENGTH_SHORT).show(); + } catch (Exception e) { + appendLog("直接调用BootReceiver失败: " + e.getMessage()); + Log.e(TAG, "直接调用BootReceiver失败", e); + Toast.makeText(this, "直接调用BootReceiver失败", Toast.LENGTH_SHORT).show(); + } + } + + private void testAlarmManager() { + appendLog("=== 测试AlarmManager ==="); + try { + // 直接调用BootReceiver中的handleBootCompleted方法 + BootReceiver bootReceiver = new BootReceiver(); + // 使用反射调用私有方法 + java.lang.reflect.Method method = BootReceiver.class.getDeclaredMethod("handleBootCompleted", android.content.Context.class); + method.setAccessible(true); + method.invoke(bootReceiver, this); + + appendLog("AlarmManager测试完成"); + Toast.makeText(this, "AlarmManager测试完成", Toast.LENGTH_SHORT).show(); + } catch (Exception e) { + appendLog("AlarmManager测试失败: " + e.getMessage()); + Log.e(TAG, "AlarmManager测试失败", e); + Toast.makeText(this, "AlarmManager测试失败", Toast.LENGTH_SHORT).show(); + } + } + + private void checkAutoStartStatus() { + appendLog("=== 自启动状态检查 ==="); + try { + AutoStartManager autoStartManager = AutoStartManager.getInstance(this); + String statusInfo = autoStartManager.getAutoStartStatusInfo(); + appendLog("自启动状态信息:\n" + statusInfo); + + Toast.makeText(this, "状态检查完成", Toast.LENGTH_SHORT).show(); + } catch (Exception e) { + appendLog("状态检查失败: " + e.getMessage()); + Log.e(TAG, "状态检查失败", e); + Toast.makeText(this, "状态检查失败", Toast.LENGTH_SHORT).show(); + } + } + + private void appendLog(String message) { + runOnUiThread(new Runnable() { + @Override + public void run() { + textViewLog.append(message + "\n"); + scrollViewLog.post(new Runnable() { + @Override + public void run() { + scrollViewLog.smoothScrollBy(0, textViewLog.getHeight()); + } + }); + } + }); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/ouxuan/oxface/debug/ShellTestActivity.java b/app/src/main/java/com/ouxuan/oxface/debug/ShellTestActivity.java new file mode 100644 index 0000000..5ac69a6 --- /dev/null +++ b/app/src/main/java/com/ouxuan/oxface/debug/ShellTestActivity.java @@ -0,0 +1,130 @@ +package com.ouxuan.oxface.debug; + +import android.app.Activity; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.ScrollView; +import android.widget.TextView; +import android.widget.Toast; + +import com.ouxuan.oxface.utils.ShellCommandManager; + +/** + * Shell命令功能测试Activity + */ +public class ShellTestActivity extends Activity { + + private static final String TAG = "ShellTestActivity"; + private TextView textViewLog; + private ScrollView scrollViewLog; + private ShellCommandManager shellCommandManager; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // 创建测试界面 + createTestLayout(); + + // 初始化Shell命令管理器 + shellCommandManager = ShellCommandManager.getInstance(this); + + Log.i(TAG, "Shell命令测试Activity已启动"); + appendLog("Shell命令测试Activity已启动"); + } + + private void createTestLayout() { + // 创建垂直布局 + LinearLayout layout = new LinearLayout(this); + layout.setOrientation(LinearLayout.VERTICAL); + layout.setPadding(50, 50, 50, 50); + + // 标题 + TextView title = new TextView(this); + title.setText("Shell命令功能测试"); + title.setTextSize(18); + title.setPadding(0, 0, 0, 30); + layout.addView(title); + + // 测试按钮1:检查ADB可用性 + Button btnCheckAdb = new Button(this); + btnCheckAdb.setText("检查ADB可用性"); + btnCheckAdb.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + testAdbAvailability(); + } + }); + layout.addView(btnCheckAdb); + + // 测试按钮2:测试Shell能力 + Button btnTestShell = new Button(this); + btnTestShell.setText("测试Shell能力"); + btnTestShell.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + testShellCapability(); + } + }); + layout.addView(btnTestShell); + + // 测试按钮3:启动应用测试 + Button btnStartApp = new Button(this); + btnStartApp.setText("启动应用测试"); + btnStartApp.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + testStartApp(); + } + }); + layout.addView(btnStartApp); + + // 日志显示区域 + scrollViewLog = new ScrollView(this); + textViewLog = new TextView(this); + textViewLog.setText("日志输出区域:\n"); + scrollViewLog.addView(textViewLog); + layout.addView(scrollViewLog); + + setContentView(layout); + } + + private void testAdbAvailability() { + appendLog("=== 检查ADB可用性 ==="); + boolean available = shellCommandManager.isAdbAvailable(); + appendLog("ADB可用性: " + (available ? "可用" : "不可用")); + Toast.makeText(this, "ADB可用性: " + (available ? "可用" : "不可用"), Toast.LENGTH_SHORT).show(); + } + + private void testShellCapability() { + appendLog("=== 测试Shell能力 ==="); + String capabilityInfo = shellCommandManager.getShellCapabilityInfo(); + appendLog(capabilityInfo); + Toast.makeText(this, "Shell能力测试完成", Toast.LENGTH_SHORT).show(); + } + + private void testStartApp() { + appendLog("=== 启动应用测试 ==="); + boolean result = shellCommandManager.startCurrentApp("shell_test"); + appendLog("启动结果: " + (result ? "成功" : "失败")); + Toast.makeText(this, "启动测试: " + (result ? "成功" : "失败"), Toast.LENGTH_SHORT).show(); + } + + private void appendLog(String message) { + runOnUiThread(new Runnable() { + @Override + public void run() { + textViewLog.append(message + "\n"); + scrollViewLog.post(new Runnable() { + @Override + public void run() { + scrollViewLog.smoothScrollBy(0, textViewLog.getHeight()); + } + }); + } + }); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/ouxuan/oxface/receiver/AlarmReceiver.java b/app/src/main/java/com/ouxuan/oxface/receiver/AlarmReceiver.java new file mode 100644 index 0000000..4d1f76f --- /dev/null +++ b/app/src/main/java/com/ouxuan/oxface/receiver/AlarmReceiver.java @@ -0,0 +1,114 @@ +package com.ouxuan.oxface.receiver; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.util.Log; + +import com.ouxuan.oxface.MainActivity; +import com.ouxuan.oxface.utils.LogManager; + +/** + * 闹钟广播接收器 + * 用于处理AlarmManager触发的延迟启动 + */ +public class AlarmReceiver extends BroadcastReceiver { + + private static final String TAG = "AlarmReceiver"; + public static final String ACTION_ALARM_TRIGGER = "com.ouxuan.oxface.ALARM_TRIGGER"; + + @Override + public void onReceive(Context context, Intent intent) { + if (intent == null || intent.getAction() == null) { + Log.w(TAG, "接收到空的Intent或Action"); + return; + } + + String action = intent.getAction(); + Log.i(TAG, "接收到闹钟广播: " + action); + LogManager.logOperation(TAG, "接收到闹钟广播: " + action); + + if (ACTION_ALARM_TRIGGER.equals(action)) { + handleAlarmTrigger(context, intent); + } else { + Log.w(TAG, "未处理的广播事件: " + action); + } + } + + /** + * 处理闹钟触发事件 + * @param context 上下文 + * @param intent Intent + */ + private void handleAlarmTrigger(Context context, Intent intent) { + try { + Log.i(TAG, "闹钟触发,开始启动应用"); + LogManager.logOperation(TAG, "闹钟触发,开始启动应用"); + + // 获取启动参数 + boolean isAutoStart = intent.getBooleanExtra("auto_start", false); + String bootReason = intent.getStringExtra("boot_reason"); + long bootTime = intent.getLongExtra("boot_time", 0); + + Log.i(TAG, "启动参数 - 自启动: " + isAutoStart + ", 启动原因: " + bootReason + ", 启动时间: " + bootTime); + + // 启动主活动 + Intent launchIntent = new Intent(context, MainActivity.class); + launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + launchIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + launchIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); + + // 添加自启动标识 + launchIntent.putExtra("auto_start", isAutoStart); + launchIntent.putExtra("boot_reason", bootReason); + launchIntent.putExtra("boot_time", bootTime); + +// context.startActivity(launchIntent); + + new BootReceiver().startMainActivity(context); + + Log.i(TAG, "应用启动成功"); + LogManager.logOperation(TAG, "应用启动成功,恢复24小时无人值守模式"); + + } catch (Exception e) { + Log.e(TAG, "处理闹钟触发事件失败", e); + LogManager.logError(TAG, "处理闹钟触发事件失败", e); + + // 备用方案:尝试通过服务启动 + try { + Intent serviceIntent = new Intent(context, AutoStartService.class); + serviceIntent.putExtra("start_reason", "alarm_fallback"); + serviceIntent.putExtra("boot_time", System.currentTimeMillis()); + context.startForegroundService(serviceIntent); + Log.i(TAG, "通过服务启动应用"); + LogManager.logOperation(TAG, "通过服务启动应用"); + } catch (Exception serviceError) { + Log.e(TAG, "通过服务启动也失败", serviceError); + LogManager.logError(TAG, "通过服务启动也失败", serviceError); + } + } + } + + /** + * 创建闹钟PendingIntent + * @param context 上下文 + * @param isAutoStart 是否为自启动 + * @param bootReason 启动原因 + * @param bootTime 启动时间 + * @return PendingIntent + */ + public static android.app.PendingIntent createAlarmPendingIntent(Context context, boolean isAutoStart, String bootReason, long bootTime) { + Intent alarmIntent = new Intent(context, AlarmReceiver.class); + alarmIntent.setAction(ACTION_ALARM_TRIGGER); + alarmIntent.putExtra("auto_start", isAutoStart); + alarmIntent.putExtra("boot_reason", bootReason); + alarmIntent.putExtra("boot_time", bootTime); + + return android.app.PendingIntent.getBroadcast( + context, + 0, + alarmIntent, + android.app.PendingIntent.FLAG_UPDATE_CURRENT | android.app.PendingIntent.FLAG_IMMUTABLE + ); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/ouxuan/oxface/receiver/AutoStartService.java b/app/src/main/java/com/ouxuan/oxface/receiver/AutoStartService.java new file mode 100644 index 0000000..7c328e7 --- /dev/null +++ b/app/src/main/java/com/ouxuan/oxface/receiver/AutoStartService.java @@ -0,0 +1,185 @@ +package com.ouxuan.oxface.receiver; + +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Build; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.util.Log; + +import androidx.core.app.NotificationCompat; + +import com.ouxuan.oxface.MainActivity; +import com.ouxuan.oxface.R; +import com.ouxuan.oxface.utils.LogManager; +import com.ouxuan.oxface.utils.ShellCommandManager; + +/** + * 自动启动服务 + * 用于在应用无法直接启动时,通过服务的方式启动主界面 + * 支持前台服务,确保在Android高版本中正常运行 + */ +public class AutoStartService extends Service { + + private static final String TAG = "AutoStartService"; + private static final String CHANNEL_ID = "auto_start_channel"; + private static final int NOTIFICATION_ID = 1001; + + @Override + public void onCreate() { + super.onCreate(); + Log.i(TAG, "AutoStartService创建"); + LogManager.logOperation(TAG, "自动启动服务已创建"); + + // 创建通知渠道 + createNotificationChannel(); + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + Log.i(TAG, "AutoStartService启动"); + LogManager.logOperation(TAG, "自动启动服务开始执行"); + + // 启动前台服务 + startForeground(NOTIFICATION_ID, createNotification()); + + // 延迟启动主界面 + Handler handler = new Handler(Looper.getMainLooper()); + handler.postDelayed(new Runnable() { + @Override + public void run() { + launchMainActivity(); + // 启动完成后停止服务 + stopSelf(); + } + }, 3000); // 延迟3秒 + + // 返回START_STICKY,确保服务被系统杀死后会重新启动 + return START_STICKY; + } + + @Override + public IBinder onBind(Intent intent) { + return null; + } + + @Override + public void onDestroy() { + super.onDestroy(); + Log.i(TAG, "AutoStartService销毁"); + LogManager.logOperation(TAG, "自动启动服务已停止"); + } + + /** + * 创建通知渠道(Android 8.0及以上版本需要) + */ + private void createNotificationChannel() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + NotificationChannel channel = new NotificationChannel( + CHANNEL_ID, + "自动启动服务", + NotificationManager.IMPORTANCE_LOW + ); + channel.setDescription("用于24小时无人值守应用自动启动"); + channel.setShowBadge(false); + channel.setSound(null, null); + channel.enableVibration(false); + + NotificationManager manager = getSystemService(NotificationManager.class); + if (manager != null) { + manager.createNotificationChannel(channel); + } + } + } + + /** + * 创建前台服务通知 + */ + private Notification createNotification() { + Intent notificationIntent = new Intent(this, MainActivity.class); + PendingIntent pendingIntent = PendingIntent.getActivity( + this, + 0, + notificationIntent, + PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE + ); + + return new NotificationCompat.Builder(this, CHANNEL_ID) + .setContentTitle("应用自动启动中...") + .setContentText("正在恢复24小时无人值守模式") + .setSmallIcon(R.mipmap.ic_launcher) + .setContentIntent(pendingIntent) + .setPriority(NotificationCompat.PRIORITY_LOW) + .setOngoing(true) + .setSilent(true) + .build(); + } + + /** + * 启动主界面 + */ + private void launchMainActivity() { + try { + Log.i(TAG, "开始启动主界面"); + + // 方式1: 优先尝试Shell命令启动(推荐方式) + ShellCommandManager shellManager = ShellCommandManager.getInstance(this); + if (shellManager.isAdbAvailable()) { + Log.i(TAG, "使用Shell命令启动主界面"); + boolean shellResult = shellManager.startCurrentApp("service_start"); + + if (shellResult) { + Log.i(TAG, "Shell命令启动主界面成功"); + LogManager.logOperation(TAG, "Shell命令启动主界面成功"); + return; // 成功启动,直接返回 + } else { + Log.w(TAG, "Shell命令启动失败,尝试备用方式"); + } + } else { + Log.w(TAG, "Shell命令不可用,使用传统启动方式"); + } + + // 方式2: 备用的传统 Intent 启动方式 + Intent launchIntent = new Intent(this, MainActivity.class); + launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + launchIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + launchIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); + + // 添加自启动标识 + launchIntent.putExtra("auto_start", true); + launchIntent.putExtra("start_source", "service"); + launchIntent.putExtra("service_start_time", System.currentTimeMillis()); + launchIntent.putExtra("start_method", "intent_fallback"); + + startActivity(launchIntent); + + Log.i(TAG, "传统Intent方式启动主界面完成"); + LogManager.logOperation(TAG, "传统Intent方式启动主界面成功"); + + } catch (Exception e) { + Log.e(TAG, "通过服务启动主界面失败", e); + LogManager.logError(TAG, "服务启动主界面失败", e); + + // 最后的备用方案 + try { + Intent finalIntent = new Intent(this, MainActivity.class); + finalIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + finalIntent.putExtra("auto_start", true); + finalIntent.putExtra("start_source", "service_final_fallback"); + finalIntent.putExtra("service_start_time", System.currentTimeMillis()); + startActivity(finalIntent); + Log.i(TAG, "最终备用方案启动成功"); + LogManager.logOperation(TAG, "最终备用方案启动成功"); + } catch (Exception finalException) { + Log.e(TAG, "所有启动方式都失败", finalException); + LogManager.logError(TAG, "所有启动方式都失败", finalException); + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/ouxuan/oxface/receiver/BootReceiver.java b/app/src/main/java/com/ouxuan/oxface/receiver/BootReceiver.java new file mode 100644 index 0000000..1087919 --- /dev/null +++ b/app/src/main/java/com/ouxuan/oxface/receiver/BootReceiver.java @@ -0,0 +1,227 @@ +package com.ouxuan.oxface.receiver; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.os.Handler; +import android.os.Looper; +import android.util.Log; + +import com.ouxuan.oxface.MainActivity; +import com.ouxuan.oxface.utils.AutoStartManager; +import com.ouxuan.oxface.utils.LogManager; +import com.ouxuan.oxface.utils.ShellCommandManager; + +/** + * 系统启动完成广播接收器 + * 用于在设备重启后自动启动应用 + * 支持24小时无人值守运营管理 + */ +public class BootReceiver extends BroadcastReceiver { + + private static final String TAG = "BootReceiver"; + + @Override + public void onReceive(Context context, Intent intent) { + if (intent == null || intent.getAction() == null) { + Log.w(TAG, "接收到空的Intent或Action"); + return; + } + + String action = intent.getAction(); + Log.i(TAG, "接收到广播: " + action); + LogManager.logOperation(TAG, "接收到系统广播: " + action); + + // 记录设备信息 + Log.i(TAG, "设备信息 - 厂商: " + android.os.Build.MANUFACTURER + + ", 型号: " + android.os.Build.MODEL + + ", Android版本: " + android.os.Build.VERSION.RELEASE); + + // 处理系统启动完成事件 + if (Intent.ACTION_BOOT_COMPLETED.equals(action) || + "android.intent.action.QUICKBOOT_POWERON".equals(action) || + "android.intent.action.REBOOT".equals(action)) { + + Log.i(TAG, "匹配到系统启动事件,开始处理"); + handleBootCompleted(context); + } else { + Log.w(TAG, "未处理的广播事件: " + action); + } + } + + /** + * 处理系统启动完成事件 + * @param context 上下文 + */ + private void handleBootCompleted(Context context) { + try { + Log.i(TAG, "系统启动完成,准备自动启动应用"); + LogManager.logOperation(TAG, "检测到系统重启,开始自动启动流程"); + + // 记录启动时间 + com.ouxuan.oxface.utils.AutoStartManager autoStartManager = com.ouxuan.oxface.utils.AutoStartManager.getInstance(context); + autoStartManager.recordBootTime(); + + // 使用AlarmManager实现延迟启动,确保在系统完全启动后执行 + android.app.AlarmManager alarmManager = (android.app.AlarmManager) context.getSystemService(Context.ALARM_SERVICE); + + // 创建闹钟PendingIntent + android.app.PendingIntent pendingIntent = com.ouxuan.oxface.receiver.AlarmReceiver.createAlarmPendingIntent( + context, + true, + "system_reboot", + System.currentTimeMillis() + ); + + long triggerTime = System.currentTimeMillis() + 5000; // 5秒后启动 + + // 使用更可靠的闹钟类型 + if (alarmManager != null) { + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { + // Android 6.0+ 使用精确闹钟 + alarmManager.setExactAndAllowWhileIdle( + android.app.AlarmManager.RTC_WAKEUP, + triggerTime, + pendingIntent + ); + } else if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) { + // Android 4.4+ 使用setExact + alarmManager.setExact( + android.app.AlarmManager.RTC_WAKEUP, + triggerTime, + pendingIntent + ); + } else { + // 旧版本使用标准闹钟 + alarmManager.set( + android.app.AlarmManager.RTC_WAKEUP, + triggerTime, + pendingIntent + ); + } + + Log.i(TAG, "已设置延迟启动闹钟,将在5秒后启动应用"); + LogManager.logOperation(TAG, "已设置延迟启动闹钟,将在5秒后启动应用,使用精确闹钟"); + } else { + // 如果AlarmManager不可用,直接启动 + Log.w(TAG, "AlarmManager不可用,立即启动应用"); + LogManager.logWarning(TAG, "AlarmManager不可用,立即启动应用"); + startMainActivity(context); + } + + } catch (Exception e) { + Log.e(TAG, "处理系统启动完成事件失败", e); + LogManager.logError(TAG, "自动启动失败", e); + + // 出现异常时尝试直接启动 + try { + startMainActivity(context); + } catch (Exception fallbackException) { + Log.e(TAG, "备用启动方式也失败", fallbackException); + LogManager.logError(TAG, "备用启动方式也失败", fallbackException); + } + } + } + + /** + * 启动主活动 + * @param context 上下文 + */ + public void startMainActivity(Context context) { + try { + Log.i(TAG, "开始启动主活动"); + + // 方式1: 优先尝试Shell命令启动(推荐方式) + ShellCommandManager shellManager = ShellCommandManager.getInstance(context); + if (shellManager.isAdbAvailable()) { + Log.i(TAG, "使用Shell命令启动主活动"); + boolean shellResult = shellManager.startCurrentApp("system_reboot"); + + if (shellResult) { + Log.i(TAG, "Shell命令启动主活动成功"); + LogManager.logOperation(TAG, "Shell命令启动主活动成功,恢复24小时无人值守模式"); + return; // 成功启动,直接返回 + } else { + Log.w(TAG, "Shell命令启动失败,尝试备用方式"); + } + } else { + Log.w(TAG, "Shell命令不可用,使用传统启动方式"); + } + + // 方式2: 备用的传统 Intent 启动方式 + Intent launchIntent = new Intent(context, MainActivity.class); + launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + launchIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + launchIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); + + // 添加自启动标识 + launchIntent.putExtra("auto_start", true); + launchIntent.putExtra("boot_reason", "system_reboot"); + launchIntent.putExtra("boot_time", System.currentTimeMillis()); + launchIntent.putExtra("start_method", "intent_fallback"); + + context.startActivity(launchIntent); + + Log.i(TAG, "传统Intent方式启动主活动完成"); + LogManager.logOperation(TAG, "传统Intent方式启动主活动成功,恢复24小时无人值守模式"); + + } catch (Exception e) { + Log.e(TAG, "启动主活动失败", e); + LogManager.logError(TAG, "启动主活动失败", e); + + // 如果所有方式都失败,尝试通过服务启动 + try { + Intent serviceIntent = new Intent(context, AutoStartService.class); + serviceIntent.putExtra("start_reason", "boot_receiver_fallback"); + serviceIntent.putExtra("boot_time", System.currentTimeMillis()); + context.startForegroundService(serviceIntent); + Log.i(TAG, "通过服务启动应用"); + LogManager.logOperation(TAG, "通过服务启动应用"); + } catch (Exception serviceError) { + Log.e(TAG, "通过服务启动也失败", serviceError); + LogManager.logError(TAG, "通过服务启动也失败", serviceError); + + // 最后的备用方案 + try { + Intent finalIntent = new Intent(context, MainActivity.class); + finalIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + finalIntent.putExtra("auto_start", true); + finalIntent.putExtra("boot_reason", "final_fallback"); + finalIntent.putExtra("boot_time", System.currentTimeMillis()); + context.startActivity(finalIntent); + Log.i(TAG, "最终备用方案启动成功"); + LogManager.logOperation(TAG, "最终备用方案启动成功"); + } catch (Exception finalException) { + Log.e(TAG, "所有启动方式都失败", finalException); + LogManager.logError(TAG, "所有启动方式都失败", finalException); + } + } + } + } + + /** + * 手动测试广播接收器(用于调试) + * @param context 上下文 + */ + public static void testBootReceiver(Context context) { + Log.i(TAG, "=== 开始测试广播接收器 ==="); + LogManager.logOperation(TAG, "手动测试广播接收器"); + + try { + BootReceiver receiver = new BootReceiver(); + Intent testIntent = new Intent(Intent.ACTION_BOOT_COMPLETED); + testIntent.putExtra("test_mode", true); + + receiver.onReceive(context, testIntent); + + Log.i(TAG, "测试广播发送完成"); + LogManager.logOperation(TAG, "测试广播发送完成"); + + } catch (Exception e) { + Log.e(TAG, "测试广播接收器失败", e); + LogManager.logError(TAG, "测试广播接收器失败", e); + } + + Log.i(TAG, "=== 广播接收器测试完成 ==="); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/ouxuan/oxface/utils/AutoStartManager.java b/app/src/main/java/com/ouxuan/oxface/utils/AutoStartManager.java new file mode 100644 index 0000000..9045dd0 --- /dev/null +++ b/app/src/main/java/com/ouxuan/oxface/utils/AutoStartManager.java @@ -0,0 +1,354 @@ +package com.ouxuan.oxface.utils; + +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.net.Uri; +import android.os.Build; +import android.os.PowerManager; +import android.provider.Settings; +import android.util.Log; + +import com.ouxuan.oxface.receiver.AutoStartService; +import com.ouxuan.oxface.utils.ShellCommandManager; + +/** + * 自动启动管理器 + * 统一管理应用的自动启动功能 + * 支持24小时无人值守运营管理 + */ +public class AutoStartManager { + + private static final String TAG = "AutoStartManager"; + private static final String PREFS_NAME = "auto_start_prefs"; + private static final String KEY_AUTO_START_ENABLED = "auto_start_enabled"; + private static final String KEY_LAST_BOOT_TIME = "last_boot_time"; + private static final String KEY_AUTO_START_COUNT = "auto_start_count"; + + private static volatile AutoStartManager instance; + private Context context; + private SharedPreferences preferences; + private ShellCommandManager shellCommandManager; + + private AutoStartManager(Context context) { + this.context = context.getApplicationContext(); + this.preferences = this.context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE); + this.shellCommandManager = ShellCommandManager.getInstance(this.context); + } + + /** + * 获取AutoStartManager单例实例 + * @param context 上下文 + * @return AutoStartManager实例 + */ + public static AutoStartManager getInstance(Context context) { + if (instance == null) { + synchronized (AutoStartManager.class) { + if (instance == null) { + instance = new AutoStartManager(context); + } + } + } + return instance; + } + + /** + * 检查并请求自启动权限 + * @return true 如果已有权限或请求成功 + */ + public boolean checkAndRequestAutoStartPermission() { + try { + // 检查电池优化白名单 + if (!isIgnoringBatteryOptimizations()) { + requestIgnoreBatteryOptimizations(); + } + + // 检查自启动权限(主要针对国产手机) + if (!hasAutoStartPermission()) { + Log.i(TAG, "缺少自启动权限,建议用户手动开启"); + LogManager.logWarning(TAG, "检测到可能缺少自启动权限,建议在系统设置中开启"); + return false; + } + + // 启用自动启动功能 + setAutoStartEnabled(true); + LogManager.logOperation(TAG, "自动启动权限检查完成,功能已启用"); + return true; + + } catch (Exception e) { + Log.e(TAG, "检查自启动权限失败", e); + LogManager.logError(TAG, "检查自启动权限失败", e); + return false; + } + } + + /** + * 检查是否在电池优化白名单中 + */ + private boolean isIgnoringBatteryOptimizations() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); + return powerManager != null && powerManager.isIgnoringBatteryOptimizations(context.getPackageName()); + } + return true; // 低版本默认返回true + } + + /** + * 请求忽略电池优化 + */ + private void requestIgnoreBatteryOptimizations() { + try { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS); + intent.setData(Uri.parse("package:" + context.getPackageName())); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(intent); + + Log.i(TAG, "已请求电池优化白名单权限"); + LogManager.logOperation(TAG, "已请求电池优化白名单权限"); + } + } catch (Exception e) { + Log.w(TAG, "请求电池优化白名单失败", e); + LogManager.logWarning(TAG, "请求电池优化白名单失败"); + } + } + + /** + * 检查是否有自启动权限(增强检测) + * 注意:不同厂商的自启动权限检测方式不同,这里提供基础检测 + */ + private boolean hasAutoStartPermission() { + // 基础检测:检查是否在电池优化白名单中 + boolean hasBasicPermission = isIgnoringBatteryOptimizations(); + + // 检查厂商信息 + String manufacturer = Build.MANUFACTURER.toLowerCase(); + String model = Build.MODEL.toLowerCase(); + + Log.i(TAG, "设备信息 - 厂商: " + manufacturer + ", 型号: " + model + ", Android版本: " + Build.VERSION.RELEASE); + + // 针对特定厂商的检测逻辑 + if (manufacturer.contains("xiaomi")) { + Log.w(TAG, "检测到小米设备,需要在安全中心开启自启动权限"); + return hasBasicPermission; + } else if (manufacturer.contains("huawei")) { + Log.w(TAG, "检测到华为设备,需要在手机管家开启自启动权限"); + return hasBasicPermission; + } else if (manufacturer.contains("oppo")) { + Log.w(TAG, "检测到OPPO设备,需要在设置中开启自启动权限"); + return hasBasicPermission; + } else if (manufacturer.contains("vivo")) { + Log.w(TAG, "检测到VIVO设备,需要在i管家开启自启动权限"); + return hasBasicPermission; + } else if (manufacturer.contains("meizu")) { + Log.w(TAG, "检测到魅族设备,需要在手机管家开启自启动权限"); + return hasBasicPermission; + } else if (manufacturer.contains("rockchip")) { + // Rockchip设备通常是工业设备,权限相对宽松 + Log.i(TAG, "检测到Rockchip设备,工业设备通常自启动权限较为宽松"); + return true; // 工业设备默认允许自启动 + } else { + Log.i(TAG, "检测到其他厂商设备: " + manufacturer + ",使用基础权限检测"); + return hasBasicPermission; + } + } + + /** + * 设置自动启动功能状态 + * @param enabled 是否启用 + */ + public void setAutoStartEnabled(boolean enabled) { + preferences.edit().putBoolean(KEY_AUTO_START_ENABLED, enabled).apply(); + LogManager.logOperation(TAG, "自动启动功能" + (enabled ? "已启用" : "已禁用")); + } + + /** + * 检查自动启动功能是否启用 + * @return true 如果启用 + */ + public boolean isAutoStartEnabled() { + return preferences.getBoolean(KEY_AUTO_START_ENABLED, false); + } + + /** + * 记录系统启动时间 + */ + public void recordBootTime() { + long currentTime = System.currentTimeMillis(); + long lastBootTime = preferences.getLong(KEY_LAST_BOOT_TIME, 0); + int autoStartCount = preferences.getInt(KEY_AUTO_START_COUNT, 0); + + preferences.edit() + .putLong(KEY_LAST_BOOT_TIME, currentTime) + .putInt(KEY_AUTO_START_COUNT, autoStartCount + 1) + .apply(); + + LogManager.logOperation(TAG, "记录系统启动时间,累计自启动次数: " + (autoStartCount + 1)); + } + + /** + * 获取最后一次启动时间 + * @return 启动时间戳 + */ + public long getLastBootTime() { + return preferences.getLong(KEY_LAST_BOOT_TIME, 0); + } + + /** + * 获取自动启动次数 + * @return 启动次数 + */ + public int getAutoStartCount() { + return preferences.getInt(KEY_AUTO_START_COUNT, 0); + } + + /** + * 手动触发应用启动(用于测试) + */ + public void triggerAppStart() { + triggerAppStartWithReason("manual_trigger"); + } + + /** + * 触发应用启动并指定原因 + * @param reason 启动原因 + */ + public void triggerAppStartWithReason(String reason) { + try { + Log.i(TAG, "触发应用启动 - 原因: " + reason); + LogManager.logOperation(TAG, "触发应用启动: " + reason); + + // 方式1: 尝试通过Shell命令启动(推荐方式) + if (shellCommandManager != null && shellCommandManager.isAdbAvailable()) { + Log.i(TAG, "使用Shell命令启动应用"); + boolean shellResult = shellCommandManager.startCurrentApp(reason); + + if (shellResult) { + Log.i(TAG, "Shell命令启动成功"); + LogManager.logOperation(TAG, "Shell命令启动应用成功"); + return; // 启动成功,直接返回 + } else { + Log.w(TAG, "Shell命令启动失败,尝试备用方式"); + LogManager.logWarning(TAG, "Shell命令启动失败,尝试服务启动"); + } + } else { + Log.w(TAG, "Shell命令不可用,使用服务启动方式"); + } + + // 方式2: 备用服务启动方式 + Intent serviceIntent = new Intent(context, AutoStartService.class); + serviceIntent.putExtra("start_reason", reason); + serviceIntent.putExtra("start_time", System.currentTimeMillis()); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + context.startForegroundService(serviceIntent); + } else { + context.startService(serviceIntent); + } + + Log.i(TAG, "备用服务启动已触发"); + LogManager.logOperation(TAG, "备用服务启动已触发: " + reason); + + } catch (Exception e) { + Log.e(TAG, "触发应用启动失败", e); + LogManager.logError(TAG, "触发应用启动失败: " + reason, e); + + // 最后的备用方案:直接启动Activity + try { + Intent launchIntent = new Intent(context, com.ouxuan.oxface.MainActivity.class); + launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + launchIntent.putExtra("auto_start", true); + launchIntent.putExtra("boot_reason", reason); + launchIntent.putExtra("boot_time", System.currentTimeMillis()); + context.startActivity(launchIntent); + + Log.i(TAG, "最终备用方案启动成功"); + LogManager.logOperation(TAG, "最终备用方案启动成功"); + } catch (Exception finalException) { + Log.e(TAG, "所有启动方式都失败", finalException); + LogManager.logError(TAG, "所有启动方式都失败", finalException); + } + } + } + + /** + * 获取自启动状态信息 + * @return 状态信息字符串 + */ + public String getAutoStartStatusInfo() { + StringBuilder info = new StringBuilder(); + info.append("自动启动功能: ").append(isAutoStartEnabled() ? "已启用" : "未启用").append("\n"); + info.append("电池优化白名单: ").append(isIgnoringBatteryOptimizations() ? "已加入" : "未加入").append("\n"); + info.append("累计自启动次数: ").append(getAutoStartCount()).append("\n"); + + long lastBootTime = getLastBootTime(); + if (lastBootTime > 0) { + info.append("最后启动时间: ").append(new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss", java.util.Locale.getDefault()).format(new java.util.Date(lastBootTime))).append("\n"); + } else { + info.append("最后启动时间: 未记录\n"); + } + + info.append("设备厂商: ").append(Build.MANUFACTURER).append("\n"); + info.append("系统版本: ").append(Build.VERSION.RELEASE); + + return info.toString(); + } + + /** + * 重置自启动统计信息 + */ + public void resetAutoStartStats() { + preferences.edit() + .remove(KEY_LAST_BOOT_TIME) + .remove(KEY_AUTO_START_COUNT) + .apply(); + + LogManager.logOperation(TAG, "自启动统计信息已重置"); + } + + /** + * 测试自启动功能(用于调试) + */ + public void testAutoStartFunction() { + Log.i(TAG, "=== 开始测试自启动功能 ==="); + LogManager.logOperation(TAG, "开始测试自启动功能"); + + // 1. 检查权限状态 + boolean hasPermission = checkAndRequestAutoStartPermission(); + Log.i(TAG, "权限检查结果: " + hasPermission); + + // 2. 模拟系统启动 + recordBootTime(); + + // 3. 测试Shell命令功能 + if (shellCommandManager != null) { + Log.i(TAG, "测试Shell命令功能"); + String shellTestResult = shellCommandManager.testAppStart(); + Log.i(TAG, "Shell命令测试结果:\n" + shellTestResult); + + // 显示Shell能力信息 + String capabilityInfo = shellCommandManager.getShellCapabilityInfo(); + Log.i(TAG, "Shell能力信息:\n" + capabilityInfo); + } + + // 4. 测试广播接收器 + try { + Class bootReceiverClass = Class.forName("com.ouxuan.oxface.receiver.BootReceiver"); + java.lang.reflect.Method testMethod = bootReceiverClass.getMethod("testBootReceiver", Context.class); + testMethod.invoke(null, context); + Log.i(TAG, "广播接收器测试完成"); + } catch (Exception e) { + Log.e(TAG, "广播接收器测试失败", e); + } + + // 5. 尝试启动服务(使用新的方法) + triggerAppStartWithReason("test_trigger"); + + // 6. 输出状态信息 + String statusInfo = getAutoStartStatusInfo(); + Log.i(TAG, "当前状态:\n" + statusInfo); + + Log.i(TAG, "=== 自启动功能测试完成 ==="); + LogManager.logOperation(TAG, "自启动功能测试完成"); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/ouxuan/oxface/utils/AutoStartTestHelper.java b/app/src/main/java/com/ouxuan/oxface/utils/AutoStartTestHelper.java new file mode 100644 index 0000000..ee36d79 --- /dev/null +++ b/app/src/main/java/com/ouxuan/oxface/utils/AutoStartTestHelper.java @@ -0,0 +1,93 @@ +package com.ouxuan.oxface.utils; + +import android.content.Context; +import android.content.Intent; +import android.util.Log; + +import com.ouxuan.oxface.receiver.BootReceiver; + +/** + * 自启动功能测试辅助类 + * 提供简单的测试方法来验证自启动功能 + */ +public class AutoStartTestHelper { + + private static final String TAG = "AutoStartTestHelper"; + + /** + * 手动触发自启动测试 + * 模拟系统启动完成事件 + * @param context 上下文 + */ + public static void triggerAutoStartTest(Context context) { + try { + Log.i(TAG, "手动触发自启动测试"); + + // 创建模拟的启动完成Intent + Intent bootIntent = new Intent(Intent.ACTION_BOOT_COMPLETED); + bootIntent.putExtra("test_mode", true); + bootIntent.putExtra("trigger_time", System.currentTimeMillis()); + + // 直接调用BootReceiver的onReceive方法 + BootReceiver bootReceiver = new BootReceiver(); + bootReceiver.onReceive(context, bootIntent); + + Log.i(TAG, "自启动测试触发完成"); + + } catch (Exception e) { + Log.e(TAG, "触发自启动测试失败", e); + } + } + + /** + * 测试AutoStartManager功能 + * @param context 上下文 + */ + public static void testAutoStartManager(Context context) { + try { + Log.i(TAG, "测试AutoStartManager功能"); + + AutoStartManager autoStartManager = AutoStartManager.getInstance(context); + autoStartManager.testAutoStartFunction(); + + Log.i(TAG, "AutoStartManager测试完成"); + + } catch (Exception e) { + Log.e(TAG, "测试AutoStartManager失败", e); + } + } + + /** + * 获取自启动功能状态报告 + * @param context 上下文 + * @return 状态报告 + */ + public static String getAutoStartStatusReport(Context context) { + try { + StringBuilder report = new StringBuilder(); + report.append("=== 自启动功能状态报告 ===\n"); + + // 获取AutoStartManager状态 + AutoStartManager autoStartManager = AutoStartManager.getInstance(context); + String statusInfo = autoStartManager.getAutoStartStatusInfo(); + report.append(statusInfo).append("\n"); + + // 检查Shell命令能力 + ShellCommandManager shellManager = ShellCommandManager.getInstance(context); + boolean adbAvailable = shellManager.isAdbAvailable(); + report.append("ADB可用性: ").append(adbAvailable ? "可用" : "不可用").append("\n"); + + if (adbAvailable) { + String capabilityInfo = shellManager.getShellCapabilityInfo(); + report.append("Shell能力信息:\n").append(capabilityInfo).append("\n"); + } + + report.append("=== 状态报告结束 ==="); + return report.toString(); + + } catch (Exception e) { + Log.e(TAG, "获取状态报告失败", e); + return "获取状态报告失败: " + e.getMessage(); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/ouxuan/oxface/utils/BootSimulationHelper.java b/app/src/main/java/com/ouxuan/oxface/utils/BootSimulationHelper.java new file mode 100644 index 0000000..f9b92e0 --- /dev/null +++ b/app/src/main/java/com/ouxuan/oxface/utils/BootSimulationHelper.java @@ -0,0 +1,62 @@ +package com.ouxuan.oxface.utils; + +import android.content.Context; +import android.content.Intent; +import android.util.Log; + +import com.ouxuan.oxface.MainActivity; +import com.ouxuan.oxface.receiver.BootReceiver; + +/** + * 系统启动模拟辅助类 + * 用于测试自启动功能 + */ +public class BootSimulationHelper { + + private static final String TAG = "BootSimulationHelper"; + + /** + * 模拟系统启动完成事件 + * @param context 上下文 + */ + public static void simulateBootCompleted(Context context) { + try { + Log.i(TAG, "开始模拟系统启动完成事件"); + + // 创建模拟的启动完成Intent + Intent bootIntent = new Intent(Intent.ACTION_BOOT_COMPLETED); + bootIntent.putExtra("simulation_mode", true); + bootIntent.putExtra("simulation_time", System.currentTimeMillis()); + + // 直接调用BootReceiver的onReceive方法 + BootReceiver bootReceiver = new BootReceiver(); + bootReceiver.onReceive(context, bootIntent); + + Log.i(TAG, "系统启动模拟完成"); + + } catch (Exception e) { + Log.e(TAG, "模拟系统启动失败", e); + } + } + + /** + * 直接触发应用启动(不经过广播) + * @param context 上下文 + */ + public static void directStartApp(Context context) { + try { + Log.i(TAG, "直接触发应用启动"); + + BootReceiver bootReceiver = new BootReceiver(); + // 使用反射调用私有方法 + java.lang.reflect.Method method = BootReceiver.class.getDeclaredMethod("startMainActivity", Context.class); + method.setAccessible(true); + method.invoke(bootReceiver, context); + + Log.i(TAG, "直接启动应用完成"); + + } catch (Exception e) { + Log.e(TAG, "直接启动应用失败", e); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/ouxuan/oxface/utils/ShellCommandManager.java b/app/src/main/java/com/ouxuan/oxface/utils/ShellCommandManager.java new file mode 100644 index 0000000..f4a3fd4 --- /dev/null +++ b/app/src/main/java/com/ouxuan/oxface/utils/ShellCommandManager.java @@ -0,0 +1,275 @@ +package com.ouxuan.oxface.utils; + +import android.content.Context; +import android.util.Log; + +import com.blankj.utilcode.util.ShellUtils; + +/** + * Shell命令管理器 + * 基于UtilCode的Shell工具实现命令执行功能 + * 用于在权限受限环境下实现自启动等功能 + */ +public class ShellCommandManager { + + private static final String TAG = "ShellCommandManager"; + private static volatile ShellCommandManager instance; + private Context context; + + private ShellCommandManager(Context context) { + this.context = context.getApplicationContext(); + } + + /** + * 获取ShellCommandManager单例实例 + * @param context 上下文 + * @return ShellCommandManager实例 + */ + public static ShellCommandManager getInstance(Context context) { + if (instance == null) { + synchronized (ShellCommandManager.class) { + if (instance == null) { + instance = new ShellCommandManager(context); + } + } + } + return instance; + } + + /** + * 执行Shell命令 + * @param command 要执行的命令 + * @param isRoot 是否需要root权限 + * @return 命令执行结果 + */ + public ShellUtils.CommandResult executeCommand(String command, boolean isRoot) { + try { + Log.i(TAG, "执行Shell命令: " + command + ", 需要root: " + isRoot); + LogManager.logOperation(TAG, "执行Shell命令: " + command); + + ShellUtils.CommandResult result; + if (isRoot) { + result = ShellUtils.execCmd(command, true); + } else { + result = ShellUtils.execCmd(command, false); + } + + Log.i(TAG, "命令执行结果 - 返回码: " + result.result + + ", 成功输出: " + result.successMsg + + ", 错误输出: " + result.errorMsg); + + LogManager.logOperation(TAG, "命令执行完成 - 返回码: " + result.result); + + if (result.result == 0) { + LogManager.logOperation(TAG, "命令执行成功: " + result.successMsg); + } else { + LogManager.logWarning(TAG, "命令执行失败: " + result.errorMsg); + } + + return result; + + } catch (Exception e) { + Log.e(TAG, "执行Shell命令异常", e); + LogManager.logError(TAG, "执行Shell命令异常: " + command, e); + + // 返回失败结果 + ShellUtils.CommandResult errorResult = new ShellUtils.CommandResult(-1, "", "命令执行异常: " + e.getMessage()); + return errorResult; + } + } + + /** + * 通过ADB命令启动应用 + * @param packageName 应用包名 + * @param activityName Activity名称 + * @param autoStart 是否为自启动 + * @param bootReason 启动原因 + * @return 是否执行成功 + */ + public boolean startAppByAdb(String packageName, String activityName, boolean autoStart, String bootReason) { + try { + StringBuilder command = new StringBuilder(); + command.append("am start -n ") + .append(packageName) + .append("/") + .append(activityName); + + if (autoStart) { + command.append(" --ez auto_start true"); + } + + if (bootReason != null && !bootReason.isEmpty()) { + command.append(" --es boot_reason \\\"").append(bootReason).append("\\\""); + } + + // 添加时间戳 + command.append(" --el boot_time ").append(System.currentTimeMillis()); + + Log.i(TAG, "准备通过ADB启动应用: " + command.toString()); + LogManager.logOperation(TAG, "通过ADB启动应用: " + packageName); + + // 执行命令(不需要root权限) +// ShellUtils.CommandResult result = executeCommand(command.toString(), false); + ShellUtils.CommandResult result = executeCommand(command.toString(), true); + + boolean success = result.result == 0; + if (success) { + Log.i(TAG, "通过ADB启动应用成功"); + LogManager.logOperation(TAG, "通过ADB启动应用成功: " + packageName); + } else { + Log.w(TAG, "通过ADB启动应用失败: " + result.errorMsg); + LogManager.logWarning(TAG, "通过ADB启动应用失败: " + result.errorMsg); + + // 如果是权限问题,记录详细信息 + if (result.errorMsg != null && result.errorMsg.contains("Permission Denial")) { + Log.w(TAG, "检测到权限拒绝错误,这在某些设备上是正常的"); + LogManager.logWarning(TAG, "权限拒绝错误,将使用备用启动方式"); + } + } + + return success; + + } catch (Exception e) { + Log.e(TAG, "通过ADB启动应用异常", e); + LogManager.logError(TAG, "通过ADB启动应用异常", e); + return false; + } + } + + /** + * 启动当前应用(自启动场景) + * @param bootReason 启动原因 + * @return 是否执行成功 + */ + public boolean startCurrentApp(String bootReason) { + String packageName = context.getPackageName(); + String activityName = "com.ouxuan.oxface.MainActivity"; + + Log.i(TAG, "启动当前应用 - 包名: " + packageName + ", Activity: " + activityName); + return startAppByAdb(packageName, activityName, true, bootReason); + } + + /** + * 检查ADB是否可用 + * @return ADB是否可用 + */ + public boolean isAdbAvailable() { + try { + ShellUtils.CommandResult result = executeCommand("which am", false); + boolean available = result.result == 0 && !result.successMsg.isEmpty(); + + Log.i(TAG, "ADB可用性检查: " + available); + LogManager.logDebug(TAG, "ADB可用性: " + available); + + return available; + + } catch (Exception e) { + Log.e(TAG, "检查ADB可用性异常", e); + LogManager.logError(TAG, "检查ADB可用性异常", e); + return false; + } + } + + /** + * 获取当前应用包名 + * @return 应用包名 + */ + public String getCurrentPackageName() { + return context.getPackageName(); + } + + /** + * 执行应用启动测试 + * @return 测试结果信息 + */ + public String testAppStart() { + StringBuilder testResult = new StringBuilder(); + testResult.append("=== Shell命令自启动测试 ===\n"); + + // 1. 检查ADB可用性 + boolean adbAvailable = isAdbAvailable(); + testResult.append("ADB可用性: ").append(adbAvailable ? "可用" : "不可用").append("\n"); + + // 2. 获取包名信息 + String packageName = getCurrentPackageName(); + testResult.append("当前包名: ").append(packageName).append("\n"); + + // 3. 尝试启动应用 + if (adbAvailable) { + boolean startResult = startCurrentApp("shell_test"); + testResult.append("启动测试: ").append(startResult ? "成功" : "失败").append("\n"); + } else { + testResult.append("启动测试: 跳过(ADB不可用)\n"); + } + + testResult.append("=== 测试完成 ==="); + + String result = testResult.toString(); + Log.i(TAG, result); + LogManager.logOperation(TAG, "Shell命令自启动测试完成"); + + return result; + } + + /** + * 发送广播命令 + * @param action 广播动作 + * @param packageName 目标包名 + * @return 是否执行成功 + */ + public boolean sendBroadcast(String action, String packageName) { + try { + String command = "am broadcast -a " + action; + if (packageName != null && !packageName.isEmpty()) { + command += " -p " + packageName; + } + + Log.i(TAG, "发送广播命令: " + command); + ShellUtils.CommandResult result = executeCommand(command, false); + + boolean success = result.result == 0; + if (success) { + Log.i(TAG, "广播发送成功"); + LogManager.logOperation(TAG, "广播发送成功: " + action); + } else { + Log.w(TAG, "广播发送失败: " + result.errorMsg); + LogManager.logWarning(TAG, "广播发送失败: " + result.errorMsg); + } + + return success; + + } catch (Exception e) { + Log.e(TAG, "发送广播异常", e); + LogManager.logError(TAG, "发送广播异常", e); + return false; + } + } + + /** + * 获取Shell命令执行能力信息 + * @return 能力信息字符串 + */ + public String getShellCapabilityInfo() { + StringBuilder info = new StringBuilder(); + info.append("Shell命令执行能力信息:\n"); + + // 检查基本命令 + String[] testCommands = {"which am", "which pm", "which dumpsys", "whoami"}; + + for (String cmd : testCommands) { + ShellUtils.CommandResult result = executeCommand(cmd, false); + info.append("命令 '").append(cmd).append("': "); + if (result.result == 0) { + info.append("可用 - ").append(result.successMsg.trim()).append("\n"); + } else { + info.append("不可用\n"); + } + } + + // 检查root权限 + boolean hasRoot = ShellUtils.execCmd("id", true).result == 0; + info.append("Root权限: ").append(hasRoot ? "已授权" : "未授权").append("\n"); + + return info.toString(); + } +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 8e2e39b..33e267c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,13 +4,7 @@ # 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 @@ -28,8 +22,8 @@ org.gradle.offline=false android.sdk.path=/Users/zmt/Library/Android/sdk # 24小时运行优化配置 -# 增大Gradle内存分配 -org.gradle.jvmargs=-Xmx4096m -Dfile.encoding=UTF-8 +# Gradle内存分配和JVM参数 +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError # 并行构建优化 org.gradle.parallel=true # 启用守护进程 @@ -40,5 +34,7 @@ org.gradle.caching=true # Java版本兼容性配置 # 抑制不兼容警告 android.javaCompile.suppressSourceTargetDeprecationWarning=true +# 启用AndroidX +android.enableJetifier=true # 启用R8优化 android.enableR8.fullMode=false \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 8ebee93..b69b7b5 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-9.0.0-bin.zip +distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-7.4-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists \ No newline at end of file diff --git a/test_auto_start.sh b/test_auto_start.sh new file mode 100755 index 0000000..70c5c81 --- /dev/null +++ b/test_auto_start.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +# 自启动功能测试脚本 + +echo "=== 自启动功能测试脚本 ===" + +# 启动应用 +echo "1. 启动应用..." +adb shell am start -n com.ouxuan.oxface.debug/com.ouxuan.oxface.MainActivity + +sleep 3 + +# 检查应用是否启动 +echo "2. 检查应用进程..." +adb shell ps | grep oxface + +# 发送系统启动广播(这通常会失败,因为权限问题) +echo "3. 尝试发送系统启动广播..." +adb shell am broadcast -a android.intent.action.BOOT_COMPLETED -p com.ouxuan.oxface.debug + +# 查看日志 +echo "4. 查看相关日志..." +adb logcat -d | grep -E "(BootReceiver|AutoStartManager|ShellCommandManager)" | tail -10 + +echo "=== 测试完成 ===" \ No newline at end of file diff --git a/trigger_boot_test.sh b/trigger_boot_test.sh new file mode 100755 index 0000000..5fe591f --- /dev/null +++ b/trigger_boot_test.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +# 触发系统启动测试脚本 + +echo "=== 触发系统启动测试 ===" + +# 启动应用 +echo "1. 启动应用..." +adb shell am start -n com.ouxuan.oxface.debug/com.ouxuan.oxface.MainActivity + +sleep 3 + +# 检查应用进程 +echo "2. 检查应用进程..." +adb shell ps | grep oxface + +# 清除日志 +echo "3. 清除日志..." +adb logcat -c + +# 手动触发BootReceiver(这通常会失败,因为权限问题) +echo "4. 尝试触发系统启动事件..." +adb shell am broadcast -a android.intent.action.BOOT_COMPLETED -p com.ouxuan.oxface.debug + +sleep 3 + +# 查看日志 +echo "5. 查看相关日志..." +adb logcat -d | grep -E "(BootReceiver|AutoStartManager|ShellCommandManager)" | tail -20 + +echo "=== 测试完成 ===" \ No newline at end of file