← Docs hub

Android 앱 안에서 프레임워크를 누가 시작하는가

이 페이지는 시작 서비스 연결의 상세 문서다. 전체 로보틱스 프레임워크나 제품 부팅 완료를 뜻하지 않는다. 구현 검증 결과는 아래 검증 절에서 별도로 관리한다.

전체 구조 Atlas → 리팩터링 진행 현황 → 이 시작 서비스 상세 순서로 읽는다. 기존 Atlas의 전체 모듈 구조는 유지한다.

1. 전체 구조에서의 위치

앱은 실행할 장소를 제공하고, skix-framework/base는 프레임워크의 기능과 규칙을 제공한다. 앱 안에서 동작한다고 해서 모든 기능을 Android Service 하나에 넣는 구조는 아니다.

계층 담당 코드 역할 여기서 처리하지 않는 것
Android 등록 apps/SkixFrameworkService/app/src/main/AndroidManifest.xml Android가 생성할 서비스 컴포넌트 명시 도메인 명령·정책
Android 진입점 SkixFrameworkHostService onCreate/onDestroy를 내부 호스트에 전달 Task 실행·팬/모터 제어·외부 Binder 공개
실행 인스턴스 관리 FrameworkRuntimeHost 한 번 시작, 종료, 실패 정리, 중복·재진입 방지 서비스별 업무 상태의 재정의
서비스 조립 SkixFrameworkCompositionRoot 같은 수명 관리자와 자원 소유권을 공유하는 객체 구성 Android 컴포넌트 생성
서비스 수명 관리 FrameworkServiceRegistry 의존 순서 시작·부팅 단계 전달·역순 종료 물리 정지 판정
개별 공통·도메인 서비스 State/Policy/Resource/Task 및 각 도메인 자기 책임의 상태·정책·실행 처리 Android 프로세스 상주 보장

2. 폴더를 따라 읽기

apps/SkixFrameworkService/
  app/src/main/AndroidManifest.xml          Android 서비스 등록

skix-framework/base/
  services/runtime/java/
    com/sk/airbot/frameworkservice/server/entrypoint/service/
      SkixFrameworkHostService.java         Android Service 어댑터

  services/core/java/
    com/sk/airbot/frameworkservice/server/
      runtime/
        FrameworkRuntimeHost.java          실행 인스턴스의 시작/종료 상태
        SkixFrameworkCompositionRoot.java  공통 서비스 생성과 연결
      control/lifecycle/
        FrameworkServiceRegistry.java      의존 순서와 실패 정리
        ServiceLifecycleSupervisor.java    개별 서비스의 수명·준비 상태
      control/{state,policy,resource,capability}/
      execution/{task,evidence}/           이번 기본 조립의 내부 기능
      domain/                              별도 제품 연결이 필요한 도메인들

  services/tests/src/                       Android와 분리한 JVM 계약 검사

services/runtime/java/만 Android의 Service/Intent에 의존한다. services/core/java/의 호스트와 기존 수명 관리 코드는 Android 없이 테스트할 수 있다. base/core의 SDK Manager에 Android 호스트 내부 객체를 노출하지 않는다.

3. 정상 시작과 종료

동일 앱의 명시적 시작 요청 / 테스트 하네스
  → Android onCreate()
  → FrameworkRuntimeHost.start()
  → CompositionRoot.start()
  → Registry가 의존 순서로 onStart() 호출
  → CORE_SERVICES 단계 전달
  → 호스트 RUNNING

Android onDestroy()
  → FrameworkRuntimeHost.stop()
  → Registry가 역순으로 onStop() 호출
  → 호스트 STOPPED
  → finally에서 Android super.onDestroy()

현재 기본 조립은 Runtime, State, Evidence, Policy, Resource, Capability, Task의 7개다. 시작 호출 목록의 실제 의존 순서는 Registry가 결정한다. Health, AMR, Cleaning, IoT, LLM, Vision 등 모든 서비스가 자동으로 조립되는 것은 아니다. Diagnostics는 Health 의존성이 있어 전체 도메인 목록을 그대로 추가할 수도 없다.

RUNNING은 이 인스턴스가 시작 콜백과 공통 부팅 단계를 마쳤다는 뜻이다. 모든 로봇 기능이 사용 가능하거나 센서 관측이 유효하다는 뜻이 아니다. STATE_READY, PROVIDERS_READY, EXTERNALLY_READY는 자동으로 올리지 않는다.

4. 중복·실패·종료가 겹치면

