pm disable-user — no root, no custom recovery, no warranty loss
adb in PATH.PATH (Windows: *System properties → Environment Variables → Path*). Check the exact version in the release notes — сверьтесь с документацией for the current build number.What to do: install platform-tools and verify that ADB sees the phone.
adb version
adb devices
Why: adb (Android Debug Bridge) is the client that talks to the adbd daemon on the phone over USB. Everything in this guide is executed as the shell user, which is allowed to change package state for user 0 without root.
How to check: the first command prints the client version line; the second prints:
List of devices attached
4C7A1B2C3D device
If the state column says unauthorized or the list is empty, jump to *Troubleshooting* before continuing.
What to do: dump the full package list and the list of already-disabled packages into two files, then grep for the ad/recommendation candidates.
adb shell getprop ro.product.model
adb shell getprop ro.build.version.release
adb shell pm list packages > packages-before.txt
adb shell pm list packages -d > packages-disabled-before.txt
adb shell pm list packages | grep -iE "msa|mipicks|adsolution|analytics|hiboard|heytap|browser"
Why: pm (Package Manager) is the Android service that owns install/uninstall/enable state. Saving the “before” snapshot is your undo file: if something breaks, you re-enable exactly those packages instead of guessing.
How to check: packages-before.txt contains several hundred lines starting with package:; the grep prints only the candidates. Example output on a Xiaomi build:
package:com.miui.msa.global
package:com.miui.systemAdSolution
package:com.miui.analytics
package:com.xiaomi.mipicks
package:com.mi.globalbrowser
Package names differ between ROM builds and regions — always confirm with pm list packages on your device. If a name from the table in Step 2 does not appear, that component does not exist on your build and there is nothing to disable.
Checklist before you proceed:
adb devices shows your serial with state devicepackages-before.txt exists and is non-emptypackages-disabled-before.txt exists (may be empty on a fresh phone)getpropExample: on a HyperOS device the inventory contains package:com.miui.msa.global, which is the MIUI System Ads service; on a ColorOS device the same role is played by a com.heytap.* or com.coloros.* package, and the exact name must be read from your own dump.

What to do: put the confirmed package names into a text file, one per line, then loop over it with pm disable-user.
cat > packages-to-disable.txt <<'EOF'
com.miui.msa.global
com.miui.systemAdSolution
com.miui.analytics
com.xiaomi.mipicks
com.mi.globalbrowser
com.miui.android.fashiongallery
EOF
while read -r pkg; do
[ -z "$pkg" ] && continue
echo "== $pkg"
adb shell pm disable-user --user 0 "$pkg"
done < packages-to-disable.txt
Why: pm disable-user --user 0 flips the component to the disabled-user state for the primary user only. The APK stays in /system, so OTA updates still validate, and a single pm enable brings it back. This is the non-root equivalent of “freeze” in Titanium Backup.
Typical targets and what they control:
| Target | Typical package (verify with `pm list packages`) | What it does | Risk |
|---|---|---|---|
| System ads service | `com.miui.msa.global` | Serves “recommended” cards inside system apps | Low |
| Shared ad container | `com.miui.systemAdSolution` | Ad SDK used by several system apps | Low |
| Telemetry | `com.miui.analytics` | Usage/event reporting | Low |
| App store feed | `com.xiaomi.mipicks` | GetApps suggestions and notifications | Low, if you don’t use GetApps |
| Browser news cards | `com.mi.globalbrowser` | Mi Browser with article feed | Low, if you use another browser |
| Lock-screen ads | `com.miui.android.fashiongallery` | Wallpaper carousel with promoted content | Low |
| Negative screen | `com.miui.personalassistant` | Left-swipe news/App Vault page | Medium — the screen disappears |
| System player feeds | `com.miui.videoplayer`, `com.miui.player` | Online content inside the local player | Medium — replaces your player |
Do not disable com.android.* packages, com.miui.home (launcher), or com.android.systemui — those are core UI, and a wrong hit gives you a boot loop.
How to check: each line of the loop prints a state confirmation:
== com.miui.msa.global
Package com.miui.msa.global new state: disabled-user
Then verify the state directly:
adb shell pm list packages -d | grep -i msa
adb shell dumpsys package com.miui.msa.global | grep -i "enabled"
Expected: the first command echoes the package name; dumpsys prints enabled=2 (disabled) or enabled=3 (disabled-user) instead of enabled=0/enabled=1.
Second lever — Private DNS. Ads and news cards are fetched from remote hosts, so a filtering resolver kills the remaining requests even inside apps you keep.
adb shell settings put global private_dns_mode hostname
adb shell settings put global private_dns_specifier <filtering-dns-host>
Replace <filtering-dns-host> with the hostname published by your DNS filtering provider (the value is provider-specific — сверьтесь с документацией of that provider). This works because Android 9+ resolves all lookups over DNS-over-TLS to that host, so blocked domains fail at the resolver instead of inside the app.
Third lever — notification channels. For apps you want to keep, silence their promo channel instead of uninstalling:
adb shell cmd appops set com.miui.videoplayer POST_NOTIFICATION ignore
POST_NOTIFICATION is available on Android 13+; on older builds use *Settings → Apps → <app> → Notifications* and turn the “Recommendations” channel off manually.

