package com.safeluck.floatwindow;
|
|
import android.app.Service;
|
import android.content.ComponentName;
|
import android.content.Context;
|
import android.content.ServiceConnection;
|
import android.content.Intent;
|
import android.os.IBinder;
|
import android.os.RemoteCallbackList;
|
import android.os.RemoteException;
|
|
import com.safeluck.floatwindow.manager.AndroidCameraRecordManager;
|
import com.safeluck.floatwindow.manager.UsbCameraPushManager;
|
import com.safeluck.floatwindow.manager.UsbCameraRecordManager;
|
|
import timber.log.Timber;
|
|
/**
|
* 悬浮窗服务,用于处理推流和录像
|
* 根据MediaArgu参数选择使用不同的管理器:
|
* - USB摄像头推流:UsbCameraPushManager
|
* - USB摄像头录像:UsbCameraRecordManager
|
* - Android相机录像:AndroidCameraRecordManager
|
*/
|
public class FloatingService extends Service {
|
private static final String TAG = "FloatingService";
|
|
private Context context;
|
|
// 管理器实例
|
private UsbCameraPushManager usbCameraPushManager;
|
private UsbCameraRecordManager usbCameraRecordManager;
|
private AndroidCameraRecordManager androidCameraRecordManager;
|
|
// 当前使用的管理器类型
|
private ManagerType currentManagerType = ManagerType.NONE;
|
|
// P2 跨进程服务(用于 usbCameraId == 2)
|
private IMediaAidlInterface p2Service;
|
private boolean p2Bound = false;
|
private MediaArgu pendingP2StartMedia;
|
|
private final IMyCallback p2Callback = new IMyCallback.Stub() {
|
@Override
|
public void onResult(ResponseVO re) throws RemoteException {
|
// 将 P2 进程回调转发给客户端
|
notifyCallback(re);
|
}
|
};
|
|
private final ServiceConnection p2Connection = new ServiceConnection() {
|
@Override
|
public void onServiceConnected(ComponentName name, IBinder service) {
|
p2Service = IMediaAidlInterface.Stub.asInterface(service);
|
p2Bound = true;
|
Timber.d("P2UsbCameraVideoService connected");
|
try {
|
p2Service.registerCallback(p2Callback);
|
} catch (RemoteException e) {
|
Timber.e(e, "Failed to register p2Callback");
|
}
|
|
// 如果有 pending startMedia,连接后立刻执行
|
if (pendingP2StartMedia != null) {
|
try {
|
p2Service.startMedia(pendingP2StartMedia);
|
pendingP2StartMedia = null;
|
} catch (RemoteException e) {
|
Timber.e(e, "Failed to startMedia on P2 service");
|
}
|
}
|
}
|
|
@Override
|
public void onServiceDisconnected(ComponentName name) {
|
Timber.w("P2UsbCameraVideoService disconnected");
|
p2Bound = false;
|
p2Service = null;
|
}
|
};
|
|
/**
|
* 管理器类型枚举
|
*/
|
private enum ManagerType {
|
NONE,
|
USB_PUSH,
|
USB_RECORD,
|
ANDROID_RECORD
|
}
|
|
// AIDL Binder
|
private final IMediaAidlInterface.Stub mBinder = new IMediaAidlInterface.Stub() {
|
@Override
|
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
|
double aDouble, String aString) throws RemoteException {
|
Timber.d("basicTypes called: anInt=%d, aLong=%d, aBoolean=%s, aFloat=%f, aDouble=%f, aString=%s",
|
anInt, aLong, aBoolean, aFloat, aDouble, aString);
|
}
|
|
@Override
|
public void startMedia(MediaArgu media) throws RemoteException {
|
Timber.d("startMedia called via AIDL");
|
FloatingService.this.startMedia(media);
|
}
|
|
@Override
|
public void stopMedia(MediaArgu media) throws RemoteException {
|
Timber.d("stopMedia called via AIDL");
|
FloatingService.this.stopMedia(media);
|
}
|
|
@Override
|
public void registerCallback(IMyCallback cb) throws RemoteException {
|
if (cb != null) {
|
mCallbacks.register(cb);
|
Timber.d("Callback registered");
|
}
|
}
|
|
@Override
|
public void unregisterCallback(IMyCallback cb) throws RemoteException {
|
if (cb != null) {
|
mCallbacks.unregister(cb);
|
Timber.d("Callback unregistered");
|
}
|
}
|
};
|
|
// 回调列表,使用 RemoteCallbackList 管理跨进程回调
|
private final RemoteCallbackList<IMyCallback> mCallbacks = new RemoteCallbackList<>();
|
|
@Override
|
public void onCreate() {
|
super.onCreate();
|
context = this;
|
|
// 初始化管理器
|
usbCameraPushManager = new UsbCameraPushManager(context);
|
usbCameraPushManager.setCallback(new UsbCameraPushManager.PushCallback() {
|
@Override
|
public void onResult(ResponseVO response) {
|
notifyCallback(response);
|
}
|
});
|
|
usbCameraRecordManager = new UsbCameraRecordManager(context);
|
usbCameraRecordManager.setCallback(new UsbCameraRecordManager.RecordCallback() {
|
@Override
|
public void onResult(ResponseVO response) {
|
notifyCallback(response);
|
}
|
});
|
|
androidCameraRecordManager = new AndroidCameraRecordManager(context);
|
androidCameraRecordManager.setCallback(new AndroidCameraRecordManager.RecordCallback() {
|
@Override
|
public void onResult(ResponseVO response) {
|
notifyCallback(response);
|
}
|
});
|
|
Timber.d("FloatingService onCreate");
|
}
|
|
private void ensureP2Bound() {
|
if (p2Bound) return;
|
Intent intent = new Intent(this, P2UsbCameraVideoService.class);
|
try {
|
bindService(intent, p2Connection, Context.BIND_AUTO_CREATE);
|
} catch (Exception e) {
|
Timber.e(e, "bindService P2UsbCameraVideoService failed");
|
}
|
}
|
|
@Override
|
public int onStartCommand(Intent intent, int flags, int startId) {
|
return START_STICKY;
|
}
|
|
/**
|
* 启动媒体(通过 AIDL 调用)
|
*/
|
private void startMedia(MediaArgu media) {
|
if (media == null) {
|
Timber.w("startMedia: media is null");
|
notifyCallback(1, -1, "MediaArgu is null");
|
return;
|
}
|
|
// usbCameraId == 2:走 P2 跨进程服务,支持两路 USB 同时工作(本 Service 不再跟踪其状态)
|
if (media.isUsedOutCamera() && media.getUsbCameraId() == 2) {
|
ensureP2Bound();
|
if (p2Service != null) {
|
try {
|
p2Service.startMedia(media);
|
} catch (RemoteException e) {
|
Timber.e(e, "startMedia forward to P2 failed");
|
notifyCallback(1, -3, "启动P2服务失败: " + e.getMessage());
|
}
|
} else {
|
// 等待连接完成后执行
|
pendingP2StartMedia = media;
|
}
|
return;
|
}
|
|
// 先停止当前正在运行的管理器
|
stopCurrentManager();
|
|
// 根据参数选择管理器
|
if (media.isPush()) {
|
// 推流
|
if (media.isUsedOutCamera()) {
|
// USB摄像头推流
|
Timber.i("启动USB摄像头推流");
|
currentManagerType = ManagerType.USB_PUSH;
|
usbCameraPushManager.startPush(media);
|
} else {
|
Timber.w("推流仅支持USB摄像头");
|
notifyCallback(1, -4, "推流仅支持USB摄像头");
|
}
|
} else {
|
// 录像
|
if (media.isUsedOutCamera()) {
|
// USB摄像头录像
|
Timber.i("启动USB摄像头录像");
|
currentManagerType = ManagerType.USB_RECORD;
|
usbCameraRecordManager.startRecord(media);
|
} else {
|
// Android相机录像
|
Timber.i("启动Android相机录像");
|
currentManagerType = ManagerType.ANDROID_RECORD;
|
androidCameraRecordManager.startRecord(media);
|
}
|
}
|
}
|
|
/**
|
* 停止当前本地管理器(仅管理本进程中的 USB(P1)/Android 录像或推流)
|
*/
|
private void stopCurrentManager() {
|
switch (currentManagerType) {
|
case USB_PUSH:
|
if (usbCameraPushManager != null) {
|
usbCameraPushManager.stopPush();
|
}
|
break;
|
case USB_RECORD:
|
if (usbCameraRecordManager != null) {
|
usbCameraRecordManager.stopRecord();
|
}
|
break;
|
case ANDROID_RECORD:
|
if (androidCameraRecordManager != null) {
|
androidCameraRecordManager.stopRecord();
|
}
|
break;
|
case NONE:
|
break;
|
}
|
currentManagerType = ManagerType.NONE;
|
}
|
|
/**
|
* 停止媒体(通过 AIDL 调用,带 MediaArgu,用于区分 P1/P2)
|
*/
|
private void stopMedia(MediaArgu media) {
|
Timber.d("stopMedia called, media=%s", media);
|
if (media != null && media.isUsedOutCamera() && media.getUsbCameraId() == 2) {
|
// P2 USB 摄像头由 P2UsbCameraVideoService 管理
|
ensureP2Bound();
|
if (p2Service != null) {
|
try {
|
p2Service.stopMedia(media);
|
} catch (RemoteException e) {
|
Timber.e(e, "stopMedia forward to P2 failed");
|
}
|
} else {
|
pendingP2StartMedia = null;
|
}
|
} else {
|
// 其他情况(包括 P1 USB 与 Android 相机)由本 Service 自己管理
|
stopCurrentManager();
|
}
|
}
|
|
@Override
|
public IBinder onBind(Intent intent) {
|
Timber.d("onBind called");
|
return mBinder;
|
}
|
|
@Override
|
public void onDestroy() {
|
super.onDestroy();
|
stopCurrentManager();
|
mCallbacks.kill();
|
if (p2Bound) {
|
try {
|
if (p2Service != null) {
|
p2Service.unregisterCallback(p2Callback);
|
}
|
} catch (RemoteException e) {
|
Timber.e(e, "Failed to unregister p2Callback");
|
}
|
try {
|
unbindService(p2Connection);
|
} catch (Exception e) {
|
Timber.w(e, "unbindService P2 failed");
|
}
|
p2Bound = false;
|
p2Service = null;
|
}
|
Timber.d("FloatingService onDestroy");
|
}
|
|
/**
|
* 通知回调:统一使用 ResponseVO
|
* @param response ResponseVO 对象,包含 type、errCode、message
|
*/
|
private void notifyCallback(ResponseVO response) {
|
if (response == null) {
|
return;
|
}
|
int count = mCallbacks.beginBroadcast();
|
for (int i = 0; i < count; i++) {
|
try {
|
mCallbacks.getBroadcastItem(i).onResult(response);
|
} catch (RemoteException e) {
|
Timber.e(e, "Error notifying callback");
|
}
|
}
|
mCallbacks.finishBroadcast();
|
}
|
|
/**
|
* 创建并发送回调
|
* @param type 类型:0-录像 1-推流 2-文件名类型等
|
* @param errCode 错误码:0-成功,其他为错误码
|
* @param message 消息内容
|
*/
|
private void notifyCallback(int type, int errCode, String message) {
|
ResponseVO response = new ResponseVO();
|
response.setType(type);
|
response.setErrCode(errCode);
|
response.setMessage(message);
|
notifyCallback(response);
|
}
|
}
|