상황 처리 원칙 이유
같은 호스트에 start 두 번 두 번째 시작 거부 서비스 객체·실행 권한 중복 생성 방지
시작 전에 stop 콜백 없이 STOPPED 시작하지 않은 서비스를 종료하지 않음
정상 종료 후 stop 반복 추가 콜백 없음 자원 정리를 두 번 하지 않음
시작 콜백 실패 Registry의 기존 롤백만 수행, 호스트 FAILED 이미 정리한 서비스를 다시 정리하지 않음
CORE_SERVICES 전달 실패 시작된 composition 종료, FAILED 절반만 준비된 호스트 사용 방지
부팅 단계 실패 뒤 정리도 실패 최초 실패 유지, 정리 실패를 suppressed로 보존 최초 원인과 정리 실패를 함께 확인
종료 콜백 실패 Registry가 나머지 서비스 종료도 시도, 호스트 FAILED 한 서비스 때문에 뒤의 정리를 생략하지 않음
callback 안에서 start/stop 재호출 전이 중 재진입 거부 STARTING/STOPPING 상태 훼손 방지
서로 다른 스레드의 start/stop 직렬화 동시에 수명을 바꾸지 않음

FAILED 또는 STOPPED인 호스트는 다시 시작하지 않는다. 새 실행은 새 인스턴스의 책임이다. 이것은 영속 Task 복구나 자동 재시도 기능이 아니다.

5. 왜 지금은 비공개인가

Manifest의 exported=false, onBind()의 null 반환으로 외부 Manager 접근을 아직 열지 않는다. 실제 Manager endpoint, signature 권한과 호출자 범위 정책이 함께 준비되어야 공개할 수 있다. 존재하지 않는 Manager 대신 빈 Binder를 등록하지 않는다.

onStartCommand()는 Task나 Bundle 명령을 해석하지 않고 START_NOT_STICKY를 반환한다. 반복 시작 요청이 와도 이미 생성한 내부 런타임을 다시 만들지 않는다.

아직 제품의 자동 부팅 호출자, 부팅 receiver, init.rc, SystemServer 등록은 연결하지 않는다. 비공개 컴포넌트이므로 일반 외부 앱이나 adb shell의 직접 시작 성공을 검증 조건으로 삼지 않는다. 제품의 상주·복구 정책과 Android 백그라운드 실행 제한 검증은 후속이다.

6. 종료 콜백과 물리 정지는 다르다

프로세스가 강제로 죽으면 onDestroy() 호출이 보장되지 않는다. 따라서 이 종료 코드만으로 팬·모터 정지를 보장할 수 없다. 제품 연결 시 하드웨어 감시, 연결 단절 시 안전 동작, 재연결 후 새 관측을 함께 검증해야 한다.

새 composition의 자원 상태는 기존 기본값인 UNVERIFIED에서 출발한다. 이전 프로세스의 캐시나 Task 요청을 실제 기기 상태로 복구하지 않는다. 상태·자원 소유권 지도와 Cleaning 팬 정지 근거는 이 호스트와 다른 책임이다.

7. 검증과 남은 단계

구현 커밋은 0d7cfd1이다. 서버 JVM 425개, SDK JVM 46개가 실패·오류·건너뜀 없이 통과했다. 서버 검사에는 신규 호스트 계약 16개가 포함된다. Android 등록 정적 검사 2개와 구조·계층·공개 API·Bundle 경계 검사 4종도 통과했다.

검증은 기존 미완성 LLM/TTS 변경을 제외한 커밋 기준 소스 스냅샷으로 수행했다. 전체 작업 트리의 모든 변경이나 실제 기기 동작이 검증된 것은 아니다. 전체 재실행과 release APK 빌드를 수행했고, APK 내부 Manifest에서도 비공개 서비스 등록을 확인했다. 최종 APK SHA-256은 0dd93328889df018d9ab23f3350043dd366545b13777b9702f6b973bcd626de1이다. APK는 기기에 설치하지 않았다.

구분 완료를 판단할 증거
코드 연결 실제 composition의 JVM 시작·종료·실패·동시성 테스트
Android 등록 정적 계약 검사와 빌드 APK 내부 Manifest
제품 자동 부팅 제품 포함 설정, 시작 호출자, 부팅 로그 — 이번 범위 밖
외부 Manager 공개 실제 endpoint와 호출자 권한 검사, IPC 왕복 — 이번 범위 밖
실기기 동작 관측·Task·자원·물리 정지의 연결 검증 — 이번 범위 밖

MR6 task_manager의 기능 이식 원칙은 그대로다. 이 시작 서비스 구현을 MR6 실행기 전체 이식이나 기존 DeviceAgent 제거 완료로 집계하지 않는다.

Connections

Keyboard shortcuts

⌘K / Ctrl+KOpen command palette
/Focus search
g hGo to home
g pGo to projects
g sGo to sessions
j / kNext / prev row (tables)
?Show this help
EscClose dialogs

Structured queries

Mix key:value filters with free text in the palette:

type:sessionOnly session pages
project:llm-wikiFilter by project name (substring)
model:claudeFilter by model name (substring)
date:>2026-03-01Sessions after a date
date:<2026-04-01Sessions before a date
tags:rustPages mentioning a tag/topic
sort:dateSort results by date (newest first)

Example: type:session project:llm-wiki date:>2026-04 sort:date