param([Parameter(Mandatory)][string]$ApkPath, [Parameter(Mandatory)][string]$EvidenceDir, [string]$SourceCommit=(& git rev-parse HEAD), [bool]$SourceDirty=[bool](& git status --porcelain), [string]$MetadataName='apk-inspection.json') $ErrorActionPreference = 'Stop' $sdk = if ($env:ANDROID_SDK_ROOT) { $env:ANDROID_SDK_ROOT } else { Join-Path $env:LOCALAPPDATA 'Android/Sdk' } $aapt = Get-ChildItem -LiteralPath (Join-Path $sdk 'build-tools') -Filter aapt.exe -Recurse | Sort-Object FullName -Descending | Select-Object -First 1 if (!$aapt) { throw 'APK manifest 검사에 필요한 aapt를 찾지 못했습니다.' } $badging = & $aapt.FullName dump badging $ApkPath $nativeExit = $LASTEXITCODE $badging | Set-Content -LiteralPath (Join-Path $EvidenceDir 'apk-badging.txt') -Encoding utf8 if ($nativeExit -ne 0) { throw "aapt 종료 코드: $nativeExit" } $zip = [IO.Compression.ZipFile]::OpenRead($ApkPath) try { $entries = @($zip.Entries | ForEach-Object FullName) $abis = @($entries | Where-Object { $_ -match '^lib/([^/]+)/libil2cpp\.so$' } | ForEach-Object { ($_ -split '/')[1] }) if ($abis.Count -eq 0) { throw 'APK에 IL2CPP 라이브러리가 없습니다.' } foreach ($abi in $abis) { if ($entries -notcontains "lib/$abi/libunity.so") { throw "$abi Unity 런타임 누락" } } if ($entries | Where-Object { $_ -match '(?i)(^|/)(SourceAssets|conda|Qwen[^/]*)(/|$)|\.safetensors$' }) { throw '제작 환경 파일이 APK에 포함되었습니다.' } $entries | Set-Content -LiteralPath (Join-Path $EvidenceDir 'apk-entries.txt') -Encoding utf8 } finally { $zip.Dispose() } $package = [regex]::Match(($badging -join "`n"), "package: name='([^']+)'").Groups[1].Value $activity = [regex]::Match(($badging -join "`n"), "launchable-activity: name='([^']+)'").Groups[1].Value if ($package -ne 'com.magnetismheroes.game') { throw "예상하지 않은 패키지: $package" } if (!$activity) { throw 'APK 시작 Activity를 찾지 못했습니다.' } if (($badging -join "`n") -match "uses-permission: name='android.permission.INTERNET'") { throw '오프라인 앱에 INTERNET 권한이 있습니다.' } [ordered]@{ apk=$ApkPath; apkSha256=(Get-FileHash -LiteralPath $ApkPath -Algorithm SHA256).Hash; package=$package; abis=$abis; activity=$activity; aaptCommand=('"' + $aapt.FullName + '" dump badging "' + $ApkPath + '"'); aaptExit=$nativeExit; sourceCommit=$SourceCommit; sourceDirty=$SourceDirty } | ConvertTo-Json -Depth 4 | Set-Content -LiteralPath (Join-Path $EvidenceDir $MetadataName) -Encoding utf8