Covid-19. Наверное, каждый слышал об этом вирусе. Я сейчас не берусь описывать свое личное отношение или обсуждать теории заговоров вокруг этого. Лично для меня это реальный кейс с близкими, которые заболели.
Столкнувшись с ковид лицом к лицу, наша команда решила внести свой вклад в борьбу с этим злом.
Мы разработали мобильное приложение, которое позволяет отслеживать местоположение опасных районов с ковид, с использованием данных о локации и по Bluetooth.
В отличие от аналогов, мы хотели сделать всё анонимно и добровольно. Без использования регистрации, привязки номера телефона и email.
Без отправки данных во все возможные инстанции.
Идея была проста – чтобы каждый мог отправить свой статус здоровья и поделиться им с другими участниками добровольно. Чтобы пользователи могли видеть на карте зараженные маршруты и получать уведомления после пересечения с заболевшими. Интегрировали туда индекс заражения района и множество других, на наш взгляд, полезных идей.
Технических сложностей было много, начиная от работы локации в фоновом режиме и заканчивая обменом данных по Bluetooth без подключения.
Так как Bluetooth может вызвать много вопросов и, на наш взгляд, наиболее интересен, поделюсь хитрым решением с вами. Может кому-то пригодится в проектах.
Мы использовали Bluetooth одновременно как Beacon-маячок и как сканер маячков.
Наша задача находить контакты с заболевшими людьми рядом. Так как у нас анонимное приложение и у каждого пользователя есть только обезличенный ID, его мы и решили вещать по Bluetooth.
В Android для этого мы создали foreground службу BluetoothMonitorService. Эта служба должна вещать наш ID по Bluetooth и сканировать местность рядом на наличие других ID. В обычной службе операционная система не даст работать вашему приложению и, к сожалению, должно быть постоянное уведомление в трее.
Вторым важным моментом на этом этапе требуется наличие разрешения:
Manifest.permission.ACCESS_COARSE_LOCATION.
Так что локацию запрашивать придется. Без этого разрешения вам будет приходить пустой список.
Ну и конечно:
android.permission.BLUETOOTH
android.permission.BLUETOOTH_ADMIN
И так, начнем.
Для начала запустим вещание:
private void StartAdvertisingLegacy(long timeoutInMillis, int id) {
byte[] bytesPID = ByteBuffer.allocate(charLength).putInt(id).array();
data = new AdvertiseData.Builder()
.setIncludeDeviceName(false)
.setIncludeTxPowerLevel(false)
.addServiceUuid(pUuid)
.addManufacturerData(9, bytesPID)
.build();
try {
Log.d(TAG, "Start advertising");
advertiser = (advertiser != null) ? advertiser : BluetoothAdapter.getDefaultAdapter().getBluetoothLeAdvertiser();
advertiser.startAdvertising(settings, data, advertisingCallback);
} catch (Exception e) {
Log.d(TAG, "Failed to start advertising legacy: ${e.message}");
}
}
Как можно заметить ID пользователя мы разместили в addManufacturerData.
Будьте внимательны, размер пакета ограничен 31 байтом. Отключите отображение имени и информацию об уровень мощности, чтобы хватило на ваш Payload.
addServiceUuid – это 128-битный уникальный идентификатор атрибута. Уникальный UUID для нашего приложения, по которому можно будет отфильтровать девайс при сканировании.
После этого слушаем ответ:
AdvertiseCallback advertisingCallback = new AdvertiseCallback() {
@Override
public void onStartSuccess(AdvertiseSettings settingsInEffect) {
super.onStartSuccess(settingsInEffect);
Log.i(TAG, "Advertising onStartSuccess");
Log.i(TAG, settingsInEffect.toString());
isAdvertising = true;
}
@Override
public void onStartFailure(int errorCode) {
super.onStartFailure(errorCode);
String reason;
switch (errorCode) {
case ADVERTISE_FAILED_ALREADY_STARTED:
reason = "ADVERTISE_FAILED_ALREADY_STARTED";
isAdvertising = true;
break;
case ADVERTISE_FAILED_FEATURE_UNSUPPORTED:
reason = "ADVERTISE_FAILED_FEATURE_UNSUPPORTED";
isAdvertising = false;
break;
case ADVERTISE_FAILED_INTERNAL_ERROR:
reason = "ADVERTISE_FAILED_INTERNAL_ERROR";
isAdvertising = false;
break;
case ADVERTISE_FAILED_TOO_MANY_ADVERTISERS:
reason = "ADVERTISE_FAILED_TOO_MANY_ADVERTISERS";
isAdvertising = false;
break;
case ADVERTISE_FAILED_DATA_TOO_LARGE:
reason = "ADVERTISE_FAILED_DATA_TOO_LARGE";
isAdvertising = false;
charLength--;
break;
default:
reason = "UNDOCUMENTED";
}
Log.e(TAG, "Advertising onStartFailure: " + errorCode + " " + reason);
}
};
Параллельно запускаем наш сканер:
public void StartScan(ScanCallback scanCallback) {
ScanFilter filter = new ScanFilter.Builder()
.setServiceUuid(new ParcelUuid(UUID.fromString(serviceUUID)))
.build();
List<ScanFilter> filters = new ArrayList<>();
filters.add(filter);
ScanSettings settings = new ScanSettings.Builder()
.setReportDelay(reportDelay)
.setScanMode(ScanSettings.SCAN_MODE_LOW_POWER)
.build();
this.scanCallback = scanCallback;
//try to get a scanner if there isn't anything
scanner = (scanner != null) ? scanner : BluetoothAdapter.getDefaultAdapter().getBluetoothLeScanner();
scanner.startScan(filters, settings, scanCallback);
Log.d(TAG, "scanning started");
}
ServiceUUID должен быть такой же, как при вещании, иначе вы не будете получать вообще никакой информации. Он уникален для вашего приложения. Сгенерировать можно вот тут https://www.uuidgenerator.net.
В самой службе следует установить удобные интервалы опроса, у нас это раз в 30 секунд. Это позволяет нам беречь заряд батареи.
Приложение получилось не прожорливым. Даже с учетом работы локации, находится в конце списка по потреблению.
После нескольких месяцев разработки появились iOS и Android версии приложения. Мы отправили их на проверку в Google и Apple.
И вот тут началось самое интересное, оказывается разработать решение – это самое лёгкое. Сложности начались с размещением в сторах.
В Google у нас получилось пройти, предоставив бумагу официальных гос. органов, что они не против приложения. Получить её в России само по себе подвиг, но это уже другая история.
С Apple была тишина долгое время. Мы писали повторно, оправляли им разрешение гос. органов, но ответа не было.
И вот, в один прекрасный день, звонок. Мне позвонили из Apple! Я так переволновался, что забыл английский язык. В Apple были крайне вежливы и после пяти минут общения сказали, что найдут переводчика.
Через некоторое время, мне позвонили опять и уже на русском с акцентом стали спрашивать о приложении.
Сказали, что их медицинский департамент заинтересовался нашим приложением, что без их одобрения мы не пройдем проверку. Что если мы ответим на их вопросы, они готовы даже посотрудничать. После этого мы какое-то время покорно отвечали на все их вопросы и ждали решения и долгожданного сотрудничества.
Но чтобы не терять времени, мы решили рассказать о приложении нашим властям, чтобы показать альтернативу и возможность не принуждать людей как стадо, а дать им выбор и добровольный инструмент.
Отправляя письма во все инстанции, мы получали один ответ – полное отсутствие ответа.
Минздрав России мы решили атаковать активнее как Энди Дюфрейн. Но Минздрав был непробиваем, нам даже Госдепартамент США ответил на обращение, а вот Минздрав…
Нашему удивлению не было предела, когда Apple выпустила своё API ExposureNotification с формулировками точь-в-точь, повторяющими наши! Может быть это совпадение, но это настораживает и заставляет задуматься.
Хочу обратиться к вам за помощью в оценке приложения, в его распространении и донесении до наших властей, что можно и по-другому. Давайте попробуем вместе что-то поменять!
https://mysafe.ai
Google Play
Cost_Estimator
Оставьте пока власти в покое.
Вы продумали ситуацию, когда кто-то шаловливый, или даже группа шаловливых лиц, решит просто так проставить себе статус «I am sick» и пройтись по оживленному маршруту? Или «пометить» все магазины в округе? Помните историю немца с тележкой телефонов? И какой в этом случае толк от вашего приложения?
KivApple
Очевидно, большинство так делать не будет. Немец с тележкой никак не мешает доверять данным о пробках в общем случае. По данным этого приложения никто не будет высылать команду с огнеметами, но сознательные люди могут сделать выводы вроде "а сдам ка я тест" или "лучше пойду другим маршрутом", что в среднем будет уменьшать риски заражения. Для граждан, имеющих иную риск-политику, просто ничего не изменится.
Глючный "социальный мониторинг" выписывающий штрафы на каждый чих по надуманному предлогу гораздо вреднее и опаснее.
Cost_Estimator
Вы слишком доверяете информации, которую невозможно проверить. Давайте разовьем мысль дальше.
приводят к лишним затратам, что будет особенно мерзко, если на маршруте окажется шутник со статусом, но без вируса (ложно-положительное срабатывание). приводят к потере времени, что менее неприятно, но тоже не дает никаких гарантий, ведь на другом маршруте может оказаться зараженный, который не захотел или не успел обновить статус в необязательном приложении.Кроме того, как вы можете заставить установить приложение хотя бы 1 млн. человек? Если его установит 1000, толку не будет вообще, это просто статистическая погрешность. И среди этой 1000 окажется 10 тех самых шутников, которые будут портить кровь остальным. Не в прямом смысле, конечно. Но что если среди пользователей окажется человек более впечатлительный, и информация о наличии рядом зараженных доведет его до истерики? Или до приступа — за это готовы ответить? На мой взгляд и такие ситуации надо продумывать заранее, до релиза. Вы делаете не просто игру, а приложение, связанное со здоровьем.
Кто вам гарантирует, что получатель вашего письма в Минздраве не подумал все то, о чем я сейчас пишу, и просто решил не отвечать?
KivApple
Я не автор статьи.
Но сейчас применяется огромное количество спорных решений от обязательных перчаток (про которые много копий сломали и у каждой стороны есть очень убедительные доводы почему это полезно или наоборот только ещё больше распространит вирус) до очередей в метро из-за пропусков и глючных приложений со штрафами. Мне кажется, в таких условиях любые хоть немного адекватные эксперименты (и при этом без обязаловки под страхом штрафов) имеют право на жизнь. А кто был прав покажет опыт. На фоне того сколько людей потеряло работу или сходит с ума в четырёх стенах (а тут тоже возможны истерики и что похуже) потенциальный вред этого приложения ничтожен. А у вас на уме только "кабы чего не вышло". Хаос — время инноваций.
RepinTech
ИМХО ценность такого приложения может быть и для небольших коммьюнити. Например посёлок дачный. Там имеет смысл всем поставить и понимать, кто где ходит.
И можно не воспринимать всё буквально — насчет гарантий и прочего. Это как молоток. Вы делаете молотки. Где гарантия, что ваши молоки купит миллион человек? Где гарантия, что все будут использовать молотки по назначению, а не начнут пробивать ими головы? Минздрав против молотков? (юмор)
Так и тут. Есть инструмент, который может помочь. Дает альтернативу кривым, следящим за всеми, выписывающим штрафы госушным приложениям. Да еще и Аппле спёр часть функционала. Что говорит о правильном направлении мысли создателя приложения. Нет, парень — молодец. Сделал хорошую вещь.
Критика приложения безусловно полезна, но часть этой энергии можно направить на конструктив — предложить как улучшить или что убрать/добавить.
Я вот хочу порекомендовать сделать интеграцию c госуслугами, чтобы пропуска эти дурацкие выписывать удобно было.
Cost_Estimator
Вы действительно считаете, что в дачном поселке люди предпочтут общаться через приложение?
RepinTech
Это приложение не для общения.
Cost_Estimator
Ок, задам вопрос иначе. Вы действительно думаете, что в дачном поселке люди должны знать, кто где ходит через приложение, а не с помощью вездесущих бабулек? И узнавать о состоянии здоровья через приложение, а не «Здорово, Петрович, как сам?» Мне кажется, что локация в качестве примера взята не совсем подходящая.
Насчет критики — есть же вероятность, что автор приложения прочтет, и что-либо из сказанного нам тут натолкнет его на мысль о том, как можно улучшить свою работу.
RepinTech
это какие люди… я вот жил как-то в поселке под Форт Коллинз… там такое приложение точно бы не помешало… даже без вирусов…
nveresha
Совершенно неочевидно. У людей мнения по Covid разнятся от «фигня» до «паника». Поэтому ожидать можно, что угодно.
В небольшом городе число больных составляет до тысячи. И предположим, появился фанатик-идиот с личным мнением на Covid, что маски — это символ рабства, а над рабами стоит издеваться. Объединил кучу народу вокруг себя в онлайне. И акцию замутил: штук сто неадекватных статусов. Изменят всю картину.
Причем приложение имеет защиту от множественных регистраций? Может, кому-то и последователи не понадобятся. Только «духи».
Еще хуже, если фанатики начнут вычислять, кто тут больной. Со всеми вытекающими последствиями. Или не фанатики, а умные люди, как средство нездоровой конкуренции между торговыми точками.
RMiskevich
Защиты от «дурака» нет как таковой. С моей точки зрения это попытка дать людям (сознающим что они делают и несущим ответственность за свои действия) инструмент, которым они могли бы повлиять на ситуацию. В чем заключается альтернатива — сказать что все равно не работает или собрать у всех паспотные данные и зарегистрировать чтобы потом эти данные «нечаянно» утекли в сеть?
Cost_Estimator
Знаете, для меня, как пользователя, все едино. Что социальный мониторинг, работающий плохо потому, что его клепали в спешке, что это приложение. Первый только удалить нельзя, т. к. штрафами задушат.
Здесь, к сожалению, автор начал диалог не сразу, а уже на этапе продвижения. Варианта два — публиковать это как есть, но справедливо будет тогда предупредить пользователей о возможных косяках и их причинах — ведь приложение не застраховано от внесения в него заведомо ложной информации; либо же подумать, какие корректировки можно внести, чтобы оно давало чуть более правдивую картину. Но если автор не хочет обратной связи, а лишь пытается продвинуть свое детище в массы, ему следует принимать на себя и все риски с этим связанные. Он не обязан, но так будет честнее. Оно как атомная энергия, или молоток, с той лишь разницей, что использовавшего молоток во вред найти можно, а вводящего в заблуждение добросовестных пользователей найти будет нельзя, анонимное ведь оно.