IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Vous êtes nouveau sur Developpez.com ? Créez votre compte ou connectez-vous afin de pouvoir participer !

Vous devez avoir un compte Developpez.com et être connecté pour pouvoir participer aux discussions.

Vous n'avez pas encore de compte Developpez.com ? Créez-en un en quelques instants, c'est entièrement gratuit !

Si vous disposez déjà d'un compte et qu'il est bien activé, connectez-vous à l'aide du formulaire ci-dessous.

Identifiez-vous
Identifiant
Mot de passe
Mot de passe oublié ?
Créer un compte

L'inscription est gratuite et ne vous prendra que quelques instants !

Je m'inscris !

Le gouvernement australien publie le code source pour Android et iOS de l'application CovidSafe,
Qui serait moins efficace sur les iPhone

Le , par Nancy Rey

349PARTAGES

6  0 
Le déploiement des applications de suivi de contacts, afin d’accompagner de façon efficace le déconfinement des populations, est l’actualité du moment. C’est notamment le cas de la Chine, de la Corée du Sud, de l’Allemagne, de l’Iran, et même de la France avec l’application StopCovid qui devrait être disponible dès le 2 juin prochain. L’Australie a lancé la sienne dénommée CovidSafe le mois dernier. Il y a eu une frénésie médiatique énorme sur la publication ou non du code source de l'application par le gouvernement australien. Maintenant, c’est chose faite, le dépôt GitHub contenant le code de l'application Android et iOS est désormais accessible au public.

Le code qui fait tourner l'application sur les téléphones est maintenant divulgué pour s'assurer qu'il n'y a pas de vulnérabilités.

Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package au.gov.health.covidsafe 
 
import android.app.Application 
import android.content.Context 
import android.os.Build 
import com.atlassian.mobilekit.module.feedback.FeedbackModule 
 
import au.gov.health.covidsafe.logging.CentralLog 
import au.gov.health.covidsafe.services.BluetoothMonitoringService 
import au.gov.health.covidsafe.streetpass.CentralDevice 
import au.gov.health.covidsafe.streetpass.PeripheralDevice 
 
class TracerApp : Application() { 
 
override fun onCreate() { 
super.onCreate() 
AppContext = applicationContext 
FeedbackModule.init(this) 
} 
 
companion object { 
 
private const val TAG = "TracerApp" 
const val ORG = BuildConfig.ORG 
const val protocolVersion = BuildConfig.PROTOCOL_VERSION 
 
lateinit var AppContext: Context 
 
fun thisDeviceMsg(): String { 
BluetoothMonitoringService.broadcastMessage?.let { 
CentralLog.i(TAG, "Retrieved BM for storage: $it") 
return it 
} 
 
CentralLog.e(TAG, "No local Broadcast Message") 
return BluetoothMonitoringService.broadcastMessage!! 
} 
 
fun asPeripheralDevice(): PeripheralDevice { 
return PeripheralDevice(Build.MODEL, "SELF") 
} 
 
fun asCentralDevice(): CentralDevice { 
return CentralDevice(Build.MODEL, "SELF") 
} 
 
} 
}
Version Android de l’application de suivi développée en langage Kotlin


Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
// 
// CentralController.swift 
// CovidSafe 
// 
// Copyright © 2020 Australian Government. All rights reserved. 
// 
import Foundation 
import CoreData 
import CoreBluetooth 
import UIKit 
 
struct CentralWriteData: Codable { 
var modelC: String // phone model of central 
var rssi: Double 
var txPower: Double? 
var msg: String // tempID 
var org: String 
var v: Int 
} 
 
