TaskManager 사양 5 · 외부 연동과 기존 시스템 호환
NoneAAR, IoT/MQTT, 선택적 Planner와 기존 direct 실행 경로의 점진 전환 방식을 정의한다.
이 장의 결론: 기존 채널과 응답 계약은 유지하고, 관리가 필요한 요청만 명시적으로 Task 또는 Workflow 경로에 올린다.
18. TaskManagerClient AAR
TaskManager Core는 호출 채널에 종속되지 않는다. 시스템 앱은 typed AAR, IoT는 MQTT,
Cloud·On-device bridge는 Bundle/JSON 변환을 사용하지만, 최종 요청은 같은
submitTask 또는 submitWorkflow 계약으로 수렴해야 한다.
AAR은 시스템 앱이 문자열과 Bundle을 직접 조립하지 않도록 제공하는 product-level facade다. 다음 원칙을 지킨다.
TaskSubmitRequest와TaskWorkflowRequestbuilder가 필수 필드를 검증한다.TaskManagerClientCallback.onTaskEvent가 공통 event envelope를 전달한다.- AAR은 TaskManager runtime을 포함하지 않고 DeviceAgent Binder 경계만 감싼다.
- 필드, 기본값과 callback 의미는 Core 계약과 동일해야 한다.
typed API, builder 예시와 배포 산출물은 TaskManagerClient AAR API Specification을 기준으로 한다.
19. IoT/MQTT와 Task Monitor 계약
IoT 요청은 transport metadata와 Task metadata를 분리한다. deviceId, topic,
command ack는 MQTT 계층이 소유하고, requestId, traceId, taskMethod, queue,
policy와 payload는 Task 계약으로 전달한다.
- 모의 실행과 실제 dispatch를 UI에서 구분한다.
- HTTP·MQTT 인증을 우회하는 payload 필드를 허용하지 않는다.
- 명령 ack와 물리 완료 event를 같은 상태로 취급하지 않는다.
- source가 달라도 validator, admission, executor와 completion gate를 우회하지 않는다.
전체 payload와 운영 속성은 MQTT Task Contract을 기준으로 한다.
20. 선택 연동: Cloud Planner와 A2A
20.0 선택적 상위 연동의 경계
기본 경로에는 Planner가 필요하지 않다. 앱·PUI·IoT·예약과 사전 정의 Workflow는 상위 의미 추론 없이 같은 Core를 사용한다. Planner가 연결되더라도 실행 허용, 자원 정책, 물리 완료 증거와 최종 상태 판정은 DeviceAgent TaskManager와 각 기기 도메인이 소유한다. 상위 Planner가 없어도 기기 내부에서 안전한 생명주기를 유지한다.
상위 Planner는 목표를 해석하고 capability와 단계 의존성을 구성한다. DeviceAgent는 계획을 다시 해석하지 않고 실행 가능성, 현재 상태, 안전 정책과 실제 완료 증거를 판정한다.
goal or product scenario
-> structured task/workflow
-> DeviceAgent admission and execution
-> progress/terminal evidence
-> continue locally or request bounded replanning
정상 진행마다 Planner를 호출하지 않는다. BLOCKED, FAILED, TIMEOUT 또는 명시적
사용자 변경처럼 상위 판단이 필요한 terminal event만 재계획 후보가 된다.
requestId, traceId, cloudWorkflowId, cloudStepId는 왕복 동안 보존한다.
Cloud·On-device·DeviceAgent 폐루프와 callback 계약은 Voice TaskManager Closed Loop, 기기 상태 입력은 Device Context Planning Spec을 기준으로 한다.
제5부 · 운영·보안·검증 — 21~28장은 관측성, 영속성, 보안, 실패 UX, 시험, 변경과 참조 규격을 정리한다.
30. 기존 시스템 호환성과 점진적 전환 사양
TaskManager 도입은 기존 DeviceAgent 기능을 다른 구현으로 교체하는 작업이 아니다. 기존 PUI, IoT, 시스템 앱, Schedule과 Voice/LLM 호출이 사용하던 MainApi와 기기 도메인 구현을 유지하고, 관리가 필요한 요청만 TaskManager 생명주기에 편입하는 점진적 확장이다.
구조도의 두 실행 경로는 모두 같은 기존 기기 도메인에 도달한다. 차이는 기존 호출이 MainApi.executeMethod()와 executeMethodInternal()을 유지하는 반면, 관리 호출은 명시적 Task API 또는 forceTaskManager=true를 통해 실행 허용, 대기열과 생명주기 관리를 먼저 거친다는 점이다.
두 경로는 마지막에 같은 Movement, Cleaning, Schedule, Security, LLM/TTS와 Update 구현으로 합류한다. TaskManager는 기존 기능 로직을 복제하지 않고 실행 순서, 상태, 완료 증거와 취소 관리를 덧붙인다.
| 용어 | 한 줄 설명 |
|---|---|
| 기존 시스템 | TaskManager 도입 전부터 사용하던 MainApi, 기기 도메인 기능, 콜백과 호출 방식 |
| 호출자 | PUI, IoT, 앱, Schedule, Voice/Cloud처럼 DeviceAgent에 명령을 보내는 주체 |
| 관리 경로 | 요청을 Task로 등록해 순서, 상태, 완료와 취소를 TaskManager가 관리하는 실행 경로 |
| 선택 적용 | 모든 요청을 한꺼번에 바꾸지 않고 지정한 호출과 기능에만 TaskManager를 적용하는 방식 |
| 반환 계약 | 호출자가 언제 어떤 결과 필드를 받는지 정한 약속 |
| 점진적 전환 | 기존 기능을 유지한 채 검증된 기능부터 관리 경로로 옮기는 과정 |
| 원복 | 문제가 생겼을 때 해당 호출을 검증된 기존 경로로 되돌리는 절차 |
30.1 호환성 목표와 비목표
요약: 기존 기능을 유지하면서 필요한 경로부터 TaskManager 관리 대상으로 전환한다.
| 구분 | 목표 | 비목표 |
|---|---|---|
| 기존 호출자 | 수정하지 않은 호출자가 기존 경로와 반환 방식으로 계속 동작 | 첫 출시부터 모든 호출을 강제로 TaskManager 경로로 전환 |
| 기기 도메인 기능 | 같은 메서드가 같은 기기 도메인과 제품 정책을 사용 | 도메인 정책을 TaskManager에 복사해 우회 |
| 관리 경로 호출자 | 필요한 기능부터 Task·Workflow 생명주기를 선택 적용 | forceTaskManager를 붙여도 결과 형식과 시점이 완전히 같다고 가정 |
| 콜백 | 기존 콜백을 유지하며 Task 이벤트를 추가 발행 | 기존 콜백 수신자를 한 번에 제거 |
| 스케줄 | 제품 반복 스케줄과 TaskManager 지연 실행을 병존 | 기존 스케줄 DB를 자동 변환하거나 의미를 재해석 |
| SDK | 기존 원시 API와 타입 기반 AAR을 함께 운영 | 모든 시스템 앱의 동시 전환 |
| 원복 | 호출자 또는 시스템 속성 단위로 기존 경로 복귀 | 데이터와 앱을 되돌릴 수 없는 일괄 전환 |
호환성은 “컴파일된다”가 아니라 다음 네 수준으로 판정한다.
- 호출 호환성: 기존 메서드와 필드를 계속 보낼 수 있다.
- 동작 호환성: 같은 기기 도메인이 같은 물리 동작과 안전 정책을 수행한다.
- 관찰 호환성: 기존 콜백과 UI·IoT 상태 갱신이 유지된다.
- 운영 호환성: 배포, 재부팅 복원, 모니터링과 원복이 가능하다.
30.2 이중 실행 경로
요약: 기존 요청은 종전 경로로, 관리 대상 요청은 TaskManager 경로로 보내며 실제 기능은 같은 기기 도메인이 수행한다.
MainApi.executeMethod()의 호환 분기는 다음 순서를 따른다.
구조도의 분기 순서는 명시적 제어 API, forceTaskManager=true인 기존 메서드, 그 밖의 기존 메서드 순이다. 각 요청은 각각 TaskManager.handleControlMethod(), TaskManager.executeLegacy(), executeMethodInternal()로 전달된다.
소스 표현 빠른 해석
| 소스 표현 | 쉬운 한 줄 설명 | 실제 영향 |
|---|---|---|
MainApi.executeMethod() |
DeviceAgent로 들어온 명령이 가장 먼저 거치는 공통 접수 창구 | 여기서 기존 경로와 TaskManager 경로를 구분한다. |
TaskManager.handleControlMethod() |
Task 등록, 조회, 취소처럼 TaskManager 자체 명령을 처리하는 입구 | 일반 기기 기능과 Task 관리 명령을 섞지 않는다. |
executeMethodInternal() |
TaskManager 도입 전부터 사용하던 기존 기능 실행 경로 | 기존 호출자가 아무 설정을 바꾸지 않으면 이 경로를 계속 사용한다. |
forceTaskManager=true |
이 요청만 기존 즉시 실행 대신 Task로 관리하라는 표시 | 대기열, 상태 추적, 완료 확인과 취소가 적용될 수 있다. |
TaskManager.executeLegacy() |
기존 형식의 명령을 TaskManager가 이해할 수 있는 Task로 감싸는 연결부 | 호출 형식을 한 번에 바꾸지 않고도 점진적으로 관리 경로를 시험할 수 있다. |
runningManagedTask |
이미 TaskManager가 실행 중인 명령인지 기억하는 내부 재진입 방지 표식 | 기존 MainApi를 다시 호출해도 같은 명령이 Task로 중복 등록되는 것을 막는다. |
TaskPolicy |
기능별 실행 방식, 대기열, 우선순위와 시간 제한을 정한 실행 규칙 | 같은 메서드라도 어떤 순서와 방식으로 처리할지가 결정된다. |
TaskRecord |
접수된 작업의 ID, 상태, 결과와 실패 이유를 보관하는 실행 기록 | 모니터링, 취소, 콜백 상관관계와 장애 분석의 기준이 된다. |
소스 심볼은 현재 구현 위치를 설명하기 위한 근거다. 외부 호출자가 의존해야 하는 공개 계약은 제어 API, AAR 스키마와 콜백 계약이다. 내부 클래스 이름은 리팩터링 과정에서 바뀔 수 있다.
30.2.1 외부 MainApi 기존 경로
forceTaskManager가 없으면 기존executeMethodInternal()을 그대로 사용한다.- 기존 호출자의 즉시 반환, 오류 전달과 콜백 계약은 TaskManager 대기열 때문에 바뀌지 않는다.
- TaskManager 전체 활성화 상태와 무관하게 기존 원시 경로는 유지한다.
30.2.2 외부 MainApi 관리 경로
- 명시적
submitTask/submitWorkflow또는forceTaskManager=true요청만 관리 경로로 들어간다. TaskPolicy가DIRECT이면 기존 함수를 직접 호출하되 Task 결과를 기록한다.ASYNC와QUEUED_WAIT이면TaskRecord와 대기열을 만들고 기존 도메인 실행기를 호출한다.- 실행기가 다시
MainApi를 사용할 때runningManagedTask재진입 보호 장치가 대기열 중복 등록을 막는다.
30.2.3 내부 AppCmd 모듈 경로
DeviceAgent 내부 AppCmd에는 외부 MainApi와 다른 호환 분기가 있다.
| 조건 | 현재 동작 | 쉬운 설명 |
|---|---|---|
| Agent가 AMR 또는 UPDATE가 아님 | 기존 모듈 직접 전달 | 현재 자동 관리 대상은 두 내부 모듈로 제한된다. |
delayMillis > 0 |
기존 지연 전달 | 지연 메시지는 기존 Handler의 시간 의미를 보존한다. |
| 메서드 없음 | 기존 직접 전달 | Task로 식별할 수 없으므로 종전 경로를 유지한다. |
| 조회·센서·결과·콜백 | TaskIngressClassifier가 비Task로 분류해 직접 전달 |
상태 전달이나 콜백을 새 기기 동작 Task로 만들지 않는다. |
명령이지만 정책이 DIRECT |
직접 전달 | 빠른 조회와 즉시 결과의 반환 의미를 유지한다. |
명령이며 DIRECT가 아님 |
executeInternalModuleCommand() → executeLegacy() |
기존 모듈 명령을 대기열과 TaskRecord로 관리한다. |
| 이미 관리 대상 실행 내부 | runningManagedTask로 직접 전달 |
실행기가 기존 모듈을 호출할 때 Task가 중첩 생성되지 않는다. |
이 경로에서는 persist.sys.deviceagent.taskmanager.legacy_queue 기본값이 true이므로 조건을 만족하는 내부 명령이 자동으로 대기열에 들어갈 수 있다. 반면 외부 MainApi 원시 호출은 이 속성만으로 관리 경로에 들어가지 않으며 forceTaskManager=true가 필요하다.
30.2.4 TaskManagerClient AAR 경로
| AAR 호출 | 생성되는 요청 | 호환 의미 |
|---|---|---|
TaskSubmitRequest.toBundle() |
method=submitTask, forceTaskManager=true |
명시적으로 관리 대상 단일 Task 제출 |
TaskWorkflowRequest.toBundle() |
method=submitWorkflow, forceTaskManager=true |
명시적으로 관리 대상 Workflow 제출 |
| 조회·취소 API | TaskManager 제어 메서드 | 물리 기능이 아니라 Task 상태와 대기열을 제어 |
sendRaw(Bundle) |
호출자 Bundle을 복사해 그대로 전달 | 강제 실행 플래그를 자동으로 붙이지 않으므로 경로 선택은 호출자 책임 |
AAR은 모든 기존 호출을 자동 변환하는 프록시가 아니다. 타입 기반 요청 생성기는 관리 계약을 만들고, sendRaw()는 점진적 전환 중 아직 타입 기반 API가 없는 기존 계약을 보존한다.
forceTaskManager는 단순 로그 표시가 아니다. 요청을 Task 생명주기에 편입하고 반환 시점과 결과 구조를 바꿀 수 있는 호환성 경계 플래그다.
30.2.5 IoT·App·LLM Agent의 채널별 적용 방식
결론: MQTT 연결과 기존 명령을 일괄 교체하지 않는다. 기존 단일 명령은 그대로 두고, Task 생명주기가 필요한 요청만 선택적 관리 계약으로 확장한다.
TaskManager 도입으로 바뀌는 범위는 MQTT broker, topic, 인증, QoS와 재연결 같은 전송 계층 전체가 아니다. 바뀌는 부분은 명령 payload가 직접 실행인지 관리 대상 Task·Workflow인지 표현하는 응용 계약과, 관리 요청의 진행·완료를 돌려주는 결과 계약이다.
| 호출 경로 | 기존 호출 유지 | 관리가 필요한 단일 동작 | 여러 단계 Workflow | 현재 구현 경계 |
|---|---|---|---|---|
| 원격 App·IoT → MQTT | 기존 topic과 기능 payload를 그대로 전송 | 기존 request.method와 request.payload를 유지하고 선택적으로 request.task 추가 |
공통 submitWorkflow envelope와 subTasks[] 사용 |
단일 명령의 선택적 wrapping은 구현됨. MQTT JSON Workflow 배열 파서는 추가 필요 |
| 기기 내 System App | 기존 Binder·MainApi 원시 Bundle 유지 |
TaskManagerClient AAR의 TaskSubmitRequest 사용 |
AAR의 TaskWorkflowRequest 사용 |
타입 기반 생성기와 sendRaw() 호환 경로가 함께 존재 |
| LLM Agent·Cloud | 기존 원시 명령으로 추론 결과를 직접 실행하지 않음 | device_task_requests[]를 On-device Bridge가 submitTask로 변환 |
device_workflow_requests[]를 submitWorkflow로 변환 |
Cloud는 구조화된 요청을 생성하며, Bridge와 DeviceAgent 계약 버전 정합이 필요 |
| PUI·내부 이벤트 | 기존 도메인 직접 호출 유지 가능 | 제품이 상태 추적·취소를 요구하는 동작만 선택 적용 | 사전 정의된 제품 Workflow가 있을 때 사용 | 모든 버튼·센서·콜백을 Task로 올리면 안 됨 |
MQTT를 전부 바꾸지 않는 이유
MQTT의 기존 기능 payload는 이미 DeviceAgent 도메인 입력과 연결되어 있다. 이를 새 스키마로 일괄 치환하면 Topic parser, 응답 앱, 서버와 구버전 기기의 동시 배포 문제가 커진다. 현재 IotTaskManagerBridge는 다음 방식으로 호환성을 유지한다.
request.task 없음
-> 기존 parser가 만든 method와 payload 유지
-> 기존 MainApi 직접 실행
request.task 있음, managed 생략 또는 true
-> 기존 method를 taskMethod로 보존
-> method=submitTask, source=iot로 감쌈
-> payload는 그대로 두고 Task metadata만 추가
request.task.managed=false
-> 기존 직접 실행 유지
따라서 기존 MQTT 명령은 수정하지 않아도 계속 동작한다. TaskManager가 필요한 명령만 아래처럼 task 객체를 추가한다.
{
"correlationId": "move-bedroom-001",
"request": {
"method": "setMoveTo",
"payload": {
"positionId": "5"
},
"task": {
"managed": true,
"executionMode": "async",
"completionTarget": "movement.arrived"
}
}
}
priority, queueKey, timeoutMs, preemptPolicy까지 모든 값을 외부가 채울 필요는 없다. 생략된 값은 TaskPolicyRegistry의 메서드별 기본 정책으로 보강한다. 외부 입력값은 호출자 정책과 허용 범위로 다시 검증하며, 특히 EMERGENCY, 전체 대기열 교체와 임의 취소·보상 메서드는 그대로 신뢰하지 않는다.
복합 Workflow는 별도 계약으로 추가한다
여러 단계를 기존 단일 명령 Topic에 억지로 분산하면 서버 또는 App이 각 단계의 완료를 추측하고 다음 명령을 보내야 한다. 이 경우 TaskManager의 순서·취소·실패 전파 이점을 잃는다. 복합 실행은 하나의 submitWorkflow 요청으로 제출한다.
{
"correlationId": "clean-return-001",
"request": {
"method": "submitWorkflow",
"payload": {
"workflowName": "bedroom_clean_return",
"subTasks": [
{"taskMethod": "setMoveTo", "positionId": "5"},
{"taskMethod": "setAirCleanerOperation", "action": "1", "mode": "1"},
{"taskMethod": "returnToStation"}
]
}
}
}
이 형식은 기존 모든 MQTT Topic을 변경한다는 뜻이 아니다. submitWorkflow를 처리하는 공통 command parser 또는 전용 관리 Topic 하나를 추가하고, 기존 단일 명령 Topic은 유지하는 방식이 안전하다. 현재 MR6의 IotTaskManagerBridge는 단일 명령의 request.task 변환까지 구현되어 있으며, payload.subTasks[]를 ArrayList<Bundle>로 만드는 MQTT Workflow parser는 아직 구현 완료로 보면 안 된다.
응답과 이벤트의 호환 원칙
| 요청 종류 | 기존 응답 | 추가 관리 응답 | 호출자가 완료로 판단하는 기준 |
|---|---|---|---|
| 기존 직접 명령 | 기존 response topic·도메인 callback | 없음 | 기존 계약 유지 |
| 관리 단일 Task | 기존 기능 callback 유지 | accepted, taskId, STARTED/PROGRESS/COMPLETED/FAILED/CANCELLED |
accepted가 아니라 terminal event 또는 상태 조회 |
| 관리 Workflow | 기존 단계별 도메인 callback 유지 | 부모 Workflow와 단계별 Task event | 부모 Workflow terminal state |
기존 callback을 즉시 제거하면 구버전 App과 IoT 서버가 상태를 잃는다. 전환 기간에는 기존 callback과 Task event를 함께 발행하되, requestId, traceId, correlationId로 같은 실행을 묶고 수신자는 중복 반영하지 않아야 한다.
채널 전환 순서
- 기존 MQTT·App 명령과 응답을 기준선으로 고정한다.
- 취소·진행률·물리 완료 추적이 필요한 메서드 하나를 선택한다.
- IoT는
request.task, System App은 AAR, LLM은 구조화된 device request로 같은 Task 의미를 만든다. - 기존 callback과 Task event가 같은 물리 실행을 가리키는지 확인한다.
- 단일 Task가 안정화된 뒤 공통
submitWorkflowingress를 추가한다. - 호출자×메서드 단위로 확대하며 기존 direct 경로는 원복 수단으로 유지한다.
이 구조에서 호출 채널은 서로 달라도 TaskManager가 받는 최종 계약은 동일하다. 반대로 센서 update, heartbeat, 조회 결과와 단순 상태 동기화는 실행 생명주기가 아니므로 기존 메시지 경로에 남긴다.
30.2.6 기존 direct 명령과 관리 Task의 동시성
현재는 관리 Task끼리의 순서는 TaskManager가 통제하지만, 비관리 direct 명령은 대기열 밖에서 실행된다. 두 경로는 기존 DeviceAgent 도메인 정책에서 다시 만나므로 물리 안전 제한은 공유하지만, 실행 순서와 Task 상태까지 자동으로 정합되는 것은 아니다.
현재 구현에서 보장되는 범위
| 경쟁 조합 | 현재 조정 담당 | 보장되는 것 | 현재 한계 |
|---|---|---|---|
| 관리 Task ↔ 관리 Task | TaskManager Queue·Priority·Preemption·Domain Resource Policy | 같은 대기열의 순서, 관리 작업 취소, 일부 자원 충돌 거절 | 자원 선언이 없는 기능은 충돌을 모두 알 수 없음 |
| 기존 direct ↔ 기존 direct | StateManager, CmdPolicyManager와 각 도메인 Manager |
제품 상태, 배터리, 오류, 이동·청정 중복 등 기존 제품 정책 | 공통 Task ID·대기열·진행 상태 없음 |
| 관리 Task ↔ 기존 direct | 기존 도메인 상태와 물리 정책 | 금지된 동작의 거절 또는 도메인 고유 정지·전환 | direct 요청은 TaskManager Queue와 TaskRecord에 보이지 않으므로 전역 순서·선점 사유·상태 정합이 불완전 |
| 관리 실행 내부의 기존 API 재호출 | runningManagedTask |
같은 실행을 Task로 다시 감싸는 재진입 방지 | ThreadLocal 표식이므로 다른 스레드의 direct 명령을 막는 전역 잠금이 아님 |
| DeviceAgent 내부 AMR·UPDATE 명령 | AppCmd.executeThroughTaskManager() 선택 연결 |
조회·콜백이 아닌 비DIRECT 명령 일부를 기존 형식 그대로 대기열에 편입 | 외부 MainApi·MQTT direct 요청 전체에 적용되는 구조는 아님 |
예를 들어 관리 Task가 이동 중일 때 기존 App이 direct setMoveTo를 보내면, 후속 명령은 기존 CmdPolicyManager.setMoveTo()의 is_moving 검사에서 거절될 수 있다. 이는 물리적으로 두 이동을 동시에 실행하지 않게 하지만, 거절된 direct 요청을 TaskManager가 대기시키거나 원래 이동 Task의 선점 사유로 기록한 것은 아니다.
반대로 PUI 정지나 복귀가 기존 경로에서 현재 동작을 중단할 수 있다. 해당 도메인의 중단 callback이 TaskCompletionStateStore와 연결된 기능은 관리 Task가 취소·실패로 닫힐 수 있지만, 모든 도메인에서 원인과 Task ID가 연결된다고 가정하면 안 된다. 연결이 없는 기능은 관리 Task가 물리 중단을 모르고 시간 초과까지 남을 수 있다.
호환성을 유지하는 공통 동시성 경계
기존 direct 호출을 모두 공개 단일 Task로 바꾸지 않고도 동시성을 맞추려면, TaskManager와 기존 경로 아래에 공통 실행권 조정 경계가 필요하다.
기존 direct 요청 ─┐
├─ 실행 분류 ─ 공통 도메인 실행권/자원 점유 ─ 기존 DeviceAgent 정책 ─ 실제 동작
관리 Task 요청 ───┘ │
└─ 시작·중단·완료 증거를 원 요청에 귀속
이 경계는 자연어 의미를 추론하는 규칙이 아니다. TaskPolicyRegistry, 기능 계약과 requiredResources에 선언된 실행 특성을 사용해 결정적으로 동작한다.
| 요청 성격 | 권장 처리 | 기존 호환 의미 |
|---|---|---|
| 조회·센서·heartbeat·상태 callback | 기존 direct 유지, 실행권 미점유 | 응답 시점과 기존 상태 전달 유지 |
| 짧고 멱등적인 설정 | 기존 direct 유지 가능, 충돌 자원만 공통 검사 | 불필요한 Task 이벤트와 대기 지연 방지 |
| 이동·청정·보안·외부 앱 세션처럼 장기 자원 점유 | 명시 Task 또는 검증된 메서드만 내부 단일 Task로 승격 | 외부 payload는 유지하되 내부 실행 순서와 완료를 관리 |
| 기존 PUI 정지·사용자 취소 | 즉시 허용하되 점유 중인 관리 Task도 CANCELLED로 종료 |
사용자의 직접 통제권을 유지하면서 고립 Task 방지 |
| 오류·저전력·긴급 복귀 | 안전 우선 선점 후 기존 Task를 구조화된 사유로 종료 | 안전 정책이 Task 우선순위보다 항상 우선 |
| 일반 App·IoT의 충돌 명령 | 실행권이 비면 수행, 점유 중이면 busy 거절 또는 선택적 Task 대기 | 기존 명령이 관리 Workflow를 조용히 덮어쓰지 않게 함 |
장기 direct 동작의 실행권을 물리 완료까지 유지하려면 결국 시작·중단·완료 callback과 연결된 내부 기록이 필요하다. 따라서 이동·청정 같은 메서드는 검증이 끝난 순서대로 외부 응답 형식은 유지하면서 내부적으로 단일 Task로 승격하는 편이 단순한 잠금보다 안정적이다. 반면 조회와 상태 update까지 Task로 승격하면 대기열과 이벤트만 오염되므로 그대로 direct로 둔다.
충돌 결과의 일관된 귀속
동시성 조정 결과는 물리 동작과 관리 상태가 함께 바뀌어야 한다.
| 실제 상황 | 물리 처리 | 관리 Task 처리 | 권장 사유 |
|---|---|---|---|
| 사용자가 PUI로 정지 | 기존 도메인 정지 수행 | 관련 실행 Task 취소 | user_cancelled 또는 pui_cancelled |
| 안전 정책이 강제 복귀 | 현재 동작 중단 후 복귀 | 기존 Task 실패·선점, 복귀 Task 추적 | safety_preempted, 세부 오류 코드 |
| 일반 direct 명령이 관리 자원과 충돌 | 실행하지 않음 | 기존 Task 유지 | device_busy, 점유 자원과 Task ID |
| 같은 목적의 중복 요청 | 물리 재실행 없음 | 기존 실행 상태 반환 또는 요청 연결 | already_running |
| 허용된 우선순위 선점 | 기존 도메인 취소 확인 후 새 실행 | 이전 Task terminal 후 새 Task 시작 | preempted_by_request |
단순히 CmdPolicyManager가 POLICY_NOT_ALLOWED를 반환하는 것으로 끝내면 호출자는 “왜 안 됐는지”, TaskManager는 “기존 작업이 계속 유효한지”를 알 수 없다. 도메인 정책 결과를 errorCode, reason, 점유 자원과 관련 taskId로 연결해야 동시성 호환이 완성된다.
단계적 적용
- 기존·관리 경로의 동시 실행 조합을 로그로만 관찰한다.
- 이동·청정·보안·화면/카메라 세션의 자원 소유권과 사용자 취소 결과를 표준화한다.
- 충돌 가능 direct 명령에 공통 실행권 검사를 적용하되 기존 응답 형식은 유지한다.
- 검증된 장기 메서드만 내부 단일 Task 승격 대상으로 확대한다.
- PUI·오류·저전력 선점이 기존 관리 Task를 올바른 terminal 상태로 닫는지 실기기에서 검증한다.
- 호출자×메서드 단위 회귀가 끝날 때까지 기존 direct 원복 경로를 유지한다.
30.3 호환성 표면
요약: API 이름뿐 아니라 입력값, 응답 시점, 콜백, 저장소, SDK와 배포 방식도 호환성 검토 대상이다.
| 검토 영역 | 기존 동작 | 관리 경로에서 추가되는 동작 | 호환 조건 |
|---|---|---|---|
| 진입 API | 원시 method 기반 MainApi |
제어 API와 Task·Workflow 요청 구조 | 기존 경로를 제거하지 않음 |
| 메서드 | 기존 DeviceAgent 메서드 | 표준 taskMethod와 별칭 |
실제 기기 도메인 수행 주체가 같아야 함 |
| 필드 | 기존 Bundle 키 | 표준 키와 허용 별칭 | 어댑터에 명시된 키만 호환 |
| 반환 시점 | 메서드별 기존 즉시·비동기 응답 | DIRECT, ASYNC, QUEUED_WAIT에 따라 달라짐 | 호출자가 기대하는 시점을 전환 전에 검증 |
| 반환 구조 | 기존 outdata |
Task ID, 상태, 실패 사유와 중첩 결과 | 신규 필드는 추가 방식으로 제공하고 기존 필수 필드는 유지 |
| 콜백 | 기기 도메인 고유 콜백 | Task 진행·최종 이벤트 추가 | 기존 수신자를 유지하고 이중 발행 허용 |
| 정책 | CmdPolicyManager, StateManager |
Task 실행 허용, 자원과 선점 사전 검사 | 최종 기기 도메인 정책을 우회하지 않음 |
| 스케줄 | 제품 스케줄 DB | TaskManager 지연 예약 저장소 | 저장소와 반복 의미를 혼합하지 않음 |
| IPC·SDK | AIDL과 원시 Bundle | TaskManagerClient AAR 타입 기반 진입점 |
스키마·버전과 원시 전송 경로 유지 |
| 배포 | 플랫폼 서명 시스템 앱 | 같은 프로세스·권한의 Core와 AAR | 기존 플랫폼 인증서와 권한 유지 |
| 모니터 | 기존 제품 상태와 UI | Task, 대기열과 Workflow 이벤트 | 모니터 상태가 실제 실행 상태를 임의로 만들지 않음 |
30.4 그대로 보존해야 하는 동작
요약: TaskManager를 사용해도 실제 기능 주체, 안전 정책, 기존 콜백과 제품 스케줄의 의미는 유지해야 한다.
다음은 TaskManager 도입 전후에 바뀌면 안 되는 호환 불변 조건이다.
- 같은 요청은 Movement, Cleaning, Schedule, Security 등 기존 수행 주체가 처리한다.
- 기존 PUI, IoT와 시스템 앱의 원시 메서드는 선택 적용 전까지 기존 경로를 사용한다.
- 기존 기기 도메인 콜백과 상태 갱신은 계속 발행된다.
- LLM, 배터리, AMR, 개인정보 보호, 오류와 주 상태 제한은 기존 기기 도메인이 최종 판정한다.
- 같은 기기 동작이 기존 경로와 관리 경로에서 중복 실행되지 않는다.
- TaskManager가 비활성화돼도 기존 핵심 기능은 원시 경로로 수행할 수 있어야 한다.
- 기존 제품 스케줄의 등록, 조회, 실행 의미와 DB 소유권은 유지한다.
- 시스템 앱 패키지, UID, 서명 권한과 플랫폼 인증서 관계를 유지한다.
30.5 선택 적용 후 달라질 수 있는 동작
요약: 같은 기능도 TaskManager를 거치면 “접수”와 “실제 완료”의 응답 시점과 결과 형식이 달라질 수 있다.
TaskManager 경로는 실행 기능은 같지만 호출 의미까지 완전히 투명하지는 않다.
| 실행 방식 | 호출자가 받는 시점 | 의미 | 기존 호출자 전환 시 위험 |
|---|---|---|---|
DIRECT |
기존 메서드 반환 직후 | 기존 결과를 Task 요약과 함께 반환 | 중첩 결과와 추가 필드 파싱 확인 필요 |
ASYNC |
실행 허용과 TaskRecord 생성 직후 |
물리 완료 전 접수 상태 | 기존 호출자가 응답을 완료로 오해할 수 있음 |
QUEUED_WAIT |
최종 상태 또는 시간 초과 후 | 실제 완료 증거를 기다림 | 기존 UI 스레드나 Binder 시간 초과 위험 |
같은 이동 명령의 호출 차이
아래 예시는 반환 의미를 설명하기 위한 개념 예시다. 실제 필드는 호출자 계약과 출시 스키마를 따른다.
기존 호출
{
"method": "setMoveTo",
"positionId": "5",
"positionName": "안방"
}
기존 이동 경로는 MainApi가 이동 도메인에 명령을 전달하고 기존 메서드 결과를 반환한 뒤, 기존 이동 콜백으로 도착 상태를 알리는 계약을 그대로 유지한다.
TaskManager 선택 호출
{
"method": "submitTask",
"forceTaskManager": true,
"taskMethod": "setMoveTo",
"source": "APP",
"requestId": "move-bedroom-001",
"positionId": "5",
"positionName": "안방",
"executionMode": "async",
"completionTarget": "movement.arrived"
}
{
"accepted": true,
"taskId": "task-...",
"taskState": "PENDING",
"message": "작업이 접수됨"
}
이 응답의 accepted=true는 안방 도착을 뜻하지 않는다. 이동 콜백이 movement.arrived 증거로 연결되고 Task가 COMPLETED가 돼야 실제 완료다. 기존 호출자는 종전 콜백을 계속 받을 수 있고, 관리 경로 호출자는 Task 이벤트 또는 getTaskStatus를 추가로 사용할 수 있다.
따라서 호출자별 전환 전에 다음을 명시한다.
- 기존 호출자가 접수 응답을 기다리는지 실제 완료를 기다리는지
- 동기 호출 시간 제한과 UI 스레드 사용 여부
- 기존
outdata에서 반드시 읽는 키 - 콜백을 별도로 기다리는지
- 중복 재시도가 기기 동작을 다시 일으킬 가능성
30.6 메서드·필드 별칭 계약
요약: 메서드·필드 별칭은 등록된 항목에만 적용되며 모든 과거 입력을 자동으로 호환하지 않는다.
별칭 처리는 기존 요청 데이터를 모두 자동 수용하는 범용 기능이 아니다. TaskMethodAliasResolver, TaskFields와 실행기 어댑터에 등록된 범위만 계약이다.
| 소스 표현 | 쉬운 한 줄 설명 | 호환성에서 확인할 점 |
|---|---|---|
TaskMethodAliasResolver |
과거 기능 이름을 현재 표준 기능 이름으로 바꿔 주는 이름 변환기 | 등록되지 않은 이름은 자동으로 추정하지 않는다. |
TaskFields |
여러 모듈이 함께 사용하는 Task 필드 이름을 모아 둔 공통 정의 | 호출자와 실행기가 같은 필드를 쓰는지 확인한다. |
| 실행기 어댑터 | 표준 Task 입력을 기존 기능이 이해하는 입력으로 바꾸는 연결 코드 | 변환 과정에서 값이 빠지거나 임의 기본값이 생기면 안 된다. |
queue / queueKey |
작업을 어느 실행 대기열에서 처리할지 지정하는 필드 | 클라이언트가 쓰는 이름과 Core가 읽는 이름을 하나로 맞춰야 한다. |
| 엄격한 입력 검증 | 정의되지 않은 필드를 오류로 차단하는 검사 방식 | 기존 호출자의 필드를 조사하지 않고 켜면 정상 요청도 거절될 수 있다. |
| 호출 필드 계약 확인 | 실제 호출자가 보내는 필드 이름과 값의 전체 목록을 확인하는 작업 | 문서에 없는 기존 입력까지 전환 전에 확인한다. |
| 기준 요청 회귀 시험 | 현재 정상 동작하는 대표 요청을 고정해 변경 전후 결과를 비교하는 검사 | 별칭이나 검증기 변경으로 생기는 회귀를 자동으로 찾는다. |
현재 대표 호환 범위:
- 청정 시작·중지·일시정지·재개와 복귀 별칭을 표준 청정 메서드로 정규화한다.
- schedule 식별자는
scheduleId,scheduleid,schedule_id를 일부 경로에서 수용한다. - 활성·대기열 필드는
isActive/is_active,taskQueue/queue를 일부 어댑터가 수용한다. - 결과에는 Task, 실패 사유와 Cloud 상관관계의 camelCase·snake_case 별칭을 함께 기록하는 경로가 있다.
현재 알려진 정합 공백:
TaskManagerClient요청 생성기는queue를 쓰는 반면 정책 변경값은queueKey를 읽는 경로가 있다.setMoveTo의 엄격 검증 허용 키와 실행기가 읽는areaId,targetRoom,roomName범위가 완전히 같지 않다.- 모든 기존 Bundle 키가 표준 스키마 별칭으로 등록된 것은 아니다.
따라서 엄격한 입력 검증을 기본 활성화하기 전에 메서드별 호출 필드 계약 확인과 기준 요청 회귀 시험을 수행해야 한다. 현재 기본값을 false로 유지하는 것은 기존의 유연한 요청이 갑자기 거절되는 상황을 막기 위한 호환 장치다.
30.7 콜백과 이벤트 호환
요약: 기존 콜백은 유지하고 TaskManager 이벤트로 진행률과 완료 상태를 추가한다.
관리 대상 Task는 기존 콜백을 대체하지 않고 완료 증거로 사용한다.
기존 PUI·IoT·시스템 콜백은 유지한다. 관리 대상 Task와 연결된 콜백만 CompletionBridge가 TaskCompletionStateStore와 Task 진행·최종 이벤트로 변환한다.
| 소스 표현 | 쉬운 한 줄 설명 | 실제 영향 |
|---|---|---|
CompletionBridge |
기존 기능 콜백을 Task 완료 증거로 연결하는 변환 지점 | 기존 기능 코드를 다시 만들지 않고 Task 상태를 닫을 수 있다. |
TaskCompletionStateStore |
가장 최근의 실제 완료·취소·실패 증거를 보관하는 저장소 | 오래된 콜백과 현재 Task의 콜백을 구분하는 기준이 된다. |
| 상관관계 필드 | 콜백이 어느 Task와 Workflow 단계의 결과인지 알려 주는 식별자 | 여러 작업이 동시에 수행돼도 결과가 다른 Task에 붙는 것을 막는다. |
| 최종 이벤트 | 작업이 완료·실패·취소돼 더 이상 진행되지 않음을 알리는 마지막 이벤트 | 다음 Workflow 단계를 시작하거나 전체 흐름을 종료하는 기준이다. |
| 멱등 처리 | 같은 콜백이 여러 번 와도 결과를 한 번만 반영하는 처리 방식 | 통신 재전송 때문에 완료 처리나 후속 동작이 중복되는 것을 막는다. |
Task 이벤트 전달 범위
TaskManager 내부 이벤트와 시스템 앱이 받는 모듈 콜백의 범위는 같지 않다.
| 이벤트 조건 | 내부 TaskEventListener |
AppCmd.sendModuleCallback_main() |
AAR onTaskEvent() 관찰 가능성 |
|---|---|---|---|
| non-INTERNAL source Task | 전달 | 전달 | 높음 |
| workflow parent/step | 전달 | 전달 | 높음 |
| INTERNAL 단일 Task의 STARTED/COMPLETED | 전달 | 기본 미전달 | 낮음. 상태 조회 또는 모니터 필요 |
| INTERNAL 단일 Task의 FAILED/TIMEOUT/CANCELLED | 전달 | 전달 | 높음 |
| 기존 기기 도메인 고유 콜백 | 기존 수신자에 전달 | 콜백별 기존 경로 | Task 이벤트와 별도 계약 |
TaskManagerClientCallback.onTaskEvent()는 DeviceControlProxy의 모듈 콜백을 받는다. 따라서 내부 수신자가 본 모든 이벤트가 AAR 클라이언트에도 전달된다고 가정하면 안 된다. 호출자별 관찰 요구에 따라 모듈 콜백, getTaskStatus, 모니터 이벤트 중 어느 경로를 사용할지 계약해야 한다.
호환 규칙:
- 기존 콜백 수신자를 유지한 상태에서 Task 이벤트를 추가 발행한다.
- 콜백의 기능 의미를 TaskManager에서 다시 추정하지 않는다.
- Task 이벤트에는
taskId, Workflow·단계 상관관계와 최신 시각을 추가한다. - 같은 콜백이 재전송돼도 최종 이벤트는 멱등하게 처리한다.
- Task Monitor가 콜백을 받지 못해도 기존 기능 상태를 덮어쓰지 않는다.
- 기존 콜백 제거는 모든 수신자의 전환과 원복 검증 이후 별도 출시에서 수행한다.
30.8 제품 스케줄과 저장소 호환
요약: 반복 제품 일정과 “30분 뒤 한 번 실행”하는 Task 예약은 서로 다른 기능과 저장소로 유지한다.
제품 반복 스케줄과 TaskManager 지연 실행은 저장 주체와 의미가 다르다.
| 구분 | 기존 제품 스케줄 | TaskManager 지연 예약 |
|---|---|---|
| 목적 | Welcome, Wakeup, 청정 같은 반복 생활 일정 | “30분 뒤 한 번 실행” 같은 단발 Task |
| 소유자 | Schedule 도메인 | TaskManager 예약 실행기 |
| 저장소 | 제품 스케줄 DB | taskmanager_scheduled_tasks.json |
| 실행 | 제품 시작 조건과 상호작용 정책 | 실행 시각에 최신 상태를 확인한 뒤 Task·Workflow 제출 |
| 전환 | 기존 기록 유지 | 기존 DB를 자동 복사하지 않음 |
기존 스케줄 기록을 TaskManager 기록으로 묵시 변환하거나, 제품 반복 일정을 단발 Task로 재해석하면 안 된다. 상위 Planner가 제품 스케줄을 만들더라도 기존 Schedule 도메인 메서드를 관리 대상 Task로 호출하며, 스케줄 데이터의 소유권은 그대로 유지한다.
30.9 AAR, Binder와 시스템 앱 호환
요약: 기존 앱은 종전 호출을 유지할 수 있고 신규 앱은 AAR을 사용할 수 있지만, 기기 서명과 권한 조건은 동일하게 적용된다.
TaskManagerClient AAR은 기존 DeviceAgent 기능을 다른 프로세스로 옮기는 SDK가 아니다. 기존 AIDL·Binder 경계 위에 타입 기반 Task API를 제공하는 진입점이다.
| 소스 표현 | 쉬운 한 줄 설명 | 기존 시스템과의 관계 |
|---|---|---|
TaskManagerClient AAR |
다른 시스템 앱이 TaskManager를 일정한 방식으로 호출하도록 제공하는 Android 라이브러리 | 앱마다 제각각 Bundle을 만드는 코드를 줄이되 기존 호출을 즉시 없애지는 않는다. |
| Binder/AIDL | Android 프로세스 사이에서 DeviceAgent API를 호출하는 통신 통로 | TaskManager를 도입해도 기존 프로세스와 권한 경계는 유지된다. |
| 타입 기반 요청 생성기 | 메서드와 필수 필드를 코드에서 명확하게 구성하는 호출 도구 | 잘못된 문자열과 누락 필드를 호출 단계에서 줄인다. |
sendRaw(Bundle) |
기존 Bundle 형식을 그대로 보낼 수 있는 호환용 우회 입구 | 아직 AAR로 감싸지 않은 기능도 단계적으로 옮길 수 있다. |
| 계약 버전 | 클라이언트와 DeviceAgent가 이해하는 API 계약의 버전 | 서로 다른 APK 조합에서 지원 여부를 명확히 판단한다. |
| 플랫폼 인증서 | 특권 앱이 서명 권한과 동일 UID 관계를 유지하는 데 필요한 제품 서명 | 디버그 서명 APK를 넣으면 권한 오류나 부팅 문제가 생길 수 있다. |
- 기존 앱은 원시 Bundle 호출을 유지할 수 있다.
- 신규 앱은 타입 기반 요청 생성기로 Task·Workflow·예약·제어 API를 사용할 수 있다.
sendRaw(Bundle)는 아직 타입 기반 API가 없는 메서드의 점진 전환 경로다.- AAR 계약 버전과 지원 메서드 목록은 DeviceAgent 출시 형상과 함께 관리한다.
- 클라이언트 AAR과 DeviceAgent APK의 갱신 순서가 달라도 지원하지 않는 계약은 명시적으로 거절해야 한다.
- 플랫폼 서명 특권 앱의 패키지, UID와 서명 권한 조건은 TaskManager 도입으로 완화되지 않는다.
- 실기기 교체 시
apksigner verify --print-certs로 기존 APK와 신규 APK 인증서를 비교하고, 디버그 인증서 APK를/system/priv-app에 배포하지 않는다.
30.10 단계적 전환
요약: 전체 기능을 한 번에 바꾸지 않고 호출자와 기능 단위로 전환하고 검증한 뒤 확대한다.
| 단계 | 적용 범위 | 완료 기준 |
|---|---|---|
| 0 | 호출자·메서드·필드·콜백 계약의 기준선 고정 | 기존 호출, 응답, 콜백과 물리 동작 증거 확보 |
| 1 | 조회·상태·제어 API를 추가 방식으로 노출 | 기존 원시 호출 회귀 없음 |
| 2 | 메서드와 호출자 단위로 forceTaskManager 선택 적용 |
응답 시점, 중복 전달과 기기 도메인 결과 검증 |
| 3 | 완료·취소 콜백을 Task 생명주기에 연결 | 조기 완료와 고립 Task 없음 |
| 4 | 시스템 앱별 타입 기반 AAR 전환 | 원시 경로와 버전 불일치 검증 |
| 5 | 검증된 메서드만 관리 경로 기본 후보로 승격 | 여러 요청 출처 회귀, 재부팅과 장기 안정성 통과 |
| 6 | 엄격한 입력 검증 확대 | 호출 필드 계약 확인과 기준 요청 회귀 시험 100% 통과 |
일괄 전환보다 호출자 x 메서드 x 실행 방식 단위 허용 목록이 안전하다. 예를 들어 Cloud Workflow의 setMoveTo만 먼저 관리 경로로 전환하고 PUI 단일 이동은 기존 경로를 유지할 수 있다.
30.11 원복 사양
요약: 문제가 생기면 전체 APK를 되돌리기 전에 해당 호출이나 관리 기능만 기존 경로로 원복할 수 있어야 한다.
| 문제 | 원복 수단 | 영향 범위 |
|---|---|---|
| 특정 호출자의 관리 경로 오동작 | forceTaskManager 제거 |
해당 요청만 기존 경로 복귀 |
| 기존 메서드의 대기열 감싸기 문제 | persist.sys.deviceagent.taskmanager.legacy_queue=false |
기존 연결부를 직접 실행으로 전환 |
| 강제하지 않은 Task API 문제 | persist.sys.deviceagent.taskmanager.enable=false |
강제 실행이 없는 submitTask·Workflow 차단, 기존 원시 경로 유지 |
| 강제 실행·AAR 요청 문제 | 호출자에서 타입 기반 제출 중단 또는 강제 플래그 제거 | 현재 활성화 속성만으로는 차단되지 않음 |
| 엄격한 스키마의 잘못된 거절 | 엄격 검증 속성·요청 해제 | 유연한 호환 모드로 복귀 |
| 클라이언트 AAR 불일치 | 원시 Bundle 경로 또는 이전 AAR | 해당 시스템 앱 |
| APK 출시 형상 문제 | 같은 플랫폼 인증서로 서명된 검증 APK 복원 | DeviceAgent 또는 시스템 앱 출시 형상 |
현재 TaskManager.isEnabled()는 forceTaskManager=true이면 활성화 속성보다 강제 실행을 우선한다. TaskSubmitRequest와 TaskWorkflowRequest도 강제 실행 플래그를 자동으로 추가한다. 따라서 persist.sys.deviceagent.taskmanager.enable=false는 모든 요청을 차단하는 전역 중지 스위치가 아니다.
강제 요청까지 즉시 차단해야 한다면 신뢰된 호출자별 기능 플래그나 DeviceAgent의 별도 긴급 실행 차단 장치가 필요하다.
원복은 설정만 바꾸는 절차로 끝나지 않는다. 실행 중인 대기열과 Task의 취소 또는 종료 대기, 콜백 이중 발행 정리, 예약 영속성 처리와 재부팅 후 상태 정합까지 정의해야 한다.
30.12 호환성 인수 기준표
요약: 빌드뿐 아니라 기존 호출, 실기기 동작, 콜백, 재부팅과 원복의 전후 결과를 비교해야 호환 완료로 본다.
각 전환 메서드는 다음 항목을 기존 경로와 관리 경로에서 대조한다.
| 검증 축 | 질문 | 필수 증거 |
|---|---|---|
| 호출자 | PUI, IOT, APP, VOICE/CLOUD 중 누가 호출하는가 | 호출자별 요청 추적 정보 |
| 요청 전달 | 같은 입력이 같은 기기 도메인에 한 번만 도달하는가 | 도메인 진입 로그와 requestId |
| 물리 동작 | 실제 동작과 안전 제한이 동일한가 | 기기 콜백, 상태, 영상 또는 센서 증거 |
| 반환 | 반환 시점과 필수 필드가 호출자 기대와 맞는가 | 기존·관리 경로 응답 비교 |
| 콜백 | 기존 콜백과 Task 이벤트가 필요한 모든 수신자에게 도달하는가 | 이중 이벤트 추적 정보 |
| 실패 | 기존 오류가 구조화된 실패 사유로 보존되는가 | 오류·실패 사유 변환표 |
| 취소 | 취소 요청이 실제 도메인 중지와 최종 상태로 닫히는가 | 취소 명령과 중지 증거 |
| 재시도 | 네트워크·시간 초과 재시도가 중복 동작을 만들지 않는가 | 같은 requestId 재제출 시험 |
| 스케줄 | 기존 제품 일정과 지연 Task가 서로 오염되지 않는가 | DB·저장소 스냅숏과 실행 시각 로그 |
| 재부팅 | 대기·실행·예약 상태가 최신 기기 상태와 정합되는가 | 재부팅 복구 추적 정보 |
| 배포 | 패키지, 소유자·권한, SELinux와 인증서가 맞는가 | APK 해시와 인증서 비교 |
| 원복 | 관리 경로를 끈 뒤 기존 기능이 즉시 복구되는가 | 원복 훈련 결과 |
최소 회귀 세트는 PUI, IoT/MQTT, Launcher·시스템 앱, 제품 Schedule과 Voice/LLM을 모두 포함해야 한다. JVM 시험이나 APK 빌드 통과만으로 기존 시스템 호환을 확정하지 않는다.
30.13 현재 남은 호환성 위험
요약: 기본 이중 경로는 마련됐지만 필드 이름, 응답 해석, 이벤트 중복, 버전 조합과 실기기 원복 검증은 아직 완료되지 않았다.
| 위험 | 소스에서 확인한 현재 상태 | 영향 | 필요한 보강 |
|---|---|---|---|
queue / queueKey 불일치 |
AAR 요청 생성기는 queue, TaskPolicyRegistry.resolve()는 queueKey 변경값을 읽음 |
호출자가 지정한 대기열이 무시될 수 있음 | 진입점 정규화와 양쪽 계약 시험 |
| 부분 별칭 | TaskMethodAliasResolver는 현재 청정 별칭 중심 |
다른 기존 이름은 자동 변환되지 않음 | 메서드별 별칭 목록과 폐기 계획 |
| 엄격 스키마 불일치 | 검증기와 실행기가 읽는 이동·스케줄 필드 범위가 다름 | 엄격 검증에서 정상 기존 요청을 잘못 거절 | 호출 필드 계약 확인 후 스키마·실행기 단일 정의 |
| 진입 분류 이름 추정 | 분류기가 result, sensor, callback, schedule 같은 메서드 표면을 사용 |
이름이 겹치는 신규 명령이 비Task로 분류될 수 있음 | 명시적 signalType 계약과 메서드 기준 시험 |
| 내부 적용 범위 제한 | AppCmd 자동 관리가 AMR·UPDATE와 지연 없는 요청에 한정 | IoT 또는 지연 모듈은 같은 방식으로 관리되지 않음 | 모듈별 의도 범위 확정과 인수 시험 |
| 콜백 가시성 차이 | INTERNAL 성공 이벤트는 내부 수신자에게 보이나 모듈 콜백은 기본 미발행 | AAR UI가 진행·완료를 놓칠 수 있음 | 호출자별 이벤트 구독·상태 조회 계약 |
| ASYNC 의미 변경 | 접수 응답이 물리 완료보다 먼저 반환 | 기존 호출자가 조기 성공 처리 가능 | 응답 비교와 콜백 전환 시험 |
| 활성화 속성 오해 | forceTaskManager가 enable=false보다 우선하고 AAR 요청 생성기가 강제 플래그를 자동 추가 |
운영자가 전체 차단으로 오해하지만 관리 요청이 계속 들어올 수 있음 | 별도 전역 실행 차단 또는 호출자별 중지 스위치와 원복 훈련 |
| 이중 이벤트 반영 | 기존 기기 도메인 콜백과 Task 이벤트가 병존 | UI·TTS·IoT 결과가 두 번 반영될 수 있음 | 수신자 멱등성과 이벤트 소유권 표 |
| 버전 조합 | AAR, DeviceAgent와 호출자 APK가 독립 배포될 수 있음 | 지원하지 않는 필드·메서드 전송 | 호환 표와 계약 버전 거절 처리 |
| 진행 중 상태 원복 | 속성을 바꿔도 실행 중인 대기열·Workflow는 남을 수 있음 | 구·신 경로가 동시에 동작할 위험 | 종료 대기·취소·재부팅 정합 절차 |
| 실기기 회귀 공백 | JVM 시험이 MainApi, 모듈과 AAR의 모든 조합을 대신하지 못함 | 출시 환경에서만 시간·권한 문제 발생 | 호출자×메서드×실행 방식×기기 인수 시험 |
30.14 소스 근거와 검증 상태
| 호환 기능 | 구현 근거 | 자동 검증 근거 | 추가 인수 시험 |
|---|---|---|---|
| MainApi 이중 경로 | MainApi.executeMethod() |
TaskManager 통합·실행기 시험 일부 | 원시·비강제·강제 요청을 실제 Binder 호출자로 비교 |
| 기존 대기열 기본값 | TaskManager.isLegacyQueueEnabled() 기본 true |
managerStatus_shouldEnableLegacyQueueByDefault |
속성 해제·설정 상태에서 실제 AMR 명령 비교 |
| 기존 명령 연결 | TaskManager.executeLegacy() |
executeLegacy_whenDefaultEnabled... |
DIRECT·ASYNC·QUEUED_WAIT별 응답 비교 |
| 내부 모듈 분류 | AppCmd.executeThroughTaskManager(), TaskIngressClassifier |
명령·관리 중·조회·콜백 직접 전달 시험 | AMR·UPDATE와 지연 메시지 실기기 회귀 |
| 재진입 방지 | runningManagedTask |
중첩 Task 미생성 시험 | 기기 도메인 콜백 재진입과 다중 스레드 부하 |
| 타입 기반 AAR | TaskSubmitRequest, TaskWorkflowRequest, TaskManagerClient |
Bundle 계약 시험 | AAR·DeviceAgent 버전 교차 설치 |
| 이벤트 분배 | notifyTaskEvent(), shouldBroadcastModuleCallback() |
모니터·이벤트 단위 시험 일부 | 기존 콜백과 AAR 콜백 동시 수신 |
| 스케줄 분리 | Schedule DB와 TaskManager JSON 저장소 | 스케줄 등록·복원·실행 시각 시험 | 재부팅, 시계 변경과 미실행 장시간 검증 |
| 서명 호환 | 플랫폼 서명 특권 앱 배포 계약 | 빌드만으로 확인 불가 | 기존·신규 APK 인증서, UID와 SELinux 비교 |
이 표에서 “자동 검증 존재”는 해당 분기 일부를 시험했다는 뜻이다. 실제 Binder 호출자, 스레드 실행 시점, 플랫폼 권한과 물리 기기 동작까지 동일하다는 의미는 아니다. L3 이상의 적합성은 실기기 증거로 별도 판정한다.