Получение списка приложений в Android

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 возвращает результат обратно в основной поток. Вот и всё, мы загрузили себе список всех установленных на устройстве приложений и можем продолжить с ним работу.

Нашли ошибку в тексте?

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *