본문으로 건너뛰기
5분 AI.
TRENDSHIFT DAILY #7 · 2026-10-28 소스 재검토

android-ad-cleaner 딥다이브
— 중국 시장 안드로이드 폰의 광고 스위치를 화면 읽기로 대신 눌러 주는 도구

CLI·데스크탑 앱

빠른 판단

무엇
중국 내수 안드로이드 폰의 광고·추천·마케팅 알림 스위치를 adb와 화면 읽기로 대신 끄는 파이썬 도구
이럴 때
OPPO·Honor·Xiaomi·vivo 중국 내수 ROM 폰의 설정 곳곳에 흩어진 광고 스위치를 되돌릴 수 있는 방식으로 한꺼번에 끄고 싶을 때
입력
USB 디버깅을 켠 안드로이드 폰의 adb 연결과 기종별 레시피
결과
꺼진 광고·추천 스위치, 정지된 광고 패키지, 백업과 되돌리기 기록
도입 전 확인
중국 내수 ROM 중심이며 기종별 지원이 다르다. uiautomator2 3.7.1 고정. 0.2.5의 선택 오류 로그는 확인 후 전송하며 서버에서 90일 보관한다. 로컬 백업에 알림 원문이 남을 수 있다.
android-ad-cleaner는 USB 디버깅(adb)으로 연결한 폰의 설정 화면을 읽어 “개인화 광고”, “콘텐츠 추천”, “마케팅 알림” 같은 스위치를 찾아 끄는 파이썬 도구입니다. 루팅이나 앱 삭제는 쓰지 않고, 광고 컴포넌트는 되돌릴 수 있는 pm suspend로만 멈춥니다. 대상은 OPPO·Honor·Xiaomi·vivo의 중국 내수용(国行) ROM이며 화면 문구가 중국어로 하드코딩돼 있습니다.
목차
  1. 프로젝트 한줄 요약
  2. 왜 주목받는가
  3. 기술 스택 전체 지도
  4. 아키텍처 심화 분석
  5. 디렉토리 구조 해부
  6. 학습 포인트 (기술별)
  7. 하드웨어 / 시스템 요구사항
  8. 직접 해볼 수 있는 실습 과제
  9. 관련 기술 심화 학습 로드맵
  10. 핵심 키워드 사전
  11. 참고 링크

1프로젝트 한줄 요약

“설정 앱을 사람 대신 눌러 주는 스크립트”와 “그 스크립트를 AI 에이전트가 안전하게 쓰도록 쓴 규칙서”의 조합

이 저장소는 폰 설정 앱의 스위치를 대신 누르는 자동화 엔진(tool/fast.py)과, 기종별 클릭 경로(recipes/), 그리고 AI 에이전트용 작업 규칙서(AGENTS.md)로 이루어져 있습니다. 같은 엔진을 GUI(pywebview) 데스크톱 앱으로도 감싸 Mac·Windows 설치 파일로 배포합니다.

