Android SDK предоставляет много средств для работы с системой. В том числе он позволяет получать список приложений, которые установлены на устройстве. Это может быть полезно, когда нужно получить сведения о сторонних приложениях (размер APK, путь до приложения, имя пакета и т.д.). Например, в наших приложениях получение списка, содержащего сторонние приложения, играет большую роль: в GreenBro с помощью этого списка выводятся сведения о приложениях, а также выполняются различные действия.
В Менеджере системных приложений и APK Extractor же список приложений необходим, чтобы удалять приложения и извлекать APK из приложений соответственно.
В этой статье мы рассмотрим, как можно получать список приложений, установленных на устройстве, а также как происходит установка приложений на устройство.
Класс PackageManager
PackageManager предоставляет API, который фактически управляет установкой, удалением и обновлением приложений. Когда мы устанавливаем файл APK, PackageManager анализирует этот APK и выводит результат.
Получить экземпляр класса PackageManager можно с помощью метода getPackageManager(). PackageManager предоставляет методы для запросов к установленным пакетам и соответствующим разрешениям.
Где хранятся файлы APK на Android?
В зависимости от типа данных, на Androiid файлы могут храниться в следующих местах:
- Предустановленные и системные приложения (Камера, Браузер и т.д.) хранятся в /system/app/
- Установленные пользователем приложения хранятся в /data/app/
- PackageManager создаёт каталог /data/data/<имя пакета>/ для хранения базы данных, файлов с предпочтениями, нативных библиотек и кеша.
Как PackageManager хранит информацию о приложении?
Менеджер пакетов хранит информацию о приложении в трёх файлах, расположенных в /data/system.
packages.xml
Этот XML-файл содержит список разрешений и пакеты\приложения. Он хранит две вещи: разрешения и пакет. Например:
<packages> <last-platform-version external="15" internal="15"> <permission-trees> <permissions> <item name="android.permission.CHANGE_WIFI_MULTICAST_STATE" package="android" protection="1"> <item name="android.permission.CLEAR_APP_USER_DATA" package="android" protection="2"> ... </item></item></permissions> <package codepath="/system/app/Contacts.apk" flags="1" ft="136567b3990" it="136567b3990" name="com.android.contacts" nativelibrarypath="/data/data/com.android.contacts/lib" shareduserid="10001" ut="136567b3990" version="15"> <sigs count="1"> <cert index="2"> </cert></sigs> </package> ... <package codepath="/data/app/com.project.t2i-2.apk" flags="0" ft="13a837c2068" it="13a83704ea3" name="com.project.t2i" nativelibrarypath="/data/data/com.project.t2i/lib" userid="10040" ut="13a837c2ecb" version="1"> <sigs count="1"> <cert index="3" key="308201e53082014ea0030201020204506825ae300d06092a86 4886f70d01010505003037310b30090603550406130255533110300e060355040a13074 16e64726f6964311630140603550403130d416e64726f6964204465627567301e170d31 32303933303130353735305a170d3432303932333130353735305a3037310b300906035 50406130255533110300e060355040a1307416e64726f6964311630140603550403130d 416e64726f696420446562756730819f300d06092a864886f70d010101050003818d003 08189028181009ce1c5fd64db794fd787887e8a2dccf6798ddd2fd6e1d8ab04cd8cdd9e bf721fb3ed6be1d67c55ce729b1e1d32b200cbcfc91c798ef056bc9b2cbc66a396aed6b a3629a18e4839353314252811412202500f11a11c3bf4eb41b2a8747c3c791c89391443 39036345b15b5e080469ac5f536fd9edffcd52dcbdf88cf43c580abd0203010001300d0 6092a864886f70d01010505000381810071fa013b4560f16640ed261262f32085a51fca 63fa6c5c46fde9a862b56b6d6f17dd49643086a39a06314426ba9a38b784601197246f8 d568e349a93bc6af315455de7a8923f40d4051a51e1658ee34aca41494ab94ce978ae38 609803dfb3004806634e6e78dd0be26fe75843958711935ffc85f9fcf81523ce23c86bc c5c7a"> </cert></sigs> <perms> <item name="android.permission.WRITE_EXTERNAL_STORAGE"> </item></perms> </package> ... </permission-trees></last-platform-version></packages>
Разрешения хранятся в теге <permissions>. Каждое разрешение имеет три атрибута: name, package и protection. Атрибут name это имя разрешения, которое мы используем в AndroidManifest.xml. Атрибут package указывает на пакет, которому принадлежит разрешение, в большинстве случаев это “android”. Атрибут protection указывает на уровень безопасности.
Тег <package> содержит 10 атрибутов и несколько подтегов.
Атрибут | Описание |
name | Имя пакета |
codePath | Путь установки APK |
nativeLibraryPath | Нативная библиотека, расположенная по умолчанию в /data/data/<имя пакета>/lib |
flag | Хранит флаги ApplicationInfo |
ft | Время в шестнадцатtричном формате |
lt | Время установки в шестнадцатеричном формате |
ut | Время последнего обновления в шестнадцатеричном формате |
version | Код версии из AndroidManifest.xml |
sharedUserId | Идентификатор пользователя Linux, который будет использоваться совместно с другими приложениями. |
userId | Идентификатор пользователя Linux |
Подтеги же здесь следующие:
- <sigs> представляет собой информацию о сигнатуре, атрибут count – количество тегов <cert>.
- <cert> это ключ сертификата, атрибут index представляет собой глобальный индекс сертификата.
- <perms> содержат разрешения, которые разработчик установил в AndroidManifest.xml
packages.list
Это простой текстовый файл, содержащий имя пакета, идентификатор пользователя, флаги и каталог data.
com.android.launcher 10013 0 /data/data/com.android.launcher com.android.quicksearchbox 10033 0 /data/data/com.android.quicksearchbox com.android.contacts 10001 0 /data/data/com.android.contacts com.android.inputmethod.latin 10006 0 /data/data/com.android.inputmethod.latin
package-stopped.xml
Этот файл содержит список пакетов, которые были остановлены. Остановленные приложения не могут принимать широковещательные сообщения.
<stopped-packages> <pkg name="com.android.widgetpreview" nl="1"></pkg> <pkg name="com.example.android.livecubes" nl="1"></pkg> <pkg name="com.android.gesture.builder" nl="1"></pkg> <pkg name="com.example.android.softkeyboard" nl="1"></pkg> </stopped-packages>
Получаем список приложений
Рассмотрим получение списка установленных приложений на примере GreenBro.
При запуске приложения запускается AsyncTask, внутри которого получаем экземпляр PackageManager и затем копируем в список List<ApplicationInfo> все данные об установленных приложениях.
final PackageManager pm = context.getPackageManager(); List<AppInfo> apps = new ArrayList<>(); List<ApplicationInfo> packages = pm.getInstalledApplications(PackageManager.GET_META_DATA);
Метод getInstalledApplications() принимает в качестве параметра флаг GET_META_DATA, который определяет, что нам нужные метаданные каждого пакета.
Результатом является список объектов ApplicationInfo для каждого установленного приложения. Класс ApplicationInfo предоставляет подробную информацию о пакете, собранную из тега <application> в AndroidManifest.xml, нам оттуда нужны лишь самые важные данные.
Поэтому в цикле проверяем каждый объект из полученного списка и записывать данные в собственный класс AppInfo, чтобы затем использовать в основном потоке.
for (ApplicationInfo appInfo : packages) { PackageInfo packageInfo; try { packageInfo = pm.getPackageInfo(appInfo.packageName, 0); File file = new File(appInfo.publicSourceDir); String size = formatFileSize(context, file.length()); AppInfo newApp = new AppInfo(applicationLabel(context, appInfo), appInfo.packageName, appInfo.sourceDir, appInfo.publicSourceDir, packageInfo.versionName, packageInfo.versionCode, isSystemPackage(packageInfo), size, file.length(), appInfo.dataDir, appInfo.nativeLibraryDir, file.lastModified(), packageInfo.firstInstallTime, packageInfo.lastUpdateTime, appInfo.enabled); apps.add(newApp); publishProgress(newApp); } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); } }
Здесь с помощью метода getPackageInfo() класса PackageManager мы получаем общую информацию о приложении по заданному имени пакета. После эта информация объединяется с информацией, полученной от getInstalledApplications() и сохраняется в объекте AppInfo со следующими полями:
- title – название приложения
- packageName – имя пакета
- sourceDir – полный путь до APK приложения
- publicSourceDir – путь до общедоступных частей sourceDir
- versionName – имя версии
- isSystem – определяет, является ли приложение системным
- size – размер приложения (в удобной форме)
- longSize – размер приложения в long
- dataDir – полный путь к каталогу data
- nativeLibraryDir – путь до нативных библиотек
- modified – дата последнего изменения
- firstInstallTime – дата установки
- lastUpdateTime – дата последнего обновления
- enabled – определяет, включено ли приложение
Чтобы узнать название приложения, можно также воспользоваться PackageManager, как показано ниже.
private String applicationLabel(Context con, ApplicationInfo packageInfo) { PackageManager p = con.getPackageManager(); return p.getApplicationLabel(packageInfo).toString(); }
Проверка же на то, является ли приложение системным, тоже достаточно проста и показана ниже.
private boolean isSystemPackage(PackageInfo pkgInfo) { return ((pkgInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0); }
В конце работы AsyncTask возвращает результат обратно в основной поток. Вот и всё, мы загрузили себе список всех установленных на устройстве приложений и можем продолжить с ним работу.
Подскажите пожалуйста, в конструкции:
final PackageManager pm = context.getPackageManager();
List apps = new ArrayList();
List packages = pm.getInstalledApplications(PackageManager.GET_META_DATA);
Чем является “context”?
Это локальная переменная, Вы можете передавать контекст из активити или фрагмента
” List packages = pm.getInstalledApplications( ”
а есть ли функция наподобие getRunnedApplications(), которая выдает список запущенных последних приложений?
как отличить приложение от сервиса? Проверка на системное приложение не помогает