20251213_134317_469

This commit is contained in:
2025-12-13 13:43:22 +08:00
parent 9cc211ec51
commit 2b7108940b
7 changed files with 129 additions and 339 deletions

View File

@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle #Created by .winboll/winboll_app_build.gradle
#Sat Dec 13 03:37:48 GMT 2025 #Sat Dec 13 05:41:14 GMT 2025
stageCount=1 stageCount=1
libraryProject= libraryProject=
baseVersion=15.12 baseVersion=15.12
publishVersion=15.12.0 publishVersion=15.12.0
buildCount=81 buildCount=99
baseBetaVersion=15.12.1 baseBetaVersion=15.12.1

View File

@@ -18,11 +18,16 @@ public class App extends GlobalApplication {
super.onCreate(); super.onCreate();
// 设置应用调试标志 // 设置应用调试标志
setIsDebugging(BuildConfig.DEBUG); setIsDebugging(BuildConfig.DEBUG);
// 初始化窗口管理类 // 初始化窗口管理类
WinBoLLActivityManager.init(this); WinBoLLActivityManager.init(this);
// 初始化 Toast 框架 // 初始化 Toast 框架
ToastUtils.init(this); ToastUtils.init(this);
} }
@Override
public void onTerminate() {
super.onTerminate();
ToastUtils.release();
}
} }

View File

@@ -32,6 +32,8 @@ import cc.winboll.studio.contacts.dun.Rules;
import cc.winboll.studio.contacts.fragments.CallLogFragment; import cc.winboll.studio.contacts.fragments.CallLogFragment;
import cc.winboll.studio.contacts.fragments.ContactsFragment; import cc.winboll.studio.contacts.fragments.ContactsFragment;
import cc.winboll.studio.contacts.fragments.LogFragment; import cc.winboll.studio.contacts.fragments.LogFragment;
import cc.winboll.studio.contacts.model.MainServiceBean;
import cc.winboll.studio.contacts.services.MainService;
import cc.winboll.studio.contacts.utils.PermissionUtils; import cc.winboll.studio.contacts.utils.PermissionUtils;
import cc.winboll.studio.contacts.views.DunTemperatureView; import cc.winboll.studio.contacts.views.DunTemperatureView;
import cc.winboll.studio.libaes.interfaces.IWinBoLLActivity; import cc.winboll.studio.libaes.interfaces.IWinBoLLActivity;
@@ -111,6 +113,17 @@ public final class MainActivity extends WinBollActivity implements IWinBoLLActiv
// 直接初始化UI原权限检查逻辑注释保留按需启用 // 直接初始化UI原权限检查逻辑注释保留按需启用
initUIAndLogic(savedInstanceState); initUIAndLogic(savedInstanceState);
MainServiceBean mainServiceBean = MainServiceBean.loadBean(this, MainServiceBean.class);
if (mainServiceBean != null && mainServiceBean.isEnable()) {
Intent intent = new Intent(this, MainService.class);
// 根据应用前后台状态选择启动方式Android 12+ 后台用 startForegroundService
if (Build.VERSION.SDK_INT >= 31) {
startForegroundService(intent);
} else {
startService(intent);
}
}
LogUtils.d(TAG, "===== onCreate: 主Activity创建流程结束 ====="); LogUtils.d(TAG, "===== onCreate: 主Activity创建流程结束 =====");
} }

View File

