Сразу про права. Команда content выполняется от пользователя shell (uid 2000), его разрешения зашиты в системный пакет com.android.shell и от прошивки к прошивке почти не меняются. Контакты, журнал звонков, календарь и медиатека на стоковом Android обычно доступны без root. SMS закрыты: у shell нет READ_SMS, понадобится root, об этом ниже.
Общий синтаксис:
Код: Выделить всё
content query --uri <URI> [--user <ID>] [--projection <столбцы>] [--where <условие>] [--sort <порядок>]
content insert --uri <URI> --bind <столбец:тип:значение> [--bind ...]
content update --uri <URI> [--where <условие>] --bind <столбец:тип:значение>
content delete --uri <URI> [--where <условие>]
content call --uri <URI> --method <метод> [--arg <аргумент>]
content read --uri <URI>
content gettype --uri <URI>
content query, URI, проекции, условия:
Тренируемся на провайдере настроек, он открыт для shell:
Код: Выделить всё
adb shell content query --uri content://settings/system --projection name:value --where "name='screen_brightness'"
Код: Выделить всё
Row: 0 name=screen_brightness, value=96
Код: Выделить всё
adb shell content query --uri content://settings/system --projection name:value --sort "name ASC"
Код: Выделить всё
adb shell dumpsys package providers | grep -iE "contacts|calendar|media|telephony"
Код: Выделить всё
adb shell content insert --uri content://settings/system --bind name:s:user_rotation --bind value:i:1
adb shell content update --uri content://settings/system --bind value:i:0 --where "name='user_rotation'"
adb shell content delete --uri content://settings/system --where "name='my_test_flag'"
Контакты:
Контакты живут за authority com.android.contacts. Список с именами:
Код: Выделить всё
adb shell content query --uri content://com.android.contacts/contacts --projection _id:display_name --sort "display_name ASC"
Row: 0 _id=4, display_name=Avito kurier
Row: 1 _id=2, display_name=Mama
Row: 2 _id=7, display_name=Ofis SPb
Код: Выделить всё
adb shell content query --uri content://com.android.contacts/data/phones --projection display_name:data1
Код: Выделить всё
adb shell content insert --uri content://com.android.contacts/raw_contacts --bind account_type:s:adb.local --bind account_name:s:adb
adb shell content query --uri content://com.android.contacts/raw_contacts --projection _id --sort "_id DESC"
Row: 0 _id=15
Код: Выделить всё
adb shell content insert --uri content://com.android.contacts/data --bind raw_contact_id:i:15 --bind mimetype:s:vnd.android.cursor.item/name --bind data1:s:IvanTestov
adb shell content insert --uri content://com.android.contacts/data --bind raw_contact_id:i:15 --bind mimetype:s:vnd.android.cursor.item/phone_v2 --bind data1:s:+79161234567 --bind data2:i:2
Код: Выделить всё
adb shell 'content insert --uri content://com.android.contacts/data --bind raw_contact_id:i:15 --bind mimetype:s:vnd.android.cursor.item/name --bind "data1:s:Ivan Testov"'
Код: Выделить всё
adb shell 'content delete --uri "content://com.android.contacts/raw_contacts?caller_is_syncadapter=true" --where "_id=15"'
Код: Выделить всё
adb shell content query --uri content://call_log/calls --projection number:type:date:duration --sort "date DESC"
Row: 0 number=+74951234567, type=3, date=1781239512345, duration=0
Row: 1 number=+79261112233, type=2, date=1781232101567, duration=184
Код: Выделить всё
adb shell content insert --uri content://call_log/calls --bind number:s:+79990001122 --bind type:i:3 --bind date:l:1781240000000 --bind duration:i:0 --bind new:i:1
adb shell content delete --uri content://call_log/calls --where "number='+79990001122'"
SMS:
Провайдер content://sms с подпапками inbox, sent, draft. Прямой запрос на Android 10-15 от shell упирается в защиту:
Код: Выделить всё
adb shell content query --uri content://sms/inbox --projection address:date:body
Error while accessing provider:sms
java.lang.SecurityException: Permission Denial: opening provider com.android.providers.telephony.SmsProvider ... requires android.permission.READ_SMS or android.permission.WRITE_SMS
Код: Выделить всё
adb shell su -c "content query --uri content://sms/inbox --projection address:date:body --sort 'date DESC'"
Row: 0 address=900, date=1781230011000, body=Perevod 5000r ot Ivan P.
Код: Выделить всё
adb shell su -c "content insert --uri content://sms/inbox --bind address:s:900 --bind body:s:TestSMS --bind date:l:1781240000000 --bind read:i:0"
Сначала смотрим, какие календари заведены на устройстве и их _id:
Код: Выделить всё
adb shell content query --uri content://com.android.calendar/calendars --projection _id:calendar_displayName:account_name
Row: 0 _id=1, calendar_displayName=qa.device01@gmail.com, account_name=qa.device01@gmail.com
Код: Выделить всё
adb shell 'echo $(( $(date +%s) * 1000 ))'
1781245637000
Код: Выделить всё
adb shell content insert --uri content://com.android.calendar/events --bind calendar_id:i:1 --bind title:s:Standup --bind dtstart:l:1781334000000 --bind dtend:l:1781337600000 --bind eventTimezone:s:Europe/Moscow
Код: Выделить всё
adb shell content query --uri content://com.android.calendar/events --projection _id:title:dtstart --where "calendar_id=1 AND deleted=0" --sort "dtstart DESC"
Row: 0 _id=57, title=Standup, dtstart=1781334000000
adb shell content insert --uri content://com.android.calendar/reminders --bind event_id:i:57 --bind minutes:i:10 --bind method:i:1
adb shell content delete --uri content://com.android.calendar/events --where "_id=57"
Медиа через MediaStore:
Медиатека живет за authority media. Тома: external_primary (встроенная память, Android 10+) и external (все хранилища разом). Картинки, видео, аудио и загрузки лежат по путям images/media, video/media, audio/media, downloads. Найти тяжелые фото:
Код: Выделить всё
adb shell content query --uri content://media/external/images/media --projection _id:_display_name:_size:relative_path --where "_size>5000000" --sort "date_added DESC"
Row: 0 _id=1041, _display_name=IMG_20260611_193412.jpg, _size=7204881, relative_path=DCIM/Camera/
Код: Выделить всё
adb push wallpaper.jpg /sdcard/Pictures/
adb shell content call --method scan_volume --uri content://media/ --arg external_primary
Код: Выделить всё
adb shell content call --method scan_file --uri content://media/ --arg /sdcard/Pictures/wallpaper.jpg
Result: Bundle[{uri=content://media/external_primary/images/media/1042}]
Код: Выделить всё
adb exec-out content read --uri content://media/external_primary/images/media/1042 > copy.jpg
adb shell content gettype --uri content://media/external_primary/images/media/1042
Result: image/jpeg
Код: Выделить всё
adb shell content delete --uri content://media/external/images/media --where "_display_name='wallpaper.jpg'"
Код: Выделить всё
adb shell content query --user 10 --uri content://com.android.contacts/contacts --projection display_name
Permission Denial. Текст исключения называет нужное разрешение. Если его нет у shell, поможет только su -c на рутованном устройстве или adb root на эмуляторе AOSP. На обычном продакшен-смартфоне adb root не сработает, сборка не debuggable.
Кавычки и пробелы. Аргументы проходят две оболочки, локальную и удаленную (подробный разбор в главе 22). Правило: весь вызов content в одинарные кавычки, значения с пробелами внутри в двойные. where со строками удобно класть в двойные кавычки снаружи, одинарные внутри: --where "name='user_rotation'".
NumberFormatException. Метки времени всегда привязывайте типом l (long), не i.
Could not find provider или Unknown URI. Опечатка в authority или пути. Список authority дает dumpsys package providers, точные пути и имена столбцов смотрите в контрактах SDK (ContactsContract, CallLog, Telephony, CalendarContract, MediaStore) либо запросите строку без --projection и прочитайте все столбцы из вывода.
Молчаливый успех. insert, update и delete при удаче ничего не печатают. Не верьте на слово, проверяйте контрольным query.
В главе 18 займемся резервным копированием, и провайдеры всплывут снова: то, что не попадает в adb backup, часто вытаскивается именно через content query. А в главе 24 эти команды станут рабочей лошадкой подготовки тестовых данных: быстрее способа накидать на устройство контакты, звонки и события перед прогоном UI-тестов просто нет.