# Milestone: USB 설치와 device 선택

## 위치

- Roadmap: `agent-roadmap/ROADMAP.md`
- Phase: `agent-roadmap/phase/usable-mvp/PHASE.md`

## 목표

USB로 연결된 Android device를 표시하고, 선택한 단일 APK를 `adb install -r`로 설치한다. 사용자는 폰 네트워크나 VPN 상태와 무관하게 Mac에서 내려받은 APK를 바로 밀어 넣을 수 있어야 한다.

## 상태

[완료]

## 승격 조건

- 없음

## 구현 잠금

- 상태: 해제
- 결정 필요: 없음

## 범위

- AppSok에 포함된 pin된 ADB runtime을 기본 실행 경로로 사용
- 사용자 지정 또는 system `adb`는 override/fallback으로만 허용
- AppSok 전용 ADB server port를 기본값으로 사용하고 공유 server fallback 제공
- `adb devices -l` 기반 device 목록
- unauthorized/offline/device 상태 표시
- 설치 대상 device 선택
- `adb install -r` 실행, stdout/stderr 표시
- install success/failure 결과 기록
- 설치 종료 후 staged APK cleanup

## 기능

### Epic: [adb-runtime] ADB 실행 환경

사용자 Mac의 Android Studio, SDK 설치, shell `PATH`에 흔들리지 않는 AppSok-managed ADB runtime을 제공한다.

- [x] [adb-path] bundled `adb`를 기본 실행 경로로 선택하고, 사용자가 지정한 custom/system `adb`는 명시적 override 또는 fallback으로만 사용한다.
- [x] [adb-bundle] macOS app bundle에 pin된 최소 ADB subset(`adb`, 필요한 `lib64/*.dylib`, `NOTICE.txt`, `source.properties`)을 포함한다. 검증: remote Mac runner에서 bundled `adb version`과 dylib 의존성 확인이 통과한다.
- [x] [adb-server] AppSok 전용 ADB server port를 기본으로 사용하고, Android Studio/CLI와 같은 기존 server를 써야 하는 경우 공유 `5037` 모드로 전환할 수 있다.
- [x] [adb-diagnostics] ADB 실행 경로, bundled version, runtime hash, server port, fallback 여부를 진단 정보로 표시한다.

### Epic: [device-list] device 탐색

연결된 Android device를 사용자가 이해할 수 있는 형태로 보여준다.

- [x] [parse-devices] `adb devices -l` 결과를 serial/state/model/product로 parsing한다. 검증: `device`, `unauthorized`, `offline` fixture test가 통과한다.
- [x] [device-state] USB debugging 승인 필요, offline, ready 상태를 UI에서 구분한다.
- [x] [refresh] 사용자가 device 목록을 새로고침할 수 있다.

### Epic: [install] APK 설치

선택한 device에 단일 APK를 설치하고 결과를 확인한다.

- [x] [install-run] 선택한 APK와 device serial로 `adb install -r`를 실행한다.
- [x] [install-output] stdout/stderr와 exit code를 UI에 표시한다.
- [x] [install-guard] APK가 선택되지 않았거나 device가 ready가 아니면 설치 버튼을 비활성화한다.
- [x] [install-result] 성공/실패 결과를 이후 이력 milestone에서 재사용할 수 있는 구조로 남긴다.
- [x] [install-cleanup] `adb install -r` 종료 후 성공/실패 결과를 보존한 뒤 staged APK를 cleanup한다. 검증: success/failure mock install test에서 cleanup이 정확히 한 번 호출되고 결과 표시가 유지된다.

## 완료 리뷰

- 상태: 승인됨
- 요청일: 2026-06-13
- 승인일: 2026-06-13
- 완료 근거:
  - `device-list` Task는 `AdbService.listDevices` fixture test, 실제 loader 기반 UI 상태 표시, 새로고침 재조회 widget test로 검증했다.
  - remote Mac runner에서 `flutter analyze`, `flutter test`, `adb devices -l`, `flutter devices`를 확인했다.
  - remote Mac runner에서 `flutter build macos`, bundled `adb version`, `otool -L` 확인이 통과했다.
- 리뷰 필요:
  - [x] 사용자가 코드 레벨 완료 검토와 archive 이동을 요청했다
  - [x] 코드 레벨 보완 이슈를 수정하고 검증했다
- 리뷰 코멘트: 코드 레벨 검토 중 실제 `AppSokApp` 경로에서 `adb install` wiring이 빠진 작은 이슈를 수정했다. local/remote `flutter analyze`, `flutter test`, remote ADB/device smoke, remote macOS build와 bundled `adb` 확인이 통과해 완료 처리한다.

## 범위 제외

- 전체 Android SDK 또는 Android Studio 설치 자동화
- `fastboot`, `sqlite3`, `mke2fs` 등 설치 흐름에 필요 없는 platform-tools 구성요소
- `adb install-multiple`
- AAB를 APK로 변환하는 `bundletool`
- 무선 ADB pairing

## 작업 컨텍스트

- 관련 경로: `lib/src/features/devices/`, `lib/src/models/adb_device.dart`, `lib/src/services/adb_service.dart`, `macos/Runner/`
- 표준선(선택): 내부 ops 도구이므로 AppSok-managed bundled ADB를 기본값으로 삼고, shell `PATH` 의존은 fallback으로만 둔다.
- 표준선(선택): 화면 코드에서 `Process`를 직접 호출하지 않고 `AdbService`를 통해 실행한다.
- 표준선(선택): `Artifact 탐색과 다운로드`는 pending install 취소 cleanup까지만 책임지고, 실제 `adb install` 종료 후 staged APK 삭제는 이 마일스톤의 `[install-cleanup]`에서 처리한다.
- 검증 메모: macOS bundle 안의 nested binary/dylib codesign과 sandbox 상태의 USB/ADB 실행은 remote Mac runner에서 smoke 검증한다.
- 선행 작업: Artifact 탐색과 다운로드
- 후속 작업: ADB 콘솔과 logcat
- 확인 필요: 없음