class CentralController: NSObject { 
enum CentralError: Error { 
case centralAlreadyOn 
case centralAlreadyOff 
} 
var centralDidUpdateStateCallback: ((CBManagerState) -> Void)? 
var characteristicDidReadValue: ((EncounterRecord) -> Void)? 
private let restoreIdentifierKey = "com.joelkek.tracer.central" 
private var central: CBCentralManager? 
private var recoveredPeripherals: [CBPeripheral] = [] 
private var queue: DispatchQueue 
 
// This dict is to keep track of discovered android devices, so that i do not connect to the same android device multiple times within the same BluetraceConfig.CentralScanInterval 
private var discoveredAndroidPeriManufacturerToUUIDMap = [Data: UUID]() 
 
// This dict has 2 purpose 
// 1. To store all the EncounterRecord, because the RSSI and TxPower is gotten at the didDiscoverPeripheral delegate, but the characterstic value is gotten at didUpdateValueForCharacteristic delegate 
// 2. Use to check for duplicated iphones peripheral being discovered, so that i dont connect to the same iphone again in the same scan window 
private var scannedPeripherals = [UUID: (peripheral: CBPeripheral, encounter: EncounterRecord)]() // stores the peripherals encountered within one scan interval 
var timerForScanning: Timer? 
 
public init(queue: DispatchQueue) { 
self.queue = queue 
super.init() 
} 
 
func turnOn() { 
DLog("CC requested to be turnOn") 
guard central == nil else { 
return 
} 
central = CBCentralManager(delegate: self, queue: self.queue, options: [CBCentralManagerOptionRestoreIdentifierKey: restoreIdentifierKey, CBCentralManagerOptionShowPowerAlertKey: 1]) 
} 
 
func turnOff() { 
DLog("CC turnOff") 
guard central != nil else { 
return 
} 
central?.stopScan() 
central = nil 
} 
 
public func getState() -> CBManagerState? { 
return central?.state 
} 
 
public func getDiscoveredPeripheralsCount() -> Int { 
let COUNT_NOT_FOUND = -1 
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { 
return COUNT_NOT_FOUND 
} 
let managedContext = appDelegate.persistentContainer.viewContext 
let fetchRequest = NSFetchRequest<Encounter>(entityName: "Encounter") 
let sortByDate = NSSortDescriptor(key: "timestamp", ascending: false) 
fetchRequest.sortDescriptors = [sortByDate] 
let fetchedResultsController = NSFetchedResultsController<Encounter>(fetchRequest: fetchRequest, managedObjectContext: managedContext, sectionNameKeyPath: nil, cacheName: nil) 
 
do { 
try fetchedResultsController.performFetch() 
return fetchedResultsController.fetchedObjects?.count ?? COUNT_NOT_FOUND 
} catch let error as NSError { 
print("Could not perform fetch. \(error), \(error.userInfo)") 
return COUNT_NOT_FOUND 
} 
} 
} 
 
extension CentralController: CBCentralManagerDelegate { 
 
func centralManager(_ central: CBCentralManager, willRestoreState dict: [String : Any]) { } 
 
func centralManagerDidUpdateState(_ central: CBCentralManager) { 
centralDidUpdateStateCallback?(central.state) 
switch central.state { 
case .poweredOn: 
DispatchQueue.main.async { 
self.timerForScanning = Timer.scheduledTimer(withTimeInterval: TimeInterval(BluetraceConfig.CentralScanInterval), repeats: true) { _ in 
DLog("CC Starting a scan") 
Encounter.timestamp(for: .scanningStarted) 
 
// for all peripherals that are not disconnected, disconnect them 
self.scannedPeripherals.forEach { (scannedPeri) in 
central.cancelPeripheralConnection(scannedPeri.value.peripheral) 
} 
// clear all peripherals, such that a new scan window can take place 
self.scannedPeripherals = [UUID: (CBPeripheral, EncounterRecord)]() 
self.discoveredAndroidPeriManufacturerToUUIDMap = [Data: UUID]() 
 
central.scanForPeripherals(withServices: [BluetraceConfig.BluetoothServiceID]) 
DispatchQueue.global().asyncAfter(deadline: .now() + .seconds(BluetraceConfig.CentralScanDuration)) { 
DLog("CC Stopping a scan") 
central.stopScan() 
Encounter.timestamp(for: .scanningStopped) 
} 
} 
self.timerForScanning?.fire() 
} 
default: 
timerForScanning?.invalidate() 
} 
} 
 
func handlePeripheralOfUncertainStatus(_ peripheral: CBPeripheral) { 
// If not connected to Peripheral, attempt connection and exit 
if peripheral.state != .connected { 
DLog("CC handlePeripheralOfUncertainStatus not connected") 
central?.connect(peripheral) 
return 
} 
// If don't know about Peripheral's services, discover services and exit 
if peripheral.services == nil { 
DLog("CC handlePeripheralOfUncertainStatus unknown services") 
peripheral.discoverServices([BluetraceConfig.BluetoothServiceID]) 
return 
} 
// If Peripheral's services don't contain targetID, disconnect and remove, then exit. 
// If it does contain targetID, discover characteristics for service 
guard let service = peripheral.services?.first(where: { $0.uuid == BluetraceConfig.BluetoothServiceID }) else { 
DLog("CC handlePeripheralOfUncertainStatus no matching Services") 
central?.cancelPeripheralConnection(peripheral) 
return 
} 
DLog("CC handlePeripheralOfUncertainStatus discoverCharacteristics") 
peripheral.discoverCharacteristics([BluetraceConfig.BluetoothServiceID], for: service) 
// If Peripheral's service's characteristics don't contain targetID, disconnect and remove, then exit. 
// If it does contain targetID, read value for characteristic 
guard let characteristic = service.characteristics?.first(where: { $0.uuid == BluetraceConfig.BluetoothServiceID}) else { 
DLog("CC handlePeripheralOfUncertainStatus no matching Characteristics") 
central?.cancelPeripheralConnection(peripheral) 
return 
} 
DLog("CC handlePeripheralOfUncertainStatus readValue") 
peripheral.readValue(for: characteristic) 
return 
} 
 
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) { 
let debugLogs = ["CentralState": BluetraceUtils.centralStateToString(central.state), 
"peripheral": peripheral, 
"advertisments": advertisementData as AnyObject] as AnyObject 
 
DLog("\(debugLogs)") 
 
// iphones will "mask" the peripheral's identifier for android devices, resulting in the same android device being discovered multiple times with different peripheral identifier. Hence Android is using use CBAdvertisementDataServiceDataKey data for identifying an android pheripheral 
if let manuData = advertisementData[CBAdvertisementDataManufacturerDataKey] as? Data { 
let androidIdentifierData = manuData.subdata(in: 2..<manuData.count) 
if discoveredAndroidPeriManufacturerToUUIDMap.keys.contains(androidIdentifierData) { 
DLog("Android Peripheral \(peripheral) has been discovered already in this window, will not attempt to connect to it again") 
return 
} else { 
peripheral.delegate = self 
discoveredAndroidPeriManufacturerToUUIDMap.updateValue(peripheral.identifier, forKey: androidIdentifierData) 
scannedPeripherals.updateValue((peripheral, EncounterRecord(rssi: RSSI.doubleValue, txPower: advertisementData[CBAdvertisementDataTxPowerLevelKey] as? Double)), forKey: peripheral.identifier) 
central.connect(peripheral) 
} 
} else { 
// Means not android device, i will check if the peripheral.identifier exist in the scannedPeripherals 
DLog("CBAdvertisementDataManufacturerDataKey Data not found. Peripheral is likely not android") 
if scannedPeripherals[peripheral.identifier] == nil { 
peripheral.delegate = self 
scannedPeripherals.updateValue((peripheral, EncounterRecord(rssi: RSSI.doubleValue, txPower: advertisementData[CBAdvertisementDataTxPowerLevelKey] as? Double)), forKey: peripheral.identifier) 
central.connect(peripheral) 
} else { 
DLog("iOS Peripheral \(peripheral) has been discovered already in this window, will not attempt to connect to it again") 
} 
} 
} 
 
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) { 
let peripheralStateString = BluetraceUtils.peripheralStateToString(peripheral.state) 
DLog("CC didConnect peripheral peripheralCentral state: \(BluetraceUtils.centralStateToString(central.state)), Peripheral state: \(peripheralStateString)") 
peripheral.delegate = self 
peripheral.discoverServices([BluetraceConfig.BluetoothServiceID]) 
} 
 
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) { 
DLog("CC didDisconnectPeripheral \(peripheral) , \(error != nil ? "error: \(error.debugDescription)" : "" )") 
} 
 