처리 대상은 README의 표 기준으로 시스템 광고 개인화·OAID 추적, 검색창 핫 키워드와 피드, 앱스토어 추천 배너, 앱별 마케팅 알림 채널, 광고 컴포넌트(com.opos.ads, com.miui.systemAdSolution), 그리고 광고 링크(hap://)가 열어 주는 “쾌응용(快应用)” 프레임워크입니다.

핵심 비유

설정 메뉴를 외워서 대신 눌러 주는 비서

사람이 하면 “설정 → 개인정보 → 광고 → 개인화 끄기”를 앱마다 반복해야 합니다. 이 도구는 화면의 글자와 스위치 위치를 읽고, 미리 적어 둔 경로(레시피)대로 눌러 줍니다.

중요한 점은 이 비서가 “위험한 일은 하지 않는다”는 약속이 대부분 문서(규칙서)에 있고 코드가 강제하는 범위는 좁다는 것입니다. 3~4장에서 그 경계를 코드로 확인합니다.

용어 먼저
adb (Android Debug Bridge)
컴퓨터에서 USB나 무선으로 안드로이드 폰에 명령을 보내는 공식 도구입니다. 폰에서 “개발자 옵션 → USB 디버깅”을 켜고 컴퓨터를 허용해야 쓸 수 있습니다. 이 프로젝트의 모든 동작이 adb 위에서 일어납니다.

1-1. 사용 방식 네 가지

방식누가 실행하나필요한 것
① AI 에이전트Claude Code 등이 AGENTS.md를 읽고 명령 실행PC, 케이블, 명령 실행 가능한 AI 도구
② 채팅 AI사람이 폰을 직접 누르고, AI는 안내만prompts/의 프롬프트
③ 수동 체크리스트사람docs/清单/ 브랜드별 목록
④ 데스크톱 클라이언트GUI 앱이 같은 CLI를 자식 프로세스로 실행Mac(Apple/Intel) 또는 Windows 10/11

핵심 설계는 “화면을 읽어 스위치를 찾는 엔진 한 벌”을 CLI, AI 에이전트, GUI가 모두 재사용한다는 점입니다. 어느 경로로 쓰든 결국 tool/run.py와 tool/fast.py가 실행됩니다.

먼저 알아둘 것
한국 갤럭시에는 그대로 쓰기 어렵습니다

레시피는 PNM-AN10(Honor), PLP120(OPPO), M154FF(Xiaomi)처럼 중국 내수 모델 번호에 묶여 있고, 클릭 대상은 “隐私”, “广告”, “设置” 같은 중국어 UI 문자열입니다. 이 글은 도구 사용법보다 UI 자동화를 어떻게 설계했는지를 배우는 데 초점을 둡니다.

2왜 주목받는가

수치와 코드에서 확인되는 것, 확인되지 않는 것

초판 관측으로, GitHub API 기준(2026-10-25 조회) 스타 264개, 포크 27개이고 저장소는 2026-10-24에 만들어졌습니다. 커밋 25개가 전부 하루 안에 올라와 있으며 릴리스는 v0.1.0, v0.2.0입니다. 즉 오래 검증된 프로젝트가 아니라 막 공개된 초기 프로젝트입니다.

2-1. 관심을 끄는 요소

2-2. 비슷한 접근과의 차이

접근방식이 프로젝트와의 차이
수동 설정사람이 앱마다 설정 열기같은 경로를 레시피로 기록해 4분 안팎에 반복(README: Honor 레시피 재실행 4분)
패키지 삭제·비활성화(pm uninstall, disable-user)시스템 앱 제거이 프로젝트는 쓰지 않음. 기능 고장과 복구 곤란을 피하려고 pm suspend만 사용
루팅·모듈system 파티션 수정루팅 없음, 시스템 업데이트 영향 없음을 목표로 함
DNS·네트워크 차단광고 도메인 차단네트워크는 건드리지 않고 앱 설정 스위치를 끔(샤오미 msa는 예외 메모 있음)

다만 “광고가 실제로 얼마나 줄었는지”를 측정한 자료는 저장소에 없습니다. README의 효과 서술은 작성자의 실측 표(기종별 ✅/⚠️/❌)이며 독립 검증은 아닙니다.

3기술 스택 전체 지도

서버도 DB도 없는 로컬 파이썬 프로젝트

다음 규모 수치는 초판 3341d60 관측값입니다. GitHub 언어 통계는 Python 90,572바이트, HTML 50,166, Shell 2,931, Inno Setup 1,507입니다. 파이썬 중 핵심 로직은 tool/ 4개 파일(합 1,027줄), gui/app.py 403줄, 레시피 3개(합 502줄)입니다. 추적 파일은 74개이며 이미지 25개, 문서 20여 개가 큰 비중을 차지합니다.

3-1. 계층별 구성

계층기술근거 파일
기기 제어adb, uiautomator2(요구 >=3.0)requirements.txt, tool/fast.py
화면 읽기dump_hierarchy()가 돌려주는 XML을 xml.etree로 파싱Phone.tree()
클릭·스크롤input swipe, input motionevent 셸 명령Phone.click(), scroll()
상태 확인dumpsys, pm, settingstool/run.py, chan_fast.py
기종 스크립트파이썬 exec()로 실행하는 레시피recipes/*/recipe.py
데스크톱 UIpywebview + 단일 HTML(394줄)gui/app.py, gui/web/index.html
패키징PyInstaller, Inno Setup, zsh 스크립트gui/windows.spec, installer.iss, build_mac.sh
배포 CIGitHub Actions(windows-latest, Python 3.12)build-windows.yml

3-2. 의존성이 거의 없다

requirements.txt는 한 줄(uiautomator2==3.7.1)입니다. 나머지는 표준 라이브러리(subprocess, re, glob, xml.etree)입니다. GUI 패키징 때만 pywebview, pyinstaller, pillow가 추가됩니다(워크플로의 pip install 줄).

3-3. 네트워크 접근

GUI는 adb가 없으면 Google의 platform-tools를 내려받습니다. 최신 소스에는 사용자가 미리보기에서 확인한 오류 로그를 유지보수자의 서버로 보내는 report_payload()·send_report()도 추가됐습니다. 따라서 네트워크 요청이 adb 다운로드 하나뿐이라는 이전 설명은 더 이상 맞지 않습니다. 이 전송 기능은 2026-10-27 v0.2.5로 출시됐습니다. README 다운로드와 gui/app.py의 VERSION도 0.2.5로 바뀌었습니다.

4아키텍처 심화 분석

진입점 하나, 엔진 하나, 기종별 스크립트 여러 개
사용자 ──┬─ AI 에이전트 (AGENTS.md 읽고 명령 실행) ──┐ ├─ GUI 앱 gui/app.py (pywebview, 자식 프로세스) ─┤ └─ 터미널 직접 실행 ──────────────────────────┤ ▼ tool/run.py (총 진입점, -s <serial> 필수) ┌───────────┬───────────┬──────────┬──────────┬─────────────┐ ▼ ▼ ▼ ▼ ▼ ▼ check/ recipe chan-scan suspend quickapp finish / backup chan-off unsuspend off|on dev-off │ │ │ │ │ │ │ ▼ ▼ └────┬─────┘ │ │ fast.py run chan_fast.py │ │ │ (레시피 exec) (알림 채널) │ │ │ │ │ │ │ └───────────┴─────┬─────┴───────────────┴───────────────────┘ ▼ adb shell / uiautomator2(dump_hierarchy) ▼ USB 연결된 안드로이드 폰

4-1. run.py: 명령 분배와 안전 목록

모든 하위 명령은 run.py -s <시리얼> <명령> 형태입니다. -s를 빼면 종료합니다(“命令格式…”). 컴퓨터에 TV나 에뮬레이터가 같이 연결돼 있어도 엉뚱한 기기를 건드리지 않게 하려는 장치입니다.

코드가 직접 강제하는 안전 규칙은 NEVER 집합입니다.

# tool/run.py
NEVER = {"com.heytap.quicksearchbox", "com.coloros.assistantscreen", "com.android.systemui",
         "com.vivo.systemuiplugin", "com.heytap.market", "com.xiaomi.market", "com.hihonor.appmarket"}

elif cmd == "suspend":
    bad = [p for p in rest if p in NEVER]
    if bad:
        sys.exit(f"这些包挂了会坏功能,不挂:{bad}")
    ...
    sh(s, f"pm suspend --user 0 {p}")
    if suspended(s, p): f.write(p + "\n")   # 되돌리기용 기록

검색창, 왼쪽 위젯 화면, 앱스토어, 시스템 UI는 정지시키면 기능이 깨지기 때문에 코드에서 거부합니다. 정지된 패키지는 backups/<모델>-<시각>/suspended.txt에 기록되고, unsuspend가 이 파일을 읽어 되돌립니다.

4-2. fast.py: Phone 클래스 (화면 읽기 → 판단 → 클릭)

핵심은 Phone 클래스입니다. 스크린샷 인식이 아니라 접근성 트리(XML)를 읽기 때문에 한 번에 약 0.03~0.1초(주석 기준)로 빠릅니다.

메서드역할
tree(), texts()현재 화면 XML을 받아 글자·좌표를 추출. 상태바(com.android.systemui) 글자는 제외
switches()스위치 노드를 찾아 (제목, 켜짐 여부, 중심 좌표) 목록 생성
tap(text)글자를 찾아 클릭. 안 보이면 스크롤, 끝까지 가도 없으면 맨 위로 돌아가 한 번 더
off(keep, only)화면의 켜진 스위치를 끔. only는 제목에 해당 단어가 있는 것만, keep은 해당 단어가 있으면 보존
dismiss(), confirm()방해 팝업은 거절 쪽 버튼으로, 스위치 끈 뒤 확인창은 확인 쪽 버튼으로 처리
setting(kw)시스템 설정의 검색창에서 키워드를 검색해 결과로 이동
start(pkg)앱을 실행하고 첫 화면의 팝업을 정리

스위치를 찾는 로직(switches())은 두 갈래입니다. 표준 위젯은 checkable="true" 속성으로 찾고, resource-id에 switch가 들어 있으나 접근성 정보가 없는 직접 그린 스위치는 화면 캡처에서 트랙 색의 채도를 봅니다.

# tool/fast.py
@staticmethod
def _pixel_on(img, l, t, r, b):
    """开关轨道取几个点:彩色(蓝/绿/橙,饱和度高)=开,灰白=关。"""
    img = img.convert("RGB")
    sat = 0
    for fx in (0.25, 0.5, 0.75):
        R, G, B = img.getpixel((int(l + (r - l) * fx), (t + b) // 2))
        sat = max(sat, max(R, G, B) - min(R, G, B))
    return sat > 60        # 채도 60 초과 = 켜짐
용어 먼저
접근성 트리 / UI 계층
화면의 모든 요소(글자, 버튼, 스위치)를 좌표·클래스·속성과 함께 트리 모양으로 내보낸 데이터입니다. 스크린 리더가 쓰는 정보이며, 자동화 도구는 이 정보로 “무엇이 어디에 있는지” 알아냅니다.

4-3. 클릭과 스크롤을 직접 만든 이유

click()은 input tap 대신 input swipe x y x y 80(제자리에서 80ms 누름)을 씁니다. AGENTS.md의 “알려진 함정” 표에 “일부 앱이 input tap을 무시한다”고 적혀 있습니다. scroll()은 input motionevent DOWN → MOVE×6 → sleep 0.12 → UP 순서로 관성을 없애 “정한 만큼만” 움직이게 하고, 제목줄·하단바를 피하려고 화면 22~80% 구간만 드래그합니다.

4-4. 레시피: 파이썬 파일을 exec()로 실행하는 미니 DSL

# fast.py 의 run 명령 (요약)
g = {"p": p, "__name__": "__recipe__", "ONLY": rest[1:]}
src = open(rest[0], encoding="utf-8").read()
build = p.d.shell("getprop ro.build.display.id").output.strip()
m = re.search(r'^BUILD\s*=\s*"([^"]+)"', src, re.M)
if m and not build.startswith(m.group(1)):
    print(f"⚠ 机型脚本是按 {m.group(1)} 录的,这台是 {build}:页面可能对不上 ...")
exec(src, g)

레시피 파일에는 import 없이 전역 p(Phone)가 이미 있습니다. @step("이름") 데코레이터가 단계를 정의하고, 데코레이터 안에서 곧바로 실행합니다. 화면이 잠겨 있으면 이후 단계를 모두 건너뛰고, 단계 안에서 예외가 나도 다음 단계로 넘어갑니다. 아래는 샤오미 레시피의 일부입니다.

# recipes/xiaomi-M154FF/recipe.py
BUILD = "OS4.0"

@step("系统广告")
def _():
    p.setting("个性化广告推荐", "个性化广告推荐", box="搜索")
    p.off(only=["个性化广告推荐"], page="系统·广告服务")

@step("应用商店")
def _():
    p.start("com.xiaomi.market")
    p.go("我的", "设置")
    gear()                                   # 글자 없는 톱니바퀴 아이콘 찾기
    if p.tap("隐私", exact=False):
        p.off(only=["个性化服务"], page="商店·隐私"); p.back()

초판 3341d60에서 읽은 레시피는 Honor 13단계, OPPO 9단계, Xiaomi 8단계였습니다. 최신 기종 범위는 뒤의 개정 절을 따릅니다. 레시피의 only=[...] 목록은 “무엇을 끌지”의 화이트리스트이고, keep=[...]는 보존 목록입니다. 이 목록이 사실상 안전 규칙입니다.

4-5. 헤매지 않게 하는 장치: miss 카운터

화면 경로가 맞지 않을 때를 위해 Phone.miss가 있습니다. 한 단계에서 대상을 한 번 못 찾으면 이후에는 스크롤하며 찾지 않고 현재 화면만 확인한 뒤 “快速放弃(빠른 포기)”합니다. 버전이 다른 기기에서 수 분을 허비하지 않고 못 찾은 단계를 빠르게 넘기기 위한 설계입니다.

4-6. 알림 채널: dumpsys notification 파싱

chan_fast.py는 dumpsys notification --noredact 출력에서 정규식으로 AppSettings와 NotificationChannel{mId=..., mName=...}를 읽습니다. 채널 이름이 KW 정규식(营销·活动·推荐·资讯·福利 등)에 맞고 SKIP(下载·订单·物流·支付·验证·来电·天气预警 등)에 걸리지 않으면 후보로 출력합니다. 끌 때는 am start -a android.settings.CHANNEL_NOTIFICATION_SETTINGS --es ...APP_PACKAGE ... --es ...CHANNEL_ID ...로 채널 설정 화면을 직접 열고 첫 스위치(“알림 허용”)를 누른 뒤 상태를 다시 읽어 확인합니다. 후보 목록은 사람이 channels.tsv에서 검토한 후 chan-off에 넣게 되어 있습니다.

4-7. GUI는 CLI의 얇은 껍데기

gui/app.py의 Api 클래스는 JS에서 호출되는 메서드(devices, status, chan_scan, start, poll, stop, restore, dev_off, diagnostics)를 노출하고, 내부에서 run.py와 chan_fast.py를 자식 프로세스로 실행해 출력을 줄 단위로 UI에 전달합니다. 패키징 후에는 파이썬이 따로 없으므로 "본 프로그램 --cli 스크립트명 인자…" 방식(cli_main의 runpy.run_path)으로 같은 스크립트를 실행합니다. GUI에서만 서비스성 채널(SERVICE 정규식: 핫스팟·긴급 알림·도난 방지·운전 모드·라이브 등)을 기본 선택에서 뺍니다.

4-8. 마무리 명령 finish와 dev-off

backup은 “충전 중 화면 켜짐(stay_on_while_plugged_in)” 원래 값을 저장하고, 레시피 실행 전에 이 값을 7로 바꿔 작업 중 화면이 꺼지지 않게 합니다. finish가 원래 값으로 복구하고 개발자 모드 해제를 안내합니다. dev-off --yes는 adb_wifi_enabled, development_settings_enabled, adb_enabled를 0으로 써서 연결을 끊습니다.

0.2.4에 포함된 변경: 읽기 세션 수명과 실패 처리

이전 개정에서 0.2.3 이후의 미발행 기능으로 설명했던 읽기 충돌 처리·보호 알림·OnePlus 적응은 CHANGELOG에서 0.2.4 수록으로 바뀌었습니다. 다음 내용은 최신 고정 소스와 0.2.4 배포 경계를 함께 읽어야 합니다.

fast.py는 매번 화면 크기를 갱신하고, 클릭 뒤 스위치가 사라지면 옛 좌표를 재사용하지 않습니다. 같은 브랜드의 참고 레시피는 AAC_REF 안전 모드에서 이름이 지정되지 않은 일반 스위치를 일괄 끄지 않습니다. 빌드는 명시한 리소스 목록만 포함해 로컬 로그·백업을 제외합니다. 이번 해설은 소스 대조이며 폰 연결·설치·회귀 테스트 실행은 하지 않았습니다.

2026-10-28 개정: 0.2.5에 포함된 오류 로그 전송

이전 README의 다운로드는 v0.2.5였습니다. 고정 소스 674364254323a59d0b7dd2632100a73cc494270f와 CHANGELOG는 기존 미발행 오류 로그 전송이 0.2.5에 수록됐음을 확인해 줍니다. 0.2.4의 기기별 수정은 유지됩니다. 배포 바이너리를 실행하거나 서명·기기 동작을 독립 검증한 것은 아닙니다.

학습 과제: side_effects()에서 허용 목록 밖 채널 변화와 앱 전체 변화가 각각 어떤 중단 경로로 이어지는지 표로 만드세요. 오류 보고 미리보기에는 없는 설치 UUID가 전송 단계에서 추가된다는 점도 report_payload()와 send_report()를 비교해 확인할 수 있습니다.

0.2.6 개정: 잠금 해제 대기와 기기별 실패 구분

현재 README의 다운로드는 v0.2.6입니다. tool/run.py::wait_unlocked는 화면을 깨운 뒤 소유자가 잠금을 풀 때까지 최대 90초 기다립니다. 자동 잠금 해제 기능이 아니며, 시간이 지나면 해당 단계를 실행하지 않았다고 종료합니다.

5디렉토리 구조 해부

코드는 작고 문서와 화면 자료가 많다
android-ad-cleaner/ (아래 규모·줄 수는 초판 3341d60 기준) ├── AGENTS.md CLAUDE.md CODEBUDDY.md AI 에이전트용 규칙서 (3개 도구 이름으로 제공) ├── README.md index.html 소개 / 중국어 랜딩 페이지 (다운로드 링크) ├── requirements.txt uiautomator2==3.7.1 ├── tool/ │ ├── run.py 348줄 총 진입점: check backup recipe chan-* suspend quickapp finish dev-off status-json │ ├── fast.py 447줄 Phone 클래스 = 화면 읽기·스위치 찾기·클릭·스크롤·팝업 처리 │ ├── chan_fast.py 116줄 알림 채널 scan/off/on/verify │ └── explore.py 116줄 새 기종 탐색(스위치를 읽기만 하고 켜고 끄지 않음, 실험적) ├── recipes/ │ ├── honor-PNM-AN10/recipe.py 13단계 │ ├── oppo-PLP120/recipe.py 9단계 │ └── xiaomi-M154FF/recipe.py 8단계 ├── gui/ │ ├── app.py 403줄 pywebview 창 + Api 클래스 + --cli 모드 │ ├── web/index.html 화면(HTML) ├── version.py smoke_test.py standalone.py │ ├── windows.spec installer.iss build_mac.sh 패키징 │ └── icon.* web/img/ ├── docs/ 00~08 번호 문서 + 清单/ (OPPO·Honor·Xiaomi·vivo 수동 목록) ├── prompts/ 채팅 AI에 붙여넣는 프롬프트 ├── images/ 설명 이미지 (+ src/*.puml 다이어그램 원본) └── .github/ 이슈 템플릿 + workflows/build-windows.yml

5-1. 읽는 순서 추천

  1. AGENTS.md — 도구가 지켜야 할 규칙과 “알려진 함정” 표.
  2. tool/run.py — 명령 목록과 NEVER, quickapps(), backup.
  3. tool/fast.py의 Phone — 가장 배울 것이 많은 파일.
  4. recipes/xiaomi-M154FF/recipe.py — 가장 짧은 레시피.
  5. docs/04-给新机型写脚本.md — 레시피 작성 규칙.
  6. gui/app.py — 자식 프로세스 + 실시간 로그 패턴.
참고

이 저장소의 .gitignore는 backups/를 제외합니다. 백업에는 설치된 앱 목록과 알림 원문이 담길 수 있어 공개 저장소에 올라가지 않게 한 구성입니다(6장 주의 참고).

6학습 포인트 (기술별)

이 저장소에서 가져갈 수 있는 설계 아이디어

6-1. UI 자동화: 좌표 대신 “글자”로 찾기

해상도·폴더블 여부가 달라도 “隐私”라는 글자를 찾아 그 위치를 누르면 동작합니다. 대신 글자가 바뀌면 깨집니다. 그래서 BUILD 접두사로 레시피가 만들어진 시스템 버전을 확인하고, 불일치하면 경고만 하고 계속 진행합니다(실패한 단계는 건너뜀).

6-2. “누른 뒤 확인한다”는 습관

off()는 각 화면에서 첫 스위치를 누르고 팝업 또는 화면 이동을 확인한 뒤, 문제가 없으면 나머지를 연속으로 누르고 마지막에 switches()를 다시 읽어 아직 켜진 것을 재시도합니다. 끝내 안 꺼진 항목은 “✗ 关不掉”로 표시합니다. 자동화에서 “눌렀다”와 “바뀌었다”를 구분하는 방법입니다.

6-3. 위험한 확인 버튼 구분하기

코드 주석에 따르면 ColorOS는 스위치 옆에 “关闭” 상태 텍스트가 있어, 이를 확인 버튼으로 오인해 누르면 스위치가 다시 켜집니다. 그래서 confirm()은 클래스 이름에 Button이 들어간 노드만 누릅니다. 이런 “실제 기기에서 겪은 사례”가 AGENTS.md의 함정 표(10건)에 정리돼 있습니다.

6-4. 되돌릴 수 있는 변경만 한다

설계 원칙
실행 전 읽기 전용 단계 → 사람 확인 → 실행 → 재확인

check(읽기) → backup → recipe → chan-scan(읽기) → 사람이 목록 편집 → chan-off(끝에 “仍开着: N 个” 재검사) → quickapp(읽기) → 사용자 동의 후 suspend. 위험한 단계 앞에 읽기 전용 단계를 배치한 점이 AI 에이전트와 함께 쓰는 도구 설계로 참고할 만합니다.

6-5. 에이전트 규칙서 vs 코드 강제력

AGENTS.md는 10개의 “하드 규칙”(모든 adb 명령에 -s, 앱 삭제 금지, 약관 동의 금지, 계정 화면 캡처 금지 등)을 둡니다. 현재 코드는 -s·NEVER 외에도 기기별 읽기 세션 잠금, 채널 페이지 신원·제목 검증과 보호 채널 제외를 강제합니다. 다만 변경 동의처럼 문서에 의존하는 경계도 남아 있습니다. 예를 들어 fast.py off 명령은 only 없이 호출하면 일반 스위치는 끄지만 광고 추적 제한처럼 반대 의미의 보호 스위치는 켜고, 의미를 판정하지 못한 부정형은 건드리지 않습니다. AI 에이전트용 도구를 만들 때는 “문서로 부탁하는 규칙”과 “코드가 막는 규칙”을 구분해 두는 편이 안전합니다.

개인정보 주의
로컬 백업에 알림 원문이 저장됩니다

backup 명령은 dumpsys notification --noredact 결과를 notif.txt로, 설치된 앱 목록을 sys.txt·user.txt로 저장합니다. GUI의 피드백 기능은 시리얼을 치환하고 앱 목록·알림 내용을 첨부하지 않는다고 설명하지만, 로컬 backups/ 폴더 자체는 민감 정보를 담을 수 있으니 공유하거나 커밋하지 마세요.

7하드웨어 / 시스템 요구사항

컴퓨터 + 케이블 + USB 디버깅을 켠 폰
항목요구 사항근거
컴퓨터macOS, Windows, Linux(CLI). 데스크톱 앱은 Mac(Apple 칩/Intel), Windows 10/11docs/01, README 다운로드 표
Python3.9 이상(문서), CI 빌드는 3.12docs/01, build-windows.yml
패키지uiautomator2==3.7.1requirements.txt
adbplatform-tools. GUI는 없으면 Google에서 내려받음docs/01, gui/app.py
케이블데이터 전송이 되는 USB 케이블docs/01
폰개발자 옵션 + USB 디버깅 켬, 컴퓨터 인증(“항상 허용”), 잠금 해제 상태AGENTS.md §3, run.py check
대상 기기OPPO/OnePlus/realme, Honor, Xiaomi/Redmi, vivo의 중국 내수 ROMAD_PKGS, QUICKAPP, README 표
Windows GUIEdge WebView2 런타임(없으면 안내 대화상자)gui/app.py 끝부분

8직접 해볼 수 있는 실습 과제

폰 없이 시작해서, 읽기 전용 명령까지만 확장
안전
이 분석은 코드를 읽기만 했고 실행하지 않았습니다

실습할 때도 처음에는 읽기 전용 명령(devices, check, quickapp, chan-scan, fast.py ls/scan)만 쓰세요. 스위치를 바꾸거나 패키지를 정지시키는 명령(recipe, chan-off, suspend, quickapp off, fast.py off/only)은 공기계나 백업이 있는 기기에서만 사용하세요. 이 도구는 중국 내수 ROM용이라 다른 ROM에서 어떻게 동작할지 보장되지 않습니다.

과제 1 (입문) · 코드 지도 그리기 폰 불필요

run.py의 cmd == "..." 분기를 모두 찾아 명령 이름과 호출하는 함수(sh, py, quickapps 등)를 표로 정리하고, “읽기 전용 / 상태를 바꿈”으로 분류하세요. 13개 명령 중 어느 것이 사람 동의를 요구하도록 문서에 적혀 있는지 AGENTS.md와 대조합니다.

과제 2 (입문) · 채널 분류 정규식 검증 폰 불필요

chan_fast.py의 KW와 SKIP 정규식을 일부만 옮겨 새 파이썬 파일에서 테스트하세요(chan_fast를 import하면 uiautomator2가 필요합니다. 아래 단어 목록은 원본의 일부입니다).

import re
KW   = re.compile(r"营销|活动|推荐|资讯|福利|优惠|红包|直播|会员|recommend|promo|news", re.I)
SKIP = re.compile(r"订单|物流|快递|支付|验证|来电|消息|提醒|天气预警|系统|红包助手", re.I)

samples = ["内容与营销", "活动优惠", "天气预警", "红包助手", "订单提醒", "直播提醒"]
for s in samples:
    print(s, "후보" if KW.search(s) and not SKIP.search(s) else "제외")
# 질문: "红包助手"가 제외되어야 하는 이유와 최신 SKIP·protected_channel의 차이는 무엇인가? (AGENTS.md §5 의 '삭제할 서비스 채널' 표와 비교)

과제 3 (초급) · 읽기 전용 점검 폰 필요

공기계에 USB 디버깅을 켜고 아래 명령만 실행해 출력 구조를 읽어 보세요. 상태를 바꾸는 명령은 아닙니다.

pip3 install -r requirements.txt
python3 tool/run.py devices                    # 시리얼, 모델, 레시피 유무
python3 tool/run.py -s <시리얼> check          # 잠금 여부, 레시피 버전 일치, 광고 패키지 상태
python3 tool/run.py -s <시리얼> quickapp       # 快应用 컴포넌트 검사 (off 를 붙이지 않음)
python3 tool/fast.py -s <시리얼> ls            # 현재 화면의 글자와 스위치 상태 목록

fast.py ls의 출력과 폰 화면을 나란히 놓고 “접근성 트리가 어떤 글자를 보여 주는가”를 관찰하세요.

과제 4 (중급) · 새 레시피 뼈대 읽고 주석 달기

recipes/xiaomi-M154FF/recipe.py의 8개 단계에 각각 “무엇을 끄는지 / 무엇을 남기는지 / 막힐 때 어떻게 되는지”를 주석으로 달아 보세요. 그리고 docs/04-给新机型写脚本.md를 보며 explore.py(스위치를 켜고 끄지 않는 탐색)가 레시피 작성을 어떻게 돕는지 설명해 봅니다.

과제 5 (심화) · 안전 가드 추가 설계

fast.py off가 only 없이 호출되는 경우를 막는 방법을 설계하세요. 예를 들어 CLI 레벨에서 --yes-all이 없으면 거부하거나, 끌 스위치 제목을 먼저 출력하고 확인을 받는 드라이런(dry-run) 모드를 추가합니다. 설계 문서와 테스트 시나리오를 먼저 쓰고, 구현은 별도 브랜치에서 하세요.

9관련 기술 심화 학습 로드맵

5주 계획
주차주제할 일
1주adb 기본기adb devices, adb shell getprop, settings get/put, dumpsys 출력 읽기(모두 조회 위주)
2주안드로이드 UI 계층uiautomator dump 결과의 bounds, checkable, resource-id 이해. Phone.switches() 다시 읽기
3주uiautomator2 APId.dump_hierarchy(), d.shell(), d.screenshot() 사용법을 공식 문서로 정리
4주패키지 관리 명령pm list packages, pm suspend/unsuspend와 disable-user의 차이, 사용자(--user 0) 개념
5주배포 패키징pywebview JS API 브리지, PyInstaller spec, GitHub Actions 윈도우 빌드, 코드 서명·공증 개념

더 나아가 접근성 서비스 API, 알림 채널(NotificationChannel) 구조, MCP/에이전트용 도구 설계(안전 규칙을 코드로 옮기는 방법)를 이어서 공부하면 좋습니다.

10핵심 키워드 사전

코드를 읽다가 막히는 말
용어쉬운 설명이 저장소에서 만나는 곳
adbPC에서 폰에 명령을 보내는 공식 도구run.py의 adb_bin(), sh()
uiautomator2파이썬으로 안드로이드 UI를 읽고 제어하는 라이브러리import uiautomator2 as u2
접근성 트리화면 요소를 구조화한 XMLPhone.tree()
OAID광고 추적용 익명 기기 식별자(중국 안드로이드)README “广告跟踪(OAID)”
pm suspend앱을 삭제하지 않고 일시 정지(아이콘 회색, 실행 불가). 되돌릴 수 있음suspend, unsuspend
pm disable-user앱 비활성화. 이 프로젝트는 쓰지 않음AGENTS.md 하드 규칙 3
快应用(쾌응용)설치 없이 열리는 제조사 연합 “경량 앱”. 광고가 hap:// 링크로 연다QUICKAPP 목록, docs/07
알림 채널앱 알림을 종류별로 나눈 안드로이드 설정 단위chan_fast.py
레시피(机型脚本)기종별 클릭 경로를 적은 파이썬 파일recipes/*/recipe.py
dumpsys시스템 서비스의 내부 상태를 텍스트로 출력알림, 키가드(잠금), 사용 기록
국행(国行)중국 내수용으로 출시된 기기·ROMREADME, AGENTS.md
ColorOS / MagicOS / HyperOS / OriginOSOPPO / Honor / Xiaomi / vivo의 안드로이드 기반 OS 이름docs/清单/
pywebview웹 화면(HTML)을 데스크톱 창에 담는 파이썬 라이브러리gui/app.py
PyInstaller파이썬 프로그램을 실행 파일로 묶는 도구gui/windows.spec
스모크 테스트빌드 결과가 최소한 켜지는지 보는 가벼운 검사gui/smoke_test.py
드라이런실제로 바꾸지 않고 바뀔 내용만 보여 주는 실행(새 안전 모드와는 별개 개념, 과제 5)

11참고 링크

2026-10-28 재검토 소스: 674364254323a59d0b7dd2632100a73cc494270f. 설치·외부 모델 호출·실행 없이 아래 파일을 대조했습니다.

README.md · CHANGELOG.md · tool/adb_link.py · tool/chan_fast.py · tool/fast.py · gui/app.py · gui/build_mac.sh

이전 개정 근거와 최신 소스의 차이를 함께 확인하기

11-1. 저장소와 핵심 소스

0.2.5 오류 로그 전송: 미리보기·동의·제한·보관 정책

11-2. 관련 기술의 공식 자료

중국어 문서가 대부분이라 번역기를 쓰되, 클릭 대상 문자열은 원문 그대로 확인하세요. 초판은 3341d60이며, 이전 2026-10-26 개정은 e2419d182ae8637ef3c9359c45effb3e80e5a8f7의 README·CHANGELOG·requirements·ui_session.py·chan_fast.py·fast.py·channel_policy.py·troubleshooting.py·gui/app.py를 기준으로 했고, 설치·실행은 하지 않고 소스를 읽어 정리했습니다.

초판 관측 수치 (2026-10-25, 현재값 아님)

항목내용기준
저장소rddmwmddr/android-ad-cleanerGitHub
선정 위치TrendShift #162026-10-25 분석 큐
스타 / 포크264 / 27GitHub API, 2026-10-25
생성일 / 최근 push2026-10-24 / 2026-10-24GitHub API
릴리스v0.2.6 (README와 CHANGELOG 기준)2026-10-28 고정 소스
언어(바이트)Python 90,572 · HTML 50,166 · Shell 2,931 · Inno Setup 1,507GitHub 언어 통계
추적 파일74개, 커밋 25개커밋 3341d60
라이선스MIT (저작권 표기 “2026”)LICENSE
한계와 사용 조건
쓰기 전에 알아둘 한계

초기 프로젝트입니다. 초판의 공개 직후 관측과 0.2.4의 제한된 실기기 결과를 구분해야 합니다. 지원 범위는 모델·OS·단계별로 다릅니다. 이 분석은 소스 읽기만 했고 실제 폰에서 실행한 결과가 아닙니다.

배포 바이너리는 확인하지 않았습니다. README의 Mac 서명·공증, Windows 설치 파일 내용은 소스와 별개입니다. 실행 파일보다 소스로 실행하는 편이 검증하기 쉽습니다.

되돌림은 일부입니다. 문서의 되돌리기 표에 따르면 앱 내부 스위치는 수동으로 다시 켜야 하고, 시스템 업데이트로 값이 초기화될 수 있습니다.

에이전트에 맡길 때의 권한. adb 접근 권한을 가진 AI에게 규칙서만으로 안전을 기대하지 말고, 처음에는 읽기 전용 명령 중심으로 진행하며 변경 전 목록을 직접 검토하세요.