@@ -1,49 +1,32 @@
package cc.winboll.studio.contacts.phonecallui; package cc.winboll.studio.contacts.phonecallui;
import android.content.ContentResolver;
import android.database.Cursor;
import android.media.AudioManager; import android.media.AudioManager;
import android.media.MediaRecorder;
import android.net.Uri;
import android.os.Build;
import android.provider.CallLog;
import android.telecom.Call; import android.telecom.Call;
import android.telecom.InCallService; import android.telecom.InCallService;
import android.telephony.TelephonyManager;
import cc.winboll.studio.contacts.ActivityStack; import cc.winboll.studio.contacts.ActivityStack;
import cc.winboll.studio.contacts.model.RingTongBean;
import cc.winboll.studio.contacts.dun.Rules; import cc.winboll.studio.contacts.dun.Rules;
import cc.winboll.studio.contacts.fragments.CallLogFragment; import cc.winboll.studio.contacts.fragments.CallLogFragment;
import cc.winboll.studio.contacts.model.RingTongBean;
import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.libappbase.LogUtils;
import java.io.File;
import java.io.IOException;
/** /**
* @Author aJIEw, ZhanGSKen&豆包大模型<zhangsken@qq.com> * @Author aJIEw, ZhanGSKen&豆包大模型<zhangsken@qq.com>
* @Date 2025/02/13 06:58:04 * @Date 2025/02/13 06:58:04
* @Describe 通话状态监听服务(需 Android 6.0+),负责通话状态回调、录音、铃音控制及黑白名单校验 * @Describe 通话状态监听服务(需 Android 6.0+),负责通话状态回调、铃音控制及黑白名单校验
* @see PhoneCallActivity * @see PhoneCallActivity
* @see android.telecom.InCallService * @see android.telecom.InCallService
*/ */
public class PhoneCallService extends InCallService { public class PhoneCallService extends InCallService {
// ====================== 常量定义区 ====================== // ====================== 常量定义区 ======================
public static final String TAG = "PhoneCallService"; public static final String TAG = "PhoneCallService";
// 强制指定正确常量值,彻底解决冲突 // Call 通话状态(固定正确值,避免冲突
// TelephonyManager 通话状态 private static final int CALL_STATE_IDLE = Call.STATE_NEW;
//private static final int CALL_STATE_OFFHOOK = TelephonyManager.CALL_STATE_OFFHOOK; // 固定值=2 private static final int CALL_STATE_RINGING = Call.STATE_RINGING; // 正确值=1
//private static final int CALL_STATE_IDLE = TelephonyManager.CALL_STATE_IDLE; // 固定值=0 private static final int CALL_STATE_CONNECTING = Call.STATE_CONNECTING; // 正确值=3
//private static final int CALL_STATE_RINGING_TELE = TelephonyManager.CALL_STATE_RINGING; // 固定值=1 private static final int CALL_STATE_ACTIVE = Call.STATE_ACTIVE; // 正确值=2
// Call 通话状态 private static final int CALL_STATE_DISCONNECTED = Call.STATE_DISCONNECTED; // 正确值=4
private static final int CALL_STATE_IDLE = Call.STATE_NEW;
private static final int CALL_STATE_RINGING = Call.STATE_RINGING; //正确值=1
private static final int CALL_STATE_CONNECTING = Call.STATE_CONNECTING; //正确值=3
private static final int CALL_STATE_ACTIVE = Call.STATE_ACTIVE; //正确值=2
private static final int CALL_STATE_DISCONNECTED = Call.STATE_DISCONNECTED; //正确值=4
// ====================== 成员变量区 ====================== // ====================== 成员变量区 ======================
private MediaRecorder mMediaRecorder;
private Call.Callback mCallCallback; private Call.Callback mCallCallback;
// ====================== 内部枚举类 ====================== // ====================== 内部枚举类 ======================
@@ -101,12 +84,11 @@ public class PhoneCallService extends InCallService {
@Override @Override
public void onDestroy() { public void onDestroy() {
super.onDestroy(); super.onDestroy();
releaseRecorder();
CallLogFragment.updateCallLogFragment(); CallLogFragment.updateCallLogFragment();
LogUtils.d(TAG, "onDestroy: 通话服务已销毁,资源已释放"); LogUtils.d(TAG, "onDestroy: 通话服务已销毁,资源已释放");
} }
// ====================== 核心修复:通话状态回调 ====================== // ====================== 核心:通话状态回调 ======================
private void initCallCallback() { private void initCallCallback() {
mCallCallback = new Call.Callback() { mCallCallback = new Call.Callback() {
@Override @Override
@@ -115,29 +97,24 @@ public class PhoneCallService extends InCallService {
LogUtils.d(TAG, "onStateChanged: 状态变更 | 状态码=" + state + " | 描述=" + getCallStateDesc(state)); LogUtils.d(TAG, "onStateChanged: 状态变更 | 状态码=" + state + " | 描述=" + getCallStateDesc(state));
switch (state) { switch (state) {
// 仅合并值为2的两个状态其他状态独立分支 // 通话活跃状态(仅监听,无录音)
case CALL_STATE_ACTIVE: case CALL_STATE_ACTIVE:
//case CALL_STATE_OFFHOOK: LogUtils.d(TAG, "onStateChanged: 通话已接通/活跃");
long callId = getCurrentCallId();
if (callId != -1) {
LogUtils.d(TAG, "onStateChanged: 通话接通/活跃,启动录音 | 通话ID=" + callId);
startRecording(callId);
} else {
LogUtils.w(TAG, "onStateChanged: 未获取到通话ID无法录音");
}
break; break;
// 状态码1独立分支彻底解决重复问题 // 响铃状态
case CALL_STATE_RINGING: case CALL_STATE_RINGING:
LogUtils.d(TAG, "onStateChanged: 通话处于响铃状态"); LogUtils.d(TAG, "onStateChanged: 通话处于响铃状态");
break; break;
// 空闲状态(通话挂断后)
case CALL_STATE_IDLE: case CALL_STATE_IDLE:
LogUtils.d(TAG, "onStateChanged: 通话挂断,停止录音"); LogUtils.d(TAG, "onStateChanged: 通话挂断");
stopRecording();
break; break;
// 通话断开,关闭界面
case CALL_STATE_DISCONNECTED: case CALL_STATE_DISCONNECTED:
ActivityStack.getInstance().finishActivity(PhoneCallActivity.class); ActivityStack.getInstance().finishActivity(PhoneCallActivity.class);
LogUtils.d(TAG, "onStateChanged: 通话断开,关闭通话界面"); LogUtils.d(TAG, "onStateChanged: 通话断开,关闭通话界面");
break; break;
// 通话连接中
case CALL_STATE_CONNECTING: case CALL_STATE_CONNECTING:
LogUtils.d(TAG, "onStateChanged: 通话正在连接"); LogUtils.d(TAG, "onStateChanged: 通话正在连接");
break; break;
@@ -149,7 +126,7 @@ public class PhoneCallService extends InCallService {
}; };
} }
// ====================== 核心业务方法区 ====================== // ====================== 核心业务方法区(铃音控制+黑白名单) ======================
private CallType judgeCallType(int callState) { private CallType judgeCallType(int callState) {
switch (callState) { switch (callState) {
case CALL_STATE_RINGING: case CALL_STATE_RINGING:
@@ -232,90 +209,7 @@ public class PhoneCallService extends InCallService {
LogUtils.d(TAG, "startPhoneCallActivity: 通话界面已启动"); LogUtils.d(TAG, "startPhoneCallActivity: 通话界面已启动");
} }
// ====================== 录音相关方法区 ======================
private void startRecording(long callId) {
releaseRecorder();
mMediaRecorder = new MediaRecorder();
try {
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.VOICE_CALL);
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
String path = getOutputFilePath(callId);
mMediaRecorder.setOutputFile(path);
mMediaRecorder.prepare();
mMediaRecorder.start();
LogUtils.d(TAG, "startRecording: 录音启动成功 | 路径=" + path);
} catch (IOException | IllegalStateException e) {
LogUtils.e(TAG, "startRecording: 录音启动失败", e);
releaseRecorder();
}
}
private void stopRecording() {
if (mMediaRecorder != null) {
try {
mMediaRecorder.stop();
} catch (IllegalStateException e) {
LogUtils.e(TAG, "stopRecording: 录音停止失败", e);
} finally {
releaseRecorder();
}
}
}
private String getOutputFilePath(long callId) {
File dir = getExternalFilesDir(TAG);
if (dir == null) {
dir = new File(getFilesDir(), TAG);
if (!dir.exists()) {
dir.mkdirs();
}
}
return new File(dir, String.format("call_%d.mp4", callId)).getAbsolutePath();
}
private void releaseRecorder() {
if (mMediaRecorder != null) {
mMediaRecorder.release();
mMediaRecorder = null;
LogUtils.d(TAG, "releaseRecorder: 录音资源已释放");
}
}
// ====================== 辅助工具方法区 ====================== // ====================== 辅助工具方法区 ======================
private long getCurrentCallId() {
ContentResolver resolver = getApplicationContext().getContentResolver();
if (resolver == null) {
LogUtils.w(TAG, "getCurrentCallId: 内容解析器为null");
return -1;
}
Uri uri = CallLog.Calls.CONTENT_URI;
String[] projection = {CallLog.Calls._ID};
String selection = CallLog.Calls.TYPE + " = " + CallLog.Calls.OUTGOING_TYPE
+ " OR " + CallLog.Calls.TYPE + " = " + CallLog.Calls.INCOMING_TYPE;
String sort = CallLog.Calls.DATE + " DESC";
Cursor cursor = null;
try {
cursor = resolver.query(uri, projection, selection, null, sort);
if (cursor != null && cursor.moveToFirst()) {
long id = cursor.getLong(cursor.getColumnIndex(CallLog.Calls._ID));
LogUtils.d(TAG, "getCurrentCallId: 通话ID=" + id);
return id;
}
LogUtils.w(TAG, "getCurrentCallId: 未查询到通话记录");
return -1;
} catch (SecurityException e) {
LogUtils.e(TAG, "getCurrentCallId: 查询失败(权限不足)", e);
return -1;
} finally {
if (cursor != null) {
cursor.close();
}
}
}
private String getCallStateDesc(int state) { private String getCallStateDesc(int state) {
switch (state) { switch (state) {
case CALL_STATE_RINGING: case CALL_STATE_RINGING:
@@ -324,8 +218,6 @@ public class PhoneCallService extends InCallService {
return "连接中"; return "连接中";
case CALL_STATE_ACTIVE: case CALL_STATE_ACTIVE:
return "活跃"; return "活跃";
//case CALL_STATE_OFFHOOK:
// return "摘机";
case CALL_STATE_IDLE: case CALL_STATE_IDLE:
return "空闲"; return "空闲";
case CALL_STATE_DISCONNECTED: case CALL_STATE_DISCONNECTED:
@@ -334,5 +226,18 @@ public class PhoneCallService extends InCallService {
return "未知状态"; return "未知状态";
} }
} }
// ====================== 静态内部类PhoneCallManager避免编译报错 ======================
public static class PhoneCallManager {
public static Call sCurrentCall;
private PhoneCallManager() {
// 私有构造,禁止实例化
}
public static Call getCurrentCall() {
return sCurrentCall;
}
}
} }

View File

@@ -10,13 +10,9 @@ import android.content.Intent;
import android.content.ServiceConnection; import android.content.ServiceConnection;
import android.os.Binder; import android.os.Binder;
import android.os.Build; import android.os.Build;
import android.os.Handler;
import android.os.IBinder; import android.os.IBinder;
import android.os.Looper;
import cc.winboll.studio.contacts.R; import cc.winboll.studio.contacts.R;
import cc.winboll.studio.contacts.model.MainServiceBean; import cc.winboll.studio.contacts.model.MainServiceBean;
import cc.winboll.studio.contacts.utils.AppForegroundUtils;
import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.libappbase.LogUtils;
/** /**
@@ -159,7 +155,7 @@ public class AssistantService extends Service {
LogUtils.d(TAG, "onCreate: 守护服务创建"); LogUtils.d(TAG, "onCreate: 守护服务创建");
// 适配 Android 12+ 后台启动限制:应用后台时启动为前台服务 // 适配 Android 12+ 后台启动限制:应用后台时启动为前台服务
if (Build.VERSION.SDK_INT >= ANDROID_12_API && !AppForegroundUtils.isAppForeground(this)) { if (Build.VERSION.SDK_INT >= ANDROID_12_API) {
Notification notification = createForegroundNotification(); Notification notification = createForegroundNotification();
// 修复:使用 dataSync 类型,添加异常捕获防止崩溃 // 修复:使用 dataSync 类型,添加异常捕获防止崩溃
try { try {
@@ -256,7 +252,7 @@ public class AssistantService extends Service {
Intent intent = new Intent(this, MainService.class); Intent intent = new Intent(this, MainService.class);
// 根据应用前后台状态选择启动方式Android 12+ 后台用 startForegroundService // 根据应用前后台状态选择启动方式Android 12+ 后台用 startForegroundService
if (Build.VERSION.SDK_INT >= ANDROID_12_API && !AppForegroundUtils.isAppForeground(this)) { if (Build.VERSION.SDK_INT >= ANDROID_12_API) {
LogUtils.d(TAG, "wakeupAndBindMain: 应用后台,启动主服务为前台服务"); LogUtils.d(TAG, "wakeupAndBindMain: 应用后台,启动主服务为前台服务");
startForegroundService(intent); startForegroundService(intent);
} else { } else {

View File

@@ -23,8 +23,6 @@ import cc.winboll.studio.contacts.listenphonecall.CallListenerService;
import cc.winboll.studio.contacts.model.MainServiceBean; import cc.winboll.studio.contacts.model.MainServiceBean;
import cc.winboll.studio.contacts.model.RingTongBean; import cc.winboll.studio.contacts.model.RingTongBean;
import cc.winboll.studio.contacts.receivers.MainReceiver; import cc.winboll.studio.contacts.receivers.MainReceiver;
import cc.winboll.studio.contacts.threads.MainServiceThread;
import cc.winboll.studio.contacts.utils.AppForegroundUtils;
import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.libappbase.LogUtils;
import java.util.Timer; import java.util.Timer;
import java.util.TimerTask; import java.util.TimerTask;
@@ -34,7 +32,7 @@ import java.util.TimerTask;
* @Date 2025/02/13 06:56:41 * @Date 2025/02/13 06:56:41
* @Describe 拨号主服务,负责核心业务逻辑、守护进程绑定、铃声音量监控及通话监听启动 * @Describe 拨号主服务,负责核心业务逻辑、守护进程绑定、铃声音量监控及通话监听启动
* 严格适配 Android API 30 + Java 7 语法规范 | 解决前台服务启动超时崩溃 * 严格适配 Android API 30 + Java 7 语法规范 | 解决前台服务启动超时崩溃
* 新增:统一在 mainService() 中启动电话监听服务 + 通话筛选服务 * 优化:移除延迟启动,非核心服务直接在 mainService() 中启动
*/ */
public class MainService extends Service { public class MainService extends Service {
// ====================== 常量定义区(全硬编码,杜绝高版本 API 依赖) ====================== // ====================== 常量定义区(全硬编码,杜绝高版本 API 依赖) ======================
@@ -52,9 +50,8 @@ public class MainService extends Service {
private static final int ANDROID_8_API = 26; private static final int ANDROID_8_API = 26;
private static final int ANDROID_10_API = 29; private static final int ANDROID_10_API = 29;
private static final int ANDROID_12_API = 31; private static final int ANDROID_12_API = 31;
// 重试延迟时间 & 非核心服务延迟启动时间 // 重试延迟时间(仅保留守护服务重绑定延迟,移除非核心服务延迟
private static final long RETRY_DELAY_MS = 3000L; private static final long RETRY_DELAY_MS = 3000L;
private static final long DELAY_NON_CORE_SERVICE_MS = 100L;
// ====================== 静态成员变量区 ====================== // ====================== 静态成员变量区 ======================
private static MainService sMainServiceInstance; private static MainService sMainServiceInstance;
@@ -63,14 +60,13 @@ public class MainService extends Service {
// ====================== 成员变量区 ====================== // ====================== 成员变量区 ======================
private volatile boolean mIsServiceRunning; private volatile boolean mIsServiceRunning;
private MainServiceBean mMainServiceBean; private MainServiceBean mMainServiceBean;
private MainServiceThread mMainServiceThread; //private MainServiceThread mMainServiceThread;
private MainServiceHandler mMainServiceHandler; private MainServiceHandler mMainServiceHandler;
private MyServiceConnection mMyServiceConnection; private MyServiceConnection mMyServiceConnection;
private AssistantService mAssistantService; private AssistantService mAssistantService;
private boolean mIsBound; private boolean mIsBound;
private MainReceiver mMainReceiver; private MainReceiver mMainReceiver;
private Timer mStreamVolumeCheckTimer; private Timer mStreamVolumeCheckTimer;
private Handler mDelayHandler;
// ====================== Binder 内部类 ====================== // ====================== Binder 内部类 ======================
public class MyBinder extends Binder { public class MyBinder extends Binder {
@@ -147,13 +143,7 @@ public class MainService extends Service {
} }
LogUtils.d(TAG, "startMainService: 执行启动主服务操作"); LogUtils.d(TAG, "startMainService: 执行启动主服务操作");
Intent intent = new Intent(context, MainService.class); Intent intent = new Intent(context, MainService.class);
// API 30 属于 Android 10+,按前台服务逻辑处理 context.startForegroundService(intent);
if (Build.VERSION.SDK_INT >= ANDROID_10_API && !AppForegroundUtils.isAppForeground(context)) {
LogUtils.d(TAG, "startMainService: 应用后台,使用 startForegroundService 启动");
context.startForegroundService(intent);
} else {
context.startService(intent);
}
} }
public static void restartMainService(Context context) { public static void restartMainService(Context context) {
@@ -194,6 +184,7 @@ public class MainService extends Service {
MainServiceBean bean = new MainServiceBean(); MainServiceBean bean = new MainServiceBean();
bean.setIsEnable(true); bean.setIsEnable(true);
MainServiceBean.saveBean(context, bean); MainServiceBean.saveBean(context, bean);
stopMainService(context);
startMainService(context); startMainService(context);
} }
@@ -213,52 +204,6 @@ public class MainService extends Service {
} }
} }
/**
* 服务启动前置校验
*/
private boolean canStartService(Class<?> serviceClass) {
// 服务未启用/已运行 → 禁止启动
if (mMainServiceBean == null || !mMainServiceBean.isEnable()) {
LogUtils.w(TAG, "canStartService: 主服务未启用,禁止启动 " + serviceClass.getSimpleName());
return false;
}
if (isServiceRunning(serviceClass)) {
LogUtils.d(TAG, "canStartService: " + serviceClass.getSimpleName() + " 已运行,跳过启动");
return false;
}
// Android 12+ 应用后台 → 禁止启动非核心服务
if (Build.VERSION.SDK_INT >= ANDROID_12_API && !AppForegroundUtils.isAppForeground(this)) {
LogUtils.w(TAG, "canStartService: Android 12+ 应用后台,禁止启动 " + serviceClass.getSimpleName());
return false;
}
return true;
}
/**
* 延迟启动非核心服务
*/
private void delayStartNonCoreService(final Class<?> serviceClass) {
if (!canStartService(serviceClass)) {
return;
}
mDelayHandler.postDelayed(new Runnable() {
@Override
public void run() {
Intent intent = new Intent(MainService.this, serviceClass);
try {
if (Build.VERSION.SDK_INT >= ANDROID_10_API && !AppForegroundUtils.isAppForeground(MainService.this)) {
startForegroundService(intent);
} else {
startService(intent);
}
LogUtils.d(TAG, "delayStartNonCoreService: " + serviceClass.getSimpleName() + " 启动成功");
} catch (Exception e) {
LogUtils.e(TAG, "delayStartNonCoreService: " + serviceClass.getSimpleName() + " 启动失败", e);
}
}
}, DELAY_NON_CORE_SERVICE_MS);
}
/** /**
* 创建前台服务通知 * 创建前台服务通知
*/ */
@@ -299,25 +244,6 @@ public class MainService extends Service {
return builder.build(); return builder.build();
} }
/**
* 启动电话监听服务原有CallListenerService
*/
private void startPhoneCallListener() {
delayStartNonCoreService(CallListenerService.class);
}
/**
* 新增启动通话筛选服务MyCallScreeningServiceAPI 10+ 生效)
*/
private void startCallScreeningService() {
// 通话筛选服务仅 Android 10+API 29+)支持,低版本跳过
if (Build.VERSION.SDK_INT >= ANDROID_10_API) {
delayStartNonCoreService(MyCallScreeningService.class);
} else {
LogUtils.d(TAG, "startCallScreeningService: 系统版本低于 Android 10不支持通话筛选服务跳过启动");
}
}
/** /**
* 检查服务是否正在运行 * 检查服务是否正在运行
*/ */
@@ -349,15 +275,17 @@ public class MainService extends Service {
sMainServiceInstance = this; sMainServiceInstance = this;
mIsServiceRunning = false; mIsServiceRunning = false;
// 仅初始化核心组件 // 仅初始化核心组件(移除 mDelayHandler 初始化,无需延迟启动)
mMainServiceBean = MainServiceBean.loadBean(this, MainServiceBean.class); mMainServiceBean = MainServiceBean.loadBean(this, MainServiceBean.class);
mMyServiceConnection = new MyServiceConnection(); mMyServiceConnection = new MyServiceConnection();
mMainServiceHandler = new MainServiceHandler(this); mMainServiceHandler = new MainServiceHandler(this);
mDelayHandler = new Handler(Looper.getMainLooper());
// 初始化铃声音量检查定时器 // 初始化铃声音量检查定时器
initVolumeCheckTimer(); initVolumeCheckTimer();
// 启动服务
mainService();
LogUtils.d(TAG, "===== onCreate: 主服务创建完成(仅初始化组件) ====="); LogUtils.d(TAG, "===== onCreate: 主服务创建完成(仅初始化组件) =====");
} }
@@ -380,19 +308,10 @@ public class MainService extends Service {
super.onDestroy(); super.onDestroy();
LogUtils.d(TAG, "===== onDestroy: 主服务开始销毁(强制清理所有资源) ====="); LogUtils.d(TAG, "===== onDestroy: 主服务开始销毁(强制清理所有资源) =====");
// 1. 强制标记服务为未运行 // 2. 释放定时器资源
mIsServiceRunning = false;
// 2. 释放延迟 Handler 资源
if (mDelayHandler != null) {
mDelayHandler.removeCallbacksAndMessages(null);
mDelayHandler = null;
}
// 3. 释放定时器资源
cancelVolumeCheckTimer(); cancelVolumeCheckTimer();
// 4. 强制解除守护服务绑定 // 3. 强制解除守护服务绑定
if (mIsBound && mMyServiceConnection != null) { if (mIsBound && mMyServiceConnection != null) {
try { try {
unbindService(mMyServiceConnection); unbindService(mMyServiceConnection);
@@ -404,97 +323,101 @@ public class MainService extends Service {
mMyServiceConnection = null; mMyServiceConnection = null;
} }
// 5. 强制注销广播接收器 // 4. 强制注销广播接收器
if (mMainReceiver != null) { if (mMainReceiver != null) {
mMainReceiver.unregisterAction(this); mMainReceiver.unregisterAction(this);
mMainReceiver = null; mMainReceiver = null;
LogUtils.d(TAG, "onDestroy: 广播接收器已注销"); LogUtils.d(TAG, "onDestroy: 广播接收器已注销");
} }
// 6. 强制停止主业务线程 // 5. 强制停止主业务线程
if (mMainServiceThread != null) { // if (mMainServiceThread != null) {
mMainServiceThread.setIsExit(true); // mMainServiceThread.setIsExit(true);
mMainServiceThread = null; // mMainServiceThread = null;
} // }
// 7. 强制停止所有子服务(新增:停止通话筛选服务 MyCallScreeningService // 6. 强制停止所有子服务
stopService(new Intent(this, AssistantService.class)); stopService(new Intent(this, AssistantService.class));
stopService(new Intent(this, CallListenerService.class)); stopService(new Intent(this, CallListenerService.class));
stopService(new Intent(this, MyCallScreeningService.class)); stopService(new Intent(this, MyCallScreeningService.class));
LogUtils.d(TAG, "onDestroy: 所有子服务已强制停止"); LogUtils.d(TAG, "onDestroy: 所有子服务已强制停止");
// 8. 清空静态实例,避免内存泄漏(不改动 Bean 配置) // 7. 清空静态实例,避免内存泄漏(不改动 Bean 配置)
sMainServiceInstance = null; sMainServiceInstance = null;
sTomCatInstance = null; sTomCatInstance = null;
mMainServiceHandler = null; mMainServiceHandler = null;
mAssistantService = null; mAssistantService = null;
mMainServiceBean = null; mMainServiceBean = null;
// 标记服务为未运行
mIsServiceRunning = false;
LogUtils.d(TAG, "===== onDestroy: 主服务销毁完成资源全清理Bean 配置无改动) ====="); LogUtils.d(TAG, "===== onDestroy: 主服务销毁完成资源全清理Bean 配置无改动) =====");
} }
// ====================== 核心业务逻辑:优先判断运行状态 → 再校验配置启用状态 ====================== // ====================== 核心业务逻辑:非核心服务直接启动,无延迟 ======================
private void mainService() { private synchronized void mainService() {
LogUtils.d(TAG, "mainService: 执行核心业务逻辑"); LogUtils.d(TAG, "mainService: 执行核心业务逻辑");
// 第一步:优先判断服务是否已运行,已运行则直接退出(不做任何动作) // 第一步:优先判断服务是否已运行,已运行则直接退出(不做任何动作)
if (mIsServiceRunning) { if (!mIsServiceRunning) {
LogUtils.d(TAG, "mainService: 服务已处于运行状态,直接退出"); mIsServiceRunning = true;
return; // 第二步:加载最新配置,校验服务是否启用(未启用则退出)
} mMainServiceBean = MainServiceBean.loadBean(this, MainServiceBean.class);
if (mMainServiceBean == null && mMainServiceBean.isEnable()) {
// 第三步:配置启用且服务未运行,执行启动流程
LogUtils.i(TAG, "mainService: 服务未运行且配置已启用,开始启动核心流程");
// 第二步:加载最新配置,校验服务是否启用(未启用则退出 // 1. 启动前台服务(优先执行,避免超时
mMainServiceBean = MainServiceBean.loadBean(this, MainServiceBean.class); try {
if (mMainServiceBean == null || !mMainServiceBean.isEnable()) { Notification foregroundNotification = createForegroundNotification();
LogUtils.w(TAG, "mainService: 服务配置未启用/配置加载失败,退出启动流程"); startForeground(FOREGROUND_NOTIFICATION_ID, foregroundNotification, FOREGROUND_SERVICE_TYPE_DATA_SYNC);
return; //startForeground(FOREGROUND_NOTIFICATION_ID, foregroundNotification);
} } catch (IllegalArgumentException e) {
LogUtils.e(TAG, "mainService: 启动前台服务失败,类型不匹配", e);
mIsServiceRunning = false;
stopSelf();
return;
} catch (Exception e) {
LogUtils.e(TAG, "mainService: 启动前台服务异常", e);
mIsServiceRunning = false;
stopSelf();
return;
}
// 第三步:配置启用且服务未运行,执行启动流程 // 2. 绑定守护服务
mIsServiceRunning = true; wakeupAndBindAssistant();
LogUtils.i(TAG, "mainService: 服务未运行且配置已启用,开始启动核心流程");
// 1. 启动前台服务(优先执行,避免超时) // 3. 初始化业务组件
try { initTomCat();
Notification foregroundNotification = createForegroundNotification(); initMainReceiver();
if (Build.VERSION.SDK_INT >= ANDROID_10_API) { Rules.getInstance(this).loadRules();
startForeground(FOREGROUND_NOTIFICATION_ID, foregroundNotification, FOREGROUND_SERVICE_TYPE_DATA_SYNC); LogUtils.d(TAG, "mainService: 黑白名单规则已加载");
LogUtils.d(TAG, "mainService: 主服务已启动为前台服务dataSync 类型API30+");
} else {
startForeground(FOREGROUND_NOTIFICATION_ID, foregroundNotification);
LogUtils.d(TAG, "mainService: 主服务已启动为前台服务(低版本兼容)");
}
} catch (IllegalArgumentException e) {
LogUtils.e(TAG, "mainService: 启动前台服务失败,类型不匹配", e);
mIsServiceRunning = false;
stopSelf();
return;
} catch (Exception e) {
LogUtils.e(TAG, "mainService: 启动前台服务异常", e);
mIsServiceRunning = false;
stopSelf();
return;
}
// 2. 绑定守护服务 // 4. 直接启动电话监听服务(移除延迟,校验通过则立即启动)
wakeupAndBindAssistant(); Intent callListenerIntent = new Intent(this, CallListenerService.class);
try {
startForegroundService(callListenerIntent);
//startService(callListenerIntent);
} catch (Exception e) {
LogUtils.e(TAG, "mainService: CallListenerService 启动失败", e);
}
// 3. 初始化业务组件
initTomCat();
initMainReceiver();
Rules.getInstance(this).loadRules();
LogUtils.d(TAG, "mainService: 黑白名单规则已加载");
// 4. 启动电话监听服务原有CallListenerService // 5. 直接启动通话筛选服务API 10+ 生效,无延迟
startPhoneCallListener(); Intent screeningIntent = new Intent(this, MyCallScreeningService.class);
try {
startForegroundService(screeningIntent);
//startService(screeningIntent);
} catch (Exception e) {
LogUtils.e(TAG, "mainService: MyCallScreeningService 启动失败", e);
}
// 5. 新增启动通话筛选服务MyCallScreeningServiceAPI 10+ 生效) // 6. 启动主业务线程
startCallScreeningService(); // mMainServiceThread = MainServiceThread.getInstance(this, mMainServiceHandler);
// mMainServiceThread.start();
// 6. 启动主业务线程 LogUtils.i(TAG, "mainService: 核心流程启动完成,主服务正常运行");
mMainServiceThread = MainServiceThread.getInstance(this, mMainServiceHandler); }
mMainServiceThread.start(); }
LogUtils.i(TAG, "mainService: 核心流程启动完成,主服务正常运行");
} }
private void wakeupAndBindAssistant() { private void wakeupAndBindAssistant() {
@@ -503,7 +426,7 @@ public class MainService extends Service {
return; return;
} }
Intent intent = new Intent(this, AssistantService.class); Intent intent = new Intent(this, AssistantService.class);
if (Build.VERSION.SDK_INT >= ANDROID_10_API && !AppForegroundUtils.isAppForeground(this)) { if (Build.VERSION.SDK_INT >= ANDROID_10_API) {
LogUtils.d(TAG, "wakeupAndBindAssistant: 应用后台,启动 AssistantService 为前台服务"); LogUtils.d(TAG, "wakeupAndBindAssistant: 应用后台,启动 AssistantService 为前台服务");
startForegroundService(intent); startForegroundService(intent);
} else { } else {

View File

@@ -1,52 +0,0 @@
package cc.winboll.studio.contacts.utils;
import android.app.ActivityManager;
import android.content.Context;
import android.os.Build;
import android.text.TextUtils;
import java.util.List;
/**
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
* @Date 2025/12/12 15:20
* @Describe AppForegroundUtils 应用前后台状态判断工具类(兼容 Java 7
*/
public class AppForegroundUtils {
public static final String TAG = "AppForegroundUtils";
/**
* 判断应用是否处于前台状态
* @param context 上下文
* @return true-前台false-后台/无上下文
*/
public static boolean isAppForeground(Context context) {
if (context == null) {
return false;
}
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
if (activityManager == null) {
return false;
}
String packageName = context.getPackageName();
List<ActivityManager.RunningAppProcessInfo> processList = activityManager.getRunningAppProcesses();
if (processList == null || processList.isEmpty()) {
return false;
}
// 遍历进程列表,匹配当前应用包名并判断前台状态
for (ActivityManager.RunningAppProcessInfo processInfo : processList) {
// Java 7 兼容:避免 Lambda使用普通循环 + TextUtils 判等
if (!TextUtils.isEmpty(processInfo.processName)
&& TextUtils.equals(processInfo.processName, packageName)
&& processInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
return true;
}
}
return false;
}
}