- Основы безопасности операционной системы Android. Безопасность на уровне Application Framework. Binder IPC
- Вступление
- Список статей
- Зачем нужен Binder IPC?
- Как работает Binder IPC?
- Service Manager
- Безопасность
- Заключение
- Русские Блоги
- Подробное объяснение Binder в Android
- Введение в Binder
- Что такое Binder
- Сценарии использования Binder
- Анализ структуры связующего
- Отношения между AIDL и Binder
- Создать файл Binder AIDL
- Общая структура Binder
- Детальный разбор методов в Binder
- Полный код вышеуказанного Binder
- Использование android.os.Binder для организации асинхронного взаимодействия в Андроиде
Основы безопасности операционной системы Android. Безопасность на уровне Application Framework. Binder IPC
Вступление
После небольшого перерыва я продолжаю объяснять базовые принципы как обеспечивается безопасность в операционной системе Android. Сегодня я начну описывать безопасность на уровне Application Framework. Но чтобы понять данную тему, вначале необходимо рассмотреть как в Android реализован механизм межпроцессного взаимодействия (Inter-Process Communication (IPC)). Этот механизм называется Binder IPC, и сегодня мы будем рассматривать его особенности. Все, кому интересно, добро пожаловать!
Список статей
Зачем нужен Binder IPC?
Как я уже писал в первой статье цикла, каждое приложение в Android выполняется в своей собственной «песочнице» (Application Sandbox). Механизм «песочницы» в Android основан на присвоении каждому приложению уникального user ID (UID) и group ID (GID), таким образом каждому приложению (application или app) в этой операционной системе соответсвует свой уникальный непривилегированный пользователь. Мы рассматривали эту тему в первой статье цикла. В тоже время владельцами всех критических ресурсов системы являются более привилегированные пользователи, имена и идентификаторы которых жестко зашиты в систему (см. system/core/include/private/android_filesystem_config.h). Системные сервисы, которые запускаются от имени этих пользователей, имеют доступ к соответствующим критическим ресурсам системы (например, к GPS данным), в то время как процессы обычных приложений доступ к этим ресурсам получить не могут.
Однако приложения должны иметь возможность «попросить» системные сервисы предоставить им такую информацию, а последние, в свою очередь, должны иметь возможность предоставить такую информацию приложениям. Так как приложения и системные сервисы исполняются в разных процессах, то для организации такого обмена операционной системе необходимо предоставить механизм обмена информацией между процессами. Более того, даже обычные приложения иногда должны взаимодействовать друг с другом. Например, почтовый клиент может «попросить» просмотрщик изображений отобразить графическое приложение к письму.
В Android обмен сигналами и данными между процессами организован с помощью фреймворка межпроцессного взаимодействия (Inter-Process Communication) Binder. Стандартный System V IPC фреймворк не поддерживается данной операционной системой, в частности, в Bionic libc отсутствуют заголовочные файлы , , , . В некоторых специфических случаях (например, для взаимодействия с демоном Zygote) используются Unix domain sockets. Все остальные способы взаимодействия между процессами, включая Intents, реализованы используя Binder IPC. Этот фреймворк позволяет синхронно и асинхронно вызывать методы удаленных объектов так, будто они локальные, обмениваться файловыми дескрипторами между процессами, link to death (автоматическое оповещение в случае если Binder определенного процесса был прерван), и т.д.
Как работает Binder IPC?
Взаимодействие между процессами организовано по синхронной клиент-серверной модели. Клиент инициирует соединение и ждет ответа со стороны сервера. Таким образом, взаимодействие между клиентом и сервером происходит последовательно. Такой дизайн позволяет разработчику вызывать удаленные методы словно они находятся в том же самом процессе. Стоит отметить, что это не рассходится с тем, что я писал выше по поводу асинхронного вызова методов: в случае асинхронного вызова, сервер сразу же возвращает пустой ответ клиенту. Схема взаимодействия процессов через Binder представлена на следующем рисунке: приложение (клиент), которое выполняется в процессе Process A, получает доступ к функцианальности, реализованной в сервисе, который выполняется в процессе Process B.
Все взаимодействия между клиентом и сервером в рамках Binder происходят через специальный Linux device driver (драйвер устройства) /dev/binder. В статье «Основы безопасности операционной системы Android. Native user space, ч.1» мы рассматривали процесс загрузки системы. Один из первых демонов, запускаемых процессом init, является ueventd — менеджер внешних устройств в Android. Этот сервис во время старта читает конфигурационный файл ueventd.rc и проигрывает события добавления внешних устройств. Эти события выставляют для устройств разрешения (permissions), а также владельца (owner) и группу (owning group). В следующем примере можно увидеть какие разрешения выставлены для /dev/binder.
Как можно заметить, разрешения для этого устройства выставлены в 0666. Это означает, что любой пользователь системы (а мы помним, что разные приложения в Android — это уникальные пользователи) имеет право писать в и читать из данного устройства. Для взаимодействия с этим драйвером была создана библиотека libbinder. Эта библиотека позволяет сделать процесс взаимодействия с драйвером прозрачным для разработчика приложений. В частности, взаимодействие между клиентом и сервером происходит через proxy (прокси) на стороне клиента и stub на стороне сервера (см. рисунок выше). Proxies и Stubs отвечают за маршалинг данных и комманд передаваемых через драйвер. Т.е. Proxy выстраивает (marshalling) данные и команды, полученные со стороны клиента таким образом, что они могут быть корректно прочитаны (unmarshalling) и однозначно поняты Stub’ом. Вообще, разработчики приложений даже не пишут Stub’ы и Proxy’и для своих приложений. Вместо этого они используют интерфейс, описанный с помощью языка AIDL. Во время компиляции приложения, на основании этого интерфейса генерируется код для Stub’ов и Proxy’и (можете поискать в своих проектах, чтобы увидеть как они выглядят).
На стороне сервера для каждого клиентского запроса создается отдельный Binder поток (см. рисунок выше). Обычно эти потоки называются как Binder Thread #n. Кстати, если мне не изменяет память, то максимальное количество Binder потоков равно 255, а максимальный размер данных, которые могут быть переданы через Binder в рамках одной транзакции составляет 1Mb. Это следует иметь ввиду, когда разрабатываете приложения, передающие или получающие данные от других процессов.
Service Manager
Внимательные читатели должны были отметить для себя тот факт, что до этого я ничего не писал о том, как Android понимает, какому процессу нужно отсылать данные. Технически каждому сервису Binder присваивает 32 битный токен, являющимся уникальным в рамках всех процессов системы (за чем следит драйвер). Этот токен используется как указатель на сервис. Если у клиента есть этот указатель, то он может взаимодействовать с сервисом. Но сначала клиенту необходимо получить это уникальное значение.
Для этого клиент обращается к Binder context manager, который в случае Android называется Service Manager (servicemanager). Service Manager — специальный сервис, Binder токен которого известен всем заранее. Не удивительно, что значение токена для этого сервиса равно 0. Binder драйвер разрешает регистрацию только одного сервиса с таким токеном, поэтому servicemanager — один из первых сервисов, запускаемых в системе. Service Manager можно представить в виде справочника. Вы говорите, что хотите найти сервис с таким-то именем, а вам в ответ возвращают его уникальный токен-номер, который вы можете использовать после для взаимодействия с искомым сервисом. Естественно, сервис сперва должен зарегистрироваться в этом «справочнике».
Безопасность
Сам по себе Binder фреймворк не отвечает за безопасность в системе, но он предоставляет механизмы для её обеспечения. Во-первых, Binder драйвер в каждую транзакцию записывает PID и UID процесса, который инициирует транзакцию. Вызываемый сервис может использовать эту информацию чтобы решить, стоит ли выполнять запрос или нет. Вы можете получить эту информацию используя методы android.os.Binder.getCallingUid(), android.os.Binder.getCallingPid(). Во-вторых, так как Binder токен уникален в рамках системы и его значение не известно априори, то он сам может использоваться как маркер безопасности (смотрите, например, вот эту статью).
Заключение
В следующей части планирую написать о разрешениях (permissions). Материал уже есть, но надо его перевести и подработать. Как всегда буду благодарен за интересные вопросы и пояснения в комментариях, возможно некоторые моменты я неправильно для себя понял.
Источник
Русские Блоги
Подробное объяснение Binder в Android
Введение в Binder
Поскольку Binder играет более важную роль в передаче информации Android, для записи анализа Binder написана отдельная статья.
Что такое Binder
Binder, что переводится как связующее, часто появляется в знаниях о межпроцессном взаимодействии Android. Вообще говоря, Binder обычно имеет следующие объяснения:
- Binder — это класс в Android, который реализует интерфейс IBinder.
- Binder — это уникальный для Android метод межпроцессного взаимодействия.
- Binder — это виртуальное физическое устройство, которое можно использовать для соединения клиента и сервера.
Заимствовать великого богаCarson_HoИзображение для его представления выглядит следующим образом:
В сочетании с приведенным выше изображением каждый должен иметь более четкое определение Binder.
Сценарии использования Binder
Связывание в основном используется в Службе, включая AIDL и Мессенджер. Связующее в обычной Службе не предполагает взаимодействия между процессами, поэтому это относительно просто. Базовая реализация Messenger — это AIDL, поэтому анализ Binder в AIDL может помочь нам понять принцип работы Binder.
Анализ структуры связующего
Отношения между AIDL и Binder
После записи файла AIDL система сгенерирует файл java, который наследует интерфейс IInterface во время сборки. Это имя файла совпадает с соответствующим именем файла AIDL. В этом файле есть внутренний класс Stub, это класс Binder.
Так что можно считатьAIDL помогает системе сгенерировать соответствующий файл Binder.
Создать файл Binder AIDL
Затем мы пишем файл AIDL. Содержимое файла AIDL находится в области кода ниже. Если вы хотите понять весь процесс AIDL, вы можете обратиться кЭта статья
Затем должен быть тот, который реализует интерфейс Parcelable в каталоге java.Book.javaкласс
После того, как указанные выше файлы будут записаны, соответствующий файл Binder будет немедленно сгенерирован в AndroidStudio, расположение находится в каталоге проекта. /build/generated/source/aidl/debug/ в. Конкретный путь показан на рисунке:
Если не build Как только сообщается об ошибке, вам нужно еще раз проверить, правильно ли написан помощник или указан правильный путь. В AndroidStudio правильная структура каталогов выглядит следующим образом:
Общая структура Binder
После получения соответствующего файла Binder давайте посмотрим на общую структуру Binder:
Этот класс наследует IInterface Интерфейс, и этот класс также является интерфейсом. Этот интерфейс объявляет два метода, а именно IBookManager.aidl Метод в. Затем объявите внутренний класс Stub. Эта заглушка — класс Binder. Внутри Stub также есть прокси-класс Proxy, который будет прокси-методом клиента в межпроцессном взаимодействии.
Детальный разбор методов в Binder
Сначала рассмотрим различные методы в Stub
Помимо метода построения, в Stub есть методы. asBinder() 、 asInterface() с участием onTransact метод. В дополнение к этим трем методам есть два упомянутых выше статических идентификатора для идентификации метода, вызываемого клиентом.
среди них asBinder() Метод эквивалентен методу get, используемому для возврата текущего объекта Binder, этот код относительно прост, мы его пропускаем.
Давайте взглянем onTransact() Метод — этот метод выполняется на сервере и распределяет конкретный метод для выполнения через код. Значение каждого значения параметра метода можно увидеть в следующих примечаниях:
Затем смотрим asInterface() метод:
Этот метод определит, находятся ли текущий сервер и клиент в одном процессе. Если в том же процессе, он вернет тот же самый объект-заглушку, если в другом процессе, он вернет инкапсулированный клиентский прокси-класс. Stub.proxy , Этот метод будет вызван у клиента, чтобы получить IBookManager Объект.
Тогда посмотрите метод в клиентском прокси Stub.Proxy
Основной метод здесь addBook с участием getBookList Метод, на самом деле, эти два метода все же имеют некоторое сходство, вот анализ getBookList метод
Вкратце, когда клиент вызывает этот метод, он создает параметры сервера _data И принять возврат _reply И возвращаем объект значения _result , А затем напишите информацию о параметрах _data в. Звоните сюда transact Метод инициирует удаленный запрос, в то время как текущий поток приостанавливается, когда заглушка onTransact После выполнения снова вернитесь к текущему методу, из _reply По значению структура _result А потом вернуться _result 。
addBook Процесс метода почти такой же, как и выше, вы можете проверить его в полном коде в конце.
Наконец, посмотрите на метод в IBookManager
За исключением двух внутренних классов выше, оставшийся код имеет только два абстрактных интерфейса, то есть методы интерфейса, определенные в IBookManager.aidl. Об этих двух методах нечего сказать, в основном они используются для наследования или реализации Stub и Stub.Proxy.
Полный код вышеуказанного Binder
Здесь прилагается полный объект Binder, чтобы каждый мог провести общее исследование:
Источник
Использование android.os.Binder для организации асинхронного взаимодействия в Андроиде
Одна из естественных и первых задач при разработке под Андроид – организация асинхронного взаимодействия. Например, обращение к серверу из некоторой активности и отображение на ней результата. Трудность состоит в том, что за время обращения к серверу поверх может быть открыта другая активность или другое приложение, исходная активность может быть безвозвратно завершена (пользователь нажал Back) и т. д. Вот получили мы результат от сервера, но активность «неактивна». Под «активна», в зависимости от обстоятельств, можно понимать, например, что находится между onStart и onStop, onResume и onPause (или, как у нас в проекте, между onPostResume и первым из onSaveInstanceState и onStop). Как понять, завершена активность окончательно (и результат нужно отдать сборщику мусора) или лишь временно неактивна (результат нужно хранить, и отобразить, как только активность станет активной)?
Удивительно, но в документации, интернетах, при личном общении я ни разу не встречал корректного и приемлемо универсального способа. Хочу безвозмездно поделиться решением, которое мы применяем два с половиной года в мобильном интернет-банкинге. Приложение установлено (как часть более крупной системы) у нескольких сотен банков, на данный момент имеет около миллиона пользователей.
Уточним понятия активность и activity record. Активность – это экземпляр класса, короткоживущий объект. Activity record – логическое понятие, экран с точки зрения пользователя, более долгоживущий.
Рассмотрим схему Bottom > Middle > Top.
- Запускаем активность BottomActivity, поверх неё MiddleActivity. При повороте экрана, временном переключении на другое приложение и т. п. активность (экземпляр класса MiddleActivity) может уничтожаться и создаваться новая, но activity record Middle остаётся неизменным. Запускаем TopActivity поверх MiddleActivity, нажимаем кнопку Back. Активность MiddleActivity снова наверху стека, могла быть пересоздана, но activity record Middle всё так же сохраняется неизменным.
- Нажимаем Back – BottomActivity наверху стека. Снова запускаем MiddleActivity. Опять наверху activity record Middle. Но это уже новый activity record, не имеющий отношения к activity record из пункта 1. Тот activity record безвозвратно умер.
Предлагаемое решение основывается на следующем замечательном свойстве android.os.Binder. Если записать Binder в android.os.Parcel, то при чтении в том же процессе (в той же виртуальной машине) гарантированно прочитаем тот же самый экземпляр объекта, который был записан. Соответственно, можно проассоциировать с активностью экземпляр объекта activity record, и сохранять этот объект неизменным с помощью механизма onSaveInstanceState. В асинхронную задачу передаётся объект activity record, в который возвращается результат. Если activity record умирает, то становится доступен сборщику мусора, вместе с результатами работы асинхронных задач.
Для иллюстрации создадим простое приложение «Length». Оно состоит из двух активностей и четырёх инфраструктурных классов.
MenuActivity состоит из одной кнопки, которая запускает LengthActivity.
Работать с Binder напрямую неудобно, так как его нельзя записать в android.os.Bundle. Поэтому обернём Binder в android.os.Parcelable.
Класс IdentityParcelable позволяет передавать через parcel-механизм «ссылки» на объекты. Например, передать в качестве extra (Intent#putExtra) объект, который не является ни Serializable, ни Parcelable, и позже получить (getExtra) тот же экземпляр в другой активности.
Классы ActivityRecord и BasicActivity действуют в связке. ActivityRecord умеет исполнять callback-и. Если активность видна (находится в состоянии между onStart и onStop), то callback исполняется сразу, иначе сохраняется для более позднего исполнения. Когда активность становится видимой, исполняются все отложенные callback-и. При создании activity record (первый вызов BasicActivity#onCreate) создаётся новый объект ActivityRecord, и дальше поддерживается в onSaveInstanceState/onCreate.
На основе ActivityRecord делаем для асинхронных задач базовый класс, похожий контрактом на android.os.AsyncTask.
Теперь, наладив инфраструктуру, делаем LengthActivity. При нажатии на кнопку асинхронно вычисляется длина введённой строки. Заметим, что при повороте экрана вычисление не начинается заново, а продолжается.
Спасибо за внимание! Буду рад услышать комментарии и поучаствовать в обсуждении. Буду счастлив узнать более простое решение, без заморочек с Binder.
UPD: deej подсказал класс android.support.v4.app.BundleCompat, который умеет записывать IBinder в Bundle. Когда разрабатывали решение, этого класса ещё не было. BundleCompat немного упрощает код, позволяя обойтись без IdentityParcelable, одним Binder-ом наподобие
Возможно, IdentityParcelable всё равно может быть полезен, например, для передачи произвольных объектов в Intent в качестве extra, хотя можно обойтись ValueBinder-ом, передавая через Bundle.
Источник