func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) { 
DLog("CC didFailToConnect peripheral \(error != nil ? "error: \(error.debugDescription)" : "" )") 
} 
} 
 
extension CentralController: CBPeripheralDelegate { 
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) { 
if let err = error { 
DLog("error: \(err)") 
} 
guard let service = peripheral.services?.first(where: { $0.uuid == BluetraceConfig.BluetoothServiceID }) else { return } 
peripheral.discoverCharacteristics([BluetraceConfig.BluetoothServiceID], for: service) 
} 
 
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) { 
if let err = error { 
DLog("error: \(err)") 
} 
guard let characteristic = service.characteristics?.first(where: { $0.uuid == BluetraceConfig.BluetoothServiceID}) else { return } 
peripheral.readValue(for: characteristic) 
 
// Do not need to wait for a successful read before writing, because no data from the read is needed in the write 
if let currEncounter = scannedPeripherals[peripheral.identifier] { 
EncounterMessageManager.shared.getTempId { (result) in 
guard let tempId = result else { 
DLog("broadcast msg not present") 
return 
} 
guard let rssi = currEncounter.encounter.rssi else { 
DLog("rssi should be present in \(currEncounter.encounter)") 
return 
} 
 
let dataToWrite = CentralWriteData(modelC: DeviceIdentifier.getModel(), 
rssi: rssi, 
txPower: currEncounter.encounter.txPower, 
msg: tempId, 
org: BluetraceConfig.OrgID, 
v: BluetraceConfig.ProtocolVersion) 
 
do { 
let encodedData = try JSONEncoder().encode(dataToWrite) 
peripheral.writeValue(encodedData, for: characteristic, type: .withResponse) 
} catch { 
DLog("Error: \(error)") 
} 
} 
} 
 
} 
 
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) { 
let debugLogs = ["characteristic": characteristic as AnyObject, 
"encounter": scannedPeripherals[peripheral.identifier] as AnyObject] as AnyObject 
 
DLog("\(debugLogs)") 
if error == nil { 
if let scannedPeri = scannedPeripherals[peripheral.identifier], 
let characteristicValue = characteristic.value { 
do { 
let peripheralCharData = try JSONDecoder().decode(PeripheralCharacteristicsData.self, from: characteristicValue) 
var encounterStruct = scannedPeri.encounter 
encounterStruct.msg = peripheralCharData.msg 
encounterStruct.update(modelP: peripheralCharData.modelP) 
encounterStruct.org = peripheralCharData.org 
encounterStruct.v = peripheralCharData.v 
scannedPeripherals.updateValue((scannedPeri.peripheral, encounterStruct), forKey: peripheral.identifier) 
encounterStruct.saveToCoreData() 
} catch { 
DLog("Error: \(error). CharacteristicValue is \(characteristicValue)") 
} 
} else { 
DLog("Error: scannedPeripherals[peripheral.identifier] is \(String(describing: scannedPeripherals[peripheral.identifier])), characteristic.value is \(String(describing: characteristic.value))") 
} 
} else { 
DLog("Error: \(error!)") 
} 
} 
 