What to do: reboot the phone, then re-check state and behaviour.
adb reboot
adb wait-for-device
adb shell pm list packages -d
adb shell settings get global private_dns_mode
adb shell settings get global private_dns_specifier
Why: some OEM services re-spawn their ad component on first boot after a change; a reboot proves the state survived and that nothing crashed at startup.
How to check: the expected screen output is a list containing each package you disabled, plus:
hostname
<filtering-dns-host>
Then inspect the device by hand — the acceptance criteria are visual:
If a package reappears in pm list packages -d output as enabled, the ROM restored it — repeat the pm disable-user command once; a second failure means the component is protected and you should stop there.
Example: after disabling com.miui.msa.global and setting a filtering resolver, the folder that previously showed three promoted game tiles shows only the apps you installed.
Problem. ADB sees no phone at all.
adb: no devices/emulators foundadb kill-server
adb start-server
adb devices
Problem. The phone is listed but commands are refused.
error: device unauthorized. Please check the confirmation dialog on your device.adb devices — the state must read device, not unauthorized.Problem. Disabling a package fails.
Failure [DELETE_FAILED_INTERNAL_ERROR] or Failure [DELETE_FAILED_USER_RESTRICTED]DELETE_FAILED_* applies to pm uninstall; pm disable-user returns Exception occurred while executing 'disable-user': java.lang.SecurityException: ... for the same protected set.appops (Step 2) and block its hosts with Private DNS. Do not chase it further — a protected component is usually wired into the launcher or settings app.You now have a repeatable, root-free procedure: snapshot the package list, disable ad/recommendation components with pm disable-user --user 0, and cut the remaining traffic with Private DNS. Keep packages-before.txt and packages-to-disable.txt in version control so you can reproduce the same state after a factory reset, and replace the preinstalled junk with tools you actually chose — see Best AI apps for Android in 2026 and, if you want a desktop-class terminal on the phone itself, How to Run Linux on Android Without Root: Termux, PRoot, and AI Tools.
Do I need root to disable system ads on a Chinese Android phone?
No. pm disable-user --user 0 runs as the shell user over ADB, which is granted permission to change package state for the primary user. Root is only required for editing files inside /system or removing an APK physically.
Will disabling system packages break OTA updates?
The APK stays in the system partition, only its state changes, so OTA validation is unaffected. A major update may reset some states — after each update, re-run adb shell pm list packages -d and re-apply the loop from packages-to-disable.txt.
How do I undo everything?
Run adb shell pm enable <package> for each entry, or restore the whole list at once:
while read -r pkg; do adb shell pm enable "$pkg"; done < packages-to-disable.txt
Expected output per line: Package com.miui.msa.global new state: enabled.
Why do ads come back after a factory reset?
A factory reset wipes /data, including the disabled state, while the APKs in /system remain. Re-run Step 1 to rebuild the inventory (package names may differ on the new build) and re-apply the disable loop.