func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) { 
DLog("didWriteValueFor to peripheral: \(peripheral), for characteristics: \(characteristic). \(error != nil ? "error: \(error.debugDescription)" : "" )") 
central?.cancelPeripheralConnection(peripheral) 
} 
}
Code source du contrôleur central de l’application iOS développée en Swift


CovidSafe fonctionne grâce au bluetooth : lorsqu’une personne en croise une autre ayant également téléchargé l’application, alors son smartphone va enregistrer ces données. Si l’un des individus avec lesquels cette personne est entrée en contact est testé positif, un professionnel de santé la contactera afin de lui indiquer la marche à suivre.


Quelques heures après sa publication sur l'App Store et Google Play, plus de 5 millions d'Australiens avaient téléchargé Covidsafe. Problème, cette application ne fonctionne pas de la même manière sous Android et sous iOS. Randall Brugeaud, directeur général de la Digital Transformation Agency (DTA), l'agence en charge du déploiement de CovidSafe, reconnaît certaines limites techniques : « ce que nous pouvons dire, c’est que la qualité de la connectivité bluetooth pour les téléphones sur lesquels l’application est installée et tourne au premier plan est très bonne. Puis, elle se détériore progressivement et la qualité de la connexion est moins bonne lorsque l’application tourne en arrière-plan ».

Dans la pratique, les utilisateurs des iPhone doivent toujours avoir leur smartphone ouvert sur l’application s’ils veulent que cette dernière enregistre chaque personne qu’ils ont croisée. Ce bug est encore plus prononcé s’il s’agit d’un ancien iPhone. Les possesseurs de téléphones Android, eux, n’ont qu’à avoir l’application en arrière-plan pour que cette dernière soit efficace.

Une mise à jour de l’application est prévue dans les jours à venir, afin de rectifier le tir. Selon Randall Brugeaud, il faudra attendre l'intégration du protocole développé en commun par Apple et Google pour que le fonctionnement de l'application soit amélioré. Ce sera fait dès que possible, le responsable annonçant que l'Australie sera l'un des premiers pays au monde à intégrer ce protocole.

L'application stockera pendant 21 jours les données des personnes avec lesquelles l’utilisateur aura été en contact, et celles-ci seront conservées sur son téléphone pendant cette période. Ces informations sont stockées de manière chiffrée sur un serveur gouvernemental, puis transmises aux autorités sanitaires de l'État et du territoire dans le cas où une personne avec laquelle vous avez été en contact a été testée positive.

C'est Amazon Web Services qui a été sélectionné pour gérer ces données dans le cadre d'un premier contrat à 700 000 dollars. Ce sont des serveurs d’Amazon qui stockent les données récoltées par l’application, suscitant des craintes de la part des citoyens australiens de voir leurs informations personnelles entre les mains du gouvernement américain.

Sources : GitHub, DTA

Et vous ?

Que pensez-vous du fonctionnement de CovidSafe ?
Que pensez-vous du choix d'Amazon Web Services pour héberger les données de CovidSafe ?

Voir aussi :

Apple et Google lancent un outil commun de traçage du COVID-19 pour iOS et Android, il se traduira d'abord par une API, puis un outil intégré nativement aux deux systèmes

Le Contrôleur européen de la protection des données appelle à la mise en oeuvre d'une application mobile paneuropéenne pour suivre le COVID-19. L'initiative devrait lancer sa plateforme cette semaine

Covid-19 : faut-il pister les téléphones pour cibler les lieux de rassemblements ? Le PM canadien n'écarte pas l'idée d'aller vers un « totalitarisme sanitaire », d'après les défenseurs des libertés

StopCovid : la France travaille sur une application qui va tracer l'historique des contacts avec les malades, mais la piste de la géolocalisation est écartée

Une erreur dans cette actualité ? Signalez-nous-la !