createViewManagers(ReactApplicationContext reactContext) {
25 | return Collections.emptyList();
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/android/src/main/java/org/itoapp/strict/Constants.java:
--------------------------------------------------------------------------------
1 | package org.itoapp.strict;
2 |
3 | public class Constants {
4 | public static final int BLUETOOTH_COMPANY_ID = 65535; // TODO get a real company ID!
5 | public static final int UUID_LENGTH = 16;
6 | public static final int HASH_LENGTH = 16;
7 | public static final int BROADCAST_LENGTH = HASH_LENGTH ;
8 | public static final int TCN_VALID_INTERVAL = 1000 * 60 * 15; //ms * sec * 15 min
9 | public static final int CHECK_SERVER_INTERVAL = 1000 * 10; //ms * 10 sec
10 | public static final int DISTANCE_SMOOTHING_MA_LENGTH = 7;
11 | public static final int CACHE_FLUSH_TIME = 1000 * 30; // 30 seconds timeout
12 | public static final long MIN_CONTACT_DURATION = 1000 * 60 * 1; //discard all contacts less than 1 minutes
13 | public static final float MIN_SCANNING_DISTANCE = 20;
14 | public static final long MIN_EXPOSURE_DURATION = 1000 * 60 * 2; // 2 minutes
15 | public static final int RATCHET_EXCHANGE_INTERVAL = (4 * 24); // 1day? if beacon gets changed every 15min
16 | }
17 |
--------------------------------------------------------------------------------
/android/src/main/java/org/itoapp/strict/Helper.java:
--------------------------------------------------------------------------------
1 | package org.itoapp.strict;
2 |
3 | import android.util.Log;
4 |
5 | import java.security.MessageDigest;
6 | import java.security.NoSuchAlgorithmException;
7 | import java.util.Arrays;
8 |
9 | import static org.itoapp.strict.Constants.HASH_LENGTH;
10 |
11 | public class Helper {
12 |
13 | private static final String LOG_TAG = "ITOHelper";
14 | private static MessageDigest sha256MessageDigest;
15 |
16 | private Helper() {
17 | }
18 |
19 | public static byte[] calculateTruncatedSHA256(byte[] uuid) {
20 | if(sha256MessageDigest == null) {
21 | try {
22 | sha256MessageDigest = MessageDigest.getInstance("SHA-256");
23 | } catch (NoSuchAlgorithmException e) {
24 | Log.wtf(LOG_TAG, "Algorithm not found", e);
25 | throw new RuntimeException(e);
26 | }
27 | }
28 |
29 | byte[] sha256Hash = sha256MessageDigest.digest(uuid);
30 | return Arrays.copyOf(sha256Hash, HASH_LENGTH);
31 | }
32 |
33 | public static byte[] hex2Byte(String s) {
34 | int len = s.length();
35 | byte[] data = new byte[len / 2];
36 | for (int i = 0; i < len; i += 2) {
37 | data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
38 | + Character.digit(s.charAt(i+1), 16));
39 | }
40 | return data;
41 | }
42 |
43 | public static String byte2Hex(byte[] in) {
44 | String s = "";
45 | for (byte b : in) {
46 | String st = String.format("%02X", b).toLowerCase();
47 | s += st;
48 | }
49 | return s;
50 | }
51 |
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/android/src/main/java/org/itoapp/strict/Preconditions.java:
--------------------------------------------------------------------------------
1 | package org.itoapp.strict;
2 |
3 | import android.Manifest;
4 | import android.bluetooth.BluetoothAdapter;
5 | import android.bluetooth.BluetoothManager;
6 | import android.content.Context;
7 | import android.content.pm.PackageManager;
8 | import android.location.LocationManager;
9 | import android.os.Build;
10 | import android.util.Log;
11 |
12 | import androidx.core.content.ContextCompat;
13 |
14 |
15 | public class Preconditions {
16 | private static final String LOG_TAG = "ITOPreconditions";
17 |
18 | public static boolean isLocationServiceEnabled(Context context) {
19 | LocationManager locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
20 | assert locationManager != null;
21 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
22 | //I have read this is not actually required on all devices, but I have not found a way
23 | //to check if it is required.
24 | //If location is not enabled the BLE scan fails silently (scan callback is never called)
25 | if (!locationManager.isLocationEnabled()) {
26 | Log.i(LOG_TAG, "Location not enabled (API>=P check)");
27 | return false;
28 | }
29 | } else {
30 | //Not sure if this is the correct check, gps is not really required, but passive provider
31 | //does not seem to be enough
32 | if (!locationManager.getProviders(true).contains(LocationManager.GPS_PROVIDER)) {
33 | Log.i(LOG_TAG, "Location not enabled (API getAll();
17 |
18 | @Insert(onConflict = OnConflictStrategy.REPLACE)
19 | public void saveOrUpdate(LocalKey localKey);
20 |
21 | @Query("DELETE FROM LocalKey")
22 | public void deleteAll();
23 | }
24 |
--------------------------------------------------------------------------------
/android/src/main/java/org/itoapp/strict/database/dao/SeenTCNDao.java:
--------------------------------------------------------------------------------
1 | package org.itoapp.strict.database.dao;
2 |
3 | import org.itoapp.strict.database.entities.SeenTCN;
4 |
5 | import java.util.List;
6 |
7 | import androidx.room.Dao;
8 | import androidx.room.Insert;
9 | import androidx.room.Query;
10 | import androidx.room.Update;
11 |
12 | @Dao
13 | public interface SeenTCNDao {
14 |
15 | @Query("SELECT * FROM SeenTCN WHERE reportedSick=1")
16 | public List findSickTCNs();
17 |
18 | @Query("SELECT * FROM SeenTCN WHERE tcn=:tcn")
19 | public SeenTCN findSeenTCNByHash(String tcn);
20 |
21 | @Insert
22 | public void insert(SeenTCN seenTCN);
23 |
24 | @Update
25 | public void update(SeenTCN seenTCN);
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/android/src/main/java/org/itoapp/strict/database/entities/LastReport.java:
--------------------------------------------------------------------------------
1 | package org.itoapp.strict.database.entities;
2 |
3 | import java.util.Date;
4 |
5 | import androidx.room.ColumnInfo;
6 | import androidx.room.Entity;
7 | import androidx.room.Index;
8 | import androidx.room.PrimaryKey;
9 |
10 |
11 | @Entity(indices = {@Index(value = {"server_url"},
12 | unique = true)})
13 | public class LastReport {
14 | @PrimaryKey(autoGenerate = true)
15 | public long id;
16 |
17 | @ColumnInfo(name="server_url")
18 | public String serverUrl;
19 |
20 | @ColumnInfo(name="lastcheck")
21 | public Date lastcheck;
22 |
23 | @ColumnInfo(name="lastReportHash")
24 | public String lastReportHash;
25 |
26 | @Override
27 | public String toString() {
28 | return "LastReport{" +
29 | "id=" + id +
30 | ", serverUrl='" + serverUrl + '\'' +
31 | ", lastcheck=" + lastcheck +
32 | ", lastReportHash='" + lastReportHash + '\'' +
33 | '}';
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/android/src/main/java/org/itoapp/strict/database/entities/LocalKey.java:
--------------------------------------------------------------------------------
1 | package org.itoapp.strict.database.entities;
2 |
3 | import java.util.Date;
4 |
5 | import androidx.annotation.NonNull;
6 | import androidx.room.ColumnInfo;
7 | import androidx.room.Entity;
8 | import androidx.room.PrimaryKey;
9 |
10 | @Entity
11 | public class LocalKey {
12 |
13 | @PrimaryKey()
14 | @NonNull
15 | public String rak;
16 |
17 | @ColumnInfo(name = "currentTCKpos")
18 | public int currentTCKpos;
19 |
20 | @ColumnInfo(name = "last_generated")
21 | public Date lastGenerated;
22 |
23 | @Override
24 | public String toString() {
25 | return "LocalKey{" +
26 | "rak='" + rak + '\'' +
27 | ", currentTCKpos=" + currentTCKpos +
28 | ", lastGenerated=" + lastGenerated +
29 | '}';
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/android/src/main/java/org/itoapp/strict/database/entities/SeenTCN.java:
--------------------------------------------------------------------------------
1 | package org.itoapp.strict.database.entities;
2 |
3 | import java.util.Date;
4 |
5 | import androidx.room.ColumnInfo;
6 | import androidx.room.Entity;
7 | import androidx.room.Index;
8 | import androidx.room.PrimaryKey;
9 |
10 | @Entity(indices = {@Index(value = {"tcn"},
11 | unique = true)})
12 | public class SeenTCN {
13 | @PrimaryKey(autoGenerate = true)
14 | public long id;
15 |
16 | @ColumnInfo(name = "tcn")
17 | public String tcn;
18 |
19 | @ColumnInfo(name = "last_seen")
20 | public Date lastSeen;
21 |
22 | @ColumnInfo(name = "total_duration")
23 | public long duration;
24 |
25 | @ColumnInfo(name = "proximity")
26 | public long proximity;
27 |
28 | @ColumnInfo(name = "reportedSick")
29 | public boolean reportedSick = false;
30 |
31 | public SeenTCN() {
32 | }
33 |
34 | public SeenTCN(String tcn, Date lastSeen, long proximity, long duration) {
35 | this.tcn = tcn;
36 | this.lastSeen = lastSeen;
37 | this.duration = duration;
38 | this.proximity = proximity;
39 | }
40 |
41 | @Override
42 | public String toString() {
43 | return "SeenTCN{" +
44 | "id=" + id +
45 | ", tcn='" + tcn + '\'' +
46 | ", lastSeen=" + lastSeen +
47 | ", duration=" + duration +
48 | ", proximity=" + proximity +
49 | ", reportedSick=" + reportedSick +
50 | '}';
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/android/src/main/java/org/itoapp/strict/network/BlockingInputStream.java:
--------------------------------------------------------------------------------
1 | package org.itoapp.strict.network;
2 |
3 | import java.io.IOException;
4 | import java.io.InputStream;
5 |
6 | class BlockingInputStream extends InputStream {
7 | private final InputStream stream;
8 |
9 | public BlockingInputStream(InputStream content) {
10 | stream = content;
11 | }
12 |
13 | /*
14 | uses the implementation from .read(byte b[], int off, int len)
15 | this will only return if array was filled to request or -1 or exception occurred
16 | */
17 |
18 | @Override
19 | public int read() throws IOException {
20 | return stream.read();
21 | }
22 |
23 | @Override
24 | public int available() throws IOException {
25 | return stream.available();
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/android/src/main/java/org/itoapp/strict/network/NetworkHelper.java:
--------------------------------------------------------------------------------
1 | package org.itoapp.strict.network;
2 |
3 | import android.text.TextUtils;
4 | import android.util.Log;
5 |
6 | import org.itoapp.strict.database.RoomDB;
7 | import org.itoapp.strict.database.entities.LastReport;
8 |
9 | import java.io.BufferedOutputStream;
10 | import java.io.IOException;
11 | import java.io.InputStream;
12 | import java.io.OutputStream;
13 | import java.net.HttpURLConnection;
14 | import java.net.MalformedURLException;
15 | import java.net.URL;
16 | import java.nio.ByteBuffer;
17 | import java.util.Arrays;
18 | import java.util.Date;
19 | import java.util.LinkedList;
20 | import java.util.List;
21 |
22 | import static org.itoapp.strict.Helper.byte2Hex;
23 |
24 | public class NetworkHelper {
25 |
26 | private static final String LOG_TAG = "ITOInfectedUUIDRepository";
27 | public static final String BASE_URL = "https://tcn.ito-app.org/tcnreport";
28 |
29 | private static final int SIGNATURELENGTH = 64;
30 | private static final int BASELENGTH = 70;
31 |
32 | public static List refreshInfectedUUIDs() {
33 | LastReport lastReportHashForServer = RoomDB.db.lastReportDao().getLastReportHashForServer(BASE_URL);
34 | if (lastReportHashForServer == null) {
35 | lastReportHashForServer = new LastReport();
36 | lastReportHashForServer.serverUrl = BASE_URL;
37 | }
38 | List reports = new LinkedList<>();
39 | HttpURLConnection urlConnection = null;
40 | try {
41 | //TODO use a more sophisticated library
42 | URL url;
43 | if (TextUtils.isEmpty(lastReportHashForServer.lastReportHash))
44 | url = new URL(BASE_URL);
45 | else
46 | url = new URL(BASE_URL + "?from=" + lastReportHashForServer.lastReportHash);
47 | urlConnection = (HttpURLConnection) url.openConnection();
48 | urlConnection.addRequestProperty("Accept", "application/octet-stream");
49 | InputStream in = new BlockingInputStream(urlConnection.getInputStream());
50 | byte[] base = new byte[BASELENGTH];
51 | byte[] memo;
52 | int readBytes;
53 | while ((readBytes = in.read(base, 0, BASELENGTH)) == BASELENGTH) {
54 | int memolength = (int) base[BASELENGTH - 1] & 0xFF;
55 | memo = new byte[memolength];
56 | if (in.read(memo, 0, memolength) < memolength) {
57 | throw new RuntimeException("Parsing from Server failed");
58 | }
59 | byte[] signature = new byte[SIGNATURELENGTH];
60 | if (in.read(signature, 0, SIGNATURELENGTH) < SIGNATURELENGTH) {
61 | throw new RuntimeException("Parsing from Server failed");
62 | }
63 | // use PushbackInputstream and get rid of BB?
64 | ByteBuffer report = ByteBuffer.allocate(BASELENGTH + memolength + SIGNATURELENGTH);
65 | report.put(base);
66 | report.put(memo);
67 | report.put(signature);
68 | reports.add(report.array());
69 | }
70 | if (readBytes > 0)
71 | throw new RuntimeException("Parsing from Server failed");
72 | } catch (MalformedURLException e) {
73 | Log.wtf(LOG_TAG, "Malformed URL?!", e);
74 | throw new RuntimeException(e);
75 | } catch (IOException e) {
76 | e.printStackTrace();
77 | } finally {
78 | if (urlConnection != null) {
79 | urlConnection.disconnect();
80 | }
81 | }
82 | if (reports.size() > 0) {
83 | byte[] lastreport = reports.get(reports.size() - 1);
84 |
85 | lastReportHashForServer.lastcheck = new Date();
86 |
87 | lastReportHashForServer.lastReportHash = byte2Hex(Arrays.copyOfRange(lastreport, 0, lastreport.length - SIGNATURELENGTH));
88 | RoomDB.db.lastReportDao().saveOrUpdate(lastReportHashForServer);
89 | }
90 | return reports;
91 | }
92 |
93 |
94 | public static void publishReports(List reports) throws IOException {
95 |
96 | HttpURLConnection urlConnection = null;
97 | for (byte[] report : reports) // FIXME: validate return code
98 | try {
99 | URL url = new URL(BASE_URL);
100 | urlConnection = (HttpURLConnection) url.openConnection();
101 | urlConnection.setDoOutput(true);
102 | urlConnection.addRequestProperty("Content-Type", "application/octet-stream");
103 | OutputStream outputStream = new BufferedOutputStream(urlConnection.getOutputStream());
104 | outputStream.write(report);
105 | outputStream.close();
106 |
107 | InputStream inputStream = urlConnection.getInputStream();
108 | inputStream.read();
109 | inputStream.close();
110 | } catch (MalformedURLException e) {
111 | Log.wtf(LOG_TAG, "Malformed URL?!", e);
112 | throw new RuntimeException(e);
113 | } finally {
114 | if (urlConnection != null)
115 | urlConnection.disconnect();
116 | }
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/android/src/main/java/org/itoapp/strict/service/BleAdvertiser.java:
--------------------------------------------------------------------------------
1 | package org.itoapp.strict.service;
2 |
3 | import android.bluetooth.BluetoothAdapter;
4 | import android.bluetooth.le.AdvertiseCallback;
5 | import android.bluetooth.le.AdvertiseData;
6 | import android.bluetooth.le.AdvertiseSettings;
7 | import android.bluetooth.le.BluetoothLeAdvertiser;
8 | import android.os.Handler;
9 | import android.util.Log;
10 |
11 | import static org.itoapp.strict.Constants.BLUETOOTH_COMPANY_ID;
12 |
13 | public class BleAdvertiser {
14 | private static final String LOG_TAG = "ITOBleAdvertiser";
15 | private final Handler serviceHandler;
16 |
17 | private byte[] broadcastData;
18 | private BluetoothLeAdvertiser bluetoothLeAdvertiser;
19 | private AdvertiseCallback bluetoothAdvertiseCallback;
20 |
21 | public BleAdvertiser(BluetoothAdapter bluetoothAdapter, Handler serviceHandler) {
22 | this.bluetoothLeAdvertiser = bluetoothAdapter.getBluetoothLeAdvertiser();
23 | this.serviceHandler = serviceHandler;
24 | }
25 |
26 | public void setBroadcastData(byte[] broadcastData) {
27 | this.broadcastData = broadcastData;
28 | if(bluetoothAdvertiseCallback != null) {
29 | restartAdvertising();
30 | }
31 | }
32 |
33 | private void restartAdvertising() {
34 | stopAdvertising();
35 | startAdvertising();
36 | }
37 |
38 | public void startAdvertising() {
39 | AdvertiseSettings settings = new AdvertiseSettings.Builder()
40 | .setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY)
41 | .setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH)
42 | .setConnectable(false)
43 | .setTimeout(0)
44 | .build();
45 |
46 |
47 | AdvertiseData data = new AdvertiseData.Builder()
48 | .setIncludeTxPowerLevel(false)
49 | .setIncludeDeviceName(false)
50 | .addManufacturerData(BLUETOOTH_COMPANY_ID, broadcastData)
51 | .build();
52 |
53 | bluetoothAdvertiseCallback = new AdvertiseCallback() {
54 | @Override
55 | public void onStartSuccess(AdvertiseSettings settingsInEffect) {
56 | super.onStartSuccess(settingsInEffect);
57 | Log.i(LOG_TAG, "Advertising onStartSuccess");
58 |
59 | // when the timeout expires, restart advertising
60 | if (settingsInEffect.getTimeout() > 0)
61 | serviceHandler.postDelayed(() -> restartAdvertising(),
62 | settingsInEffect.getTimeout());
63 | }
64 |
65 | @Override
66 | public void onStartFailure(int errorCode) {
67 | super.onStartFailure(errorCode);
68 | Log.e(LOG_TAG, "Advertising onStartFailure: " + errorCode);
69 | // TODO
70 | }
71 | };
72 |
73 | // TODO: check if null when launching with Bluetooth disabled
74 | bluetoothLeAdvertiser.startAdvertising(settings, data, bluetoothAdvertiseCallback);
75 | }
76 |
77 | public void stopAdvertising() {
78 | Log.d(LOG_TAG, "Stopping advertising");
79 | if (bluetoothAdvertiseCallback != null) {
80 | bluetoothLeAdvertiser.stopAdvertising(bluetoothAdvertiseCallback);
81 | bluetoothAdvertiseCallback = null;
82 | }
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/android/src/main/java/org/itoapp/strict/service/BleScanner.java:
--------------------------------------------------------------------------------
1 | package org.itoapp.strict.service;
2 |
3 | import android.bluetooth.BluetoothAdapter;
4 | import android.bluetooth.le.BluetoothLeScanner;
5 | import android.bluetooth.le.ScanCallback;
6 | import android.bluetooth.le.ScanFilter;
7 | import android.bluetooth.le.ScanRecord;
8 | import android.bluetooth.le.ScanResult;
9 | import android.bluetooth.le.ScanSettings;
10 | import android.os.Build;
11 | import android.util.Log;
12 |
13 | import org.itoapp.strict.Constants;
14 |
15 | import java.util.Arrays;
16 | import java.util.Collections;
17 |
18 | import static org.itoapp.strict.Constants.BLUETOOTH_COMPANY_ID;
19 | import static org.itoapp.strict.Constants.BROADCAST_LENGTH;
20 | import static org.itoapp.strict.Constants.HASH_LENGTH;
21 |
22 | public class BleScanner {
23 | private static final String LOG_TAG = "ITOBleScanner";
24 |
25 | private BluetoothLeScanner bluetoothLeScanner;
26 | private ScanCallback bluetoothScanCallback;
27 | private ContactCache contactCache;
28 |
29 | public BleScanner(BluetoothAdapter bluetoothAdapter, ContactCache contactCache) {
30 | bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner();
31 | this.contactCache = contactCache;
32 | }
33 |
34 | public void startScanning() {
35 | Log.d(LOG_TAG, "Starting scan");
36 | bluetoothScanCallback = new ScanCallback() {
37 | public void onScanResult(int callbackType, ScanResult result) {
38 |
39 | Log.d(LOG_TAG, "onScanResult");
40 |
41 | ScanRecord record = result.getScanRecord();
42 |
43 | // if there is no record, discard this packet
44 | if (record == null) {
45 | return;
46 | }
47 |
48 | byte[] receivedHash = record.getManufacturerSpecificData(BLUETOOTH_COMPANY_ID);
49 |
50 | // if there is no data, discard
51 | if (receivedHash == null) {
52 | return;
53 | }
54 |
55 | receivedHash = Arrays.copyOf(receivedHash, HASH_LENGTH);
56 |
57 | int rssi = result.getRssi();
58 |
59 | // TODO take antenna attenuation into account
60 | float distance = (float) Math.pow(10F, (-65 - rssi) / (10 * 2));
61 |
62 | if(distance < Constants.MIN_SCANNING_DISTANCE)
63 | contactCache.addReceivedBroadcast(receivedHash, distance);
64 |
65 | Log.d(LOG_TAG, Arrays.toString(receivedHash) + ":" + distance);
66 |
67 | }
68 | };
69 |
70 | ScanSettings.Builder settingsBuilder = new ScanSettings.Builder()
71 | .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
72 | .setReportDelay(0);
73 |
74 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
75 | settingsBuilder.setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
76 | .setNumOfMatches(ScanSettings.MATCH_NUM_MAX_ADVERTISEMENT)
77 | .setMatchMode(ScanSettings.MATCH_MODE_AGGRESSIVE);
78 | }
79 |
80 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
81 | settingsBuilder.setLegacy(true);
82 | }
83 |
84 | byte[] manufacturerDataMask = new byte[BROADCAST_LENGTH];
85 |
86 | ScanFilter filter = new ScanFilter.Builder()
87 | .setManufacturerData(BLUETOOTH_COMPANY_ID, manufacturerDataMask, manufacturerDataMask)
88 | .build();
89 |
90 | bluetoothLeScanner.startScan(Collections.singletonList(filter), settingsBuilder.build(), bluetoothScanCallback);
91 | }
92 |
93 | public void stopScanning() {
94 | Log.d(LOG_TAG, "Stopping scanning");
95 | if(bluetoothScanCallback != null) {
96 | bluetoothLeScanner.stopScan(bluetoothScanCallback);
97 | bluetoothScanCallback = null;
98 | }
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/android/src/main/java/org/itoapp/strict/service/CheckServerTask.java:
--------------------------------------------------------------------------------
1 | package org.itoapp.strict.service;
2 |
3 | import android.os.AsyncTask;
4 | import android.util.Log;
5 |
6 | import org.itoapp.strict.database.ItoDBHelper;
7 | import org.itoapp.strict.database.RoomDB;
8 | import org.itoapp.strict.database.entities.SeenTCN;
9 | import org.itoapp.strict.network.NetworkHelper;
10 |
11 | import java.util.List;
12 |
13 | import androidx.annotation.RequiresApi;
14 |
15 | import static org.itoapp.strict.Helper.byte2Hex;
16 |
17 | public class CheckServerTask extends AsyncTask {
18 | private static final String LOG_TAG = "ITOCheckServerTask";
19 | private ItoDBHelper dbHelper;
20 |
21 | public CheckServerTask(ItoDBHelper itoDBHelper) {
22 | this.dbHelper = itoDBHelper;
23 | }
24 |
25 | @RequiresApi(api = 24)
26 | @Override
27 | protected Void doInBackground(Void... voids) {
28 | try {
29 | List reports = NetworkHelper.refreshInfectedUUIDs();
30 | reports.stream().filter(x -> TCNProtoUtil.verifySignatureOfReportCorrect(x)).forEach(x -> TCNProtoUtil.generateAllTCNsFromReport(x, tcn -> this.checkInfection(tcn)));
31 | } catch (Exception ex){
32 | ex.printStackTrace(); // FIXME: Notify user of failed update
33 | }
34 |
35 | /* List contactResults = dbHelper.selectInfectedContacts();
36 | if (!contactResults.isEmpty()) {
37 | Log.w(LOG_TAG, "Possibly encountered UUIDs: " + contactResults.size());
38 | } */
39 | return null;
40 | }
41 |
42 | private void checkInfection(byte[] tcn) {
43 | Log.d(LOG_TAG, "Test if following TCN was seen: " + byte2Hex(tcn));
44 | final SeenTCN seenTCN = RoomDB.db.seenTCNDao().findSeenTCNByHash(byte2Hex(tcn));
45 | if (seenTCN != null && !seenTCN.reportedSick) {
46 | seenTCN.reportedSick = true;
47 | RoomDB.db.seenTCNDao().update(seenTCN);
48 | Log.d(LOG_TAG, "Updated " + seenTCN);
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/android/src/main/java/org/itoapp/strict/service/ContactCache.java:
--------------------------------------------------------------------------------
1 | package org.itoapp.strict.service;
2 |
3 | import android.os.Handler;
4 | import android.os.RemoteException;
5 | import android.util.Log;
6 |
7 | import org.itoapp.DistanceCallback;
8 | import org.itoapp.strict.Constants;
9 | import org.itoapp.strict.Helper;
10 | import org.itoapp.strict.database.ItoDBHelper;
11 |
12 | import java.nio.ByteBuffer;
13 | import java.util.ArrayList;
14 | import java.util.HashMap;
15 | import java.util.List;
16 |
17 | import androidx.collection.CircularArray;
18 |
19 | public class ContactCache {
20 | private static final String LOG_TAG = "ContactCache";
21 |
22 | private ItoDBHelper dbHelper;
23 | private Handler serviceHandler;
24 | private HashMap cache = new HashMap<>();
25 | private DistanceCallback distanceCallback;
26 |
27 | public ContactCache(ItoDBHelper dbHelper, Handler serviceHandler) {
28 | this.dbHelper = dbHelper;
29 | this.serviceHandler = serviceHandler;
30 | }
31 |
32 | private void flush(ByteBuffer hash) {
33 | Log.d(LOG_TAG, "Flushing distance to DB");
34 | CacheEntry entry = cache.get(hash);
35 | entry.lowestDistance = Math.min(calculateDistance(entry), entry.lowestDistance);
36 | long contactDuration = entry.lastReceived - entry.firstReceived;
37 | if (contactDuration > Constants.MIN_CONTACT_DURATION) {
38 | dbHelper.insertContact(entry.hash, (int) entry.lowestDistance, contactDuration);
39 | Log.d(LOG_TAG, "Flushing " + Helper.byte2Hex(hash.array()) + " to DB");
40 | }
41 | cache.remove(hash);
42 |
43 | reportDistances();
44 | }
45 |
46 | public void flush() {
47 | for (ByteBuffer hash : cache.keySet()) {
48 | flush(hash);
49 | }
50 | }
51 |
52 | public void addReceivedBroadcast(byte[] hash, float distance) {
53 | ByteBuffer hashString = ByteBuffer.wrap(hash);
54 | CacheEntry entry = cache.get(hashString);
55 |
56 | if (entry == null) {
57 | // new unknown broadcast
58 | entry = new CacheEntry();
59 | cache.put(hashString, entry);
60 | entry.hash = hash;
61 | entry.firstReceived = System.currentTimeMillis();
62 | }
63 |
64 | entry.lastReceived = System.currentTimeMillis();
65 |
66 | // postpone flushing
67 | serviceHandler.removeCallbacks(entry.flushRunnable);
68 | serviceHandler.postDelayed(entry.flushRunnable, Constants.CACHE_FLUSH_TIME);
69 |
70 | CircularArray distances = entry.distances;
71 | distances.addFirst(distance);
72 | if (distances.size() == Constants.DISTANCE_SMOOTHING_MA_LENGTH) {
73 | entry.lowestDistance = Math.min(calculateDistance(entry), entry.lowestDistance);
74 | distances.popLast();
75 | distances.clear();
76 | distances.addFirst(entry.lowestDistance);
77 | //int contactDuration = (int) (entry.lastReceived - entry.firstReceived);
78 | //dbHelper.insertContact(entry.hash, (int) entry.lowestDistance, contactDuration);
79 | }
80 | reportDistances();
81 | }
82 |
83 | private void reportDistances() {
84 |
85 | if (distanceCallback != null) {
86 | try {
87 | distanceCallback.onDistanceMeasurements(calculateDistances());
88 | } catch (RemoteException e) {
89 | distanceCallback = null;
90 | }
91 | }
92 | }
93 |
94 | private float calculateDistance(CacheEntry cacheEntry) {
95 | CircularArray measuredDistances = cacheEntry.distances;
96 | float distance = 0;
97 |
98 | for (int j = 0; j < measuredDistances.size(); j++) {
99 | distance += measuredDistances.get(j) / measuredDistances.size();
100 | }
101 | return distance;
102 | }
103 |
104 | private float[] calculateDistances() {
105 | float[] distances = new float[cache.size()];
106 | List cacheEntries = new ArrayList<>(cache.values());
107 | for (int i = 0; i < distances.length; i++) {
108 | CacheEntry cacheEntry = cacheEntries.get(i);
109 | distances[i] = calculateDistance(cacheEntry);
110 | }
111 |
112 | return distances;
113 | }
114 |
115 | public void setDistanceCallback(DistanceCallback distanceCallback) {
116 | this.distanceCallback = distanceCallback;
117 | }
118 |
119 | private class CacheEntry {
120 | long firstReceived;
121 | long lastReceived;
122 | byte[] hash;
123 | CircularArray distances = new CircularArray<>(Constants.DISTANCE_SMOOTHING_MA_LENGTH);
124 | float lowestDistance = Float.MAX_VALUE;
125 | Runnable flushRunnable = () -> flush(ByteBuffer.wrap(hash));
126 | }
127 | }
128 |
--------------------------------------------------------------------------------
/android/src/main/java/org/itoapp/strict/service/INextTCNCallback.java:
--------------------------------------------------------------------------------
1 | package org.itoapp.strict.service;
2 |
3 | public interface INextTCNCallback {
4 |
5 | public void next(byte [] tcn);
6 | }
7 |
--------------------------------------------------------------------------------
/android/src/main/java/org/itoapp/strict/service/PublishBeaconsTask.java:
--------------------------------------------------------------------------------
1 | package org.itoapp.strict.service;
2 |
3 | import android.os.AsyncTask;
4 | import android.os.RemoteException;
5 | import android.util.Log;
6 |
7 | import org.itoapp.PublishUUIDsCallback;
8 | import org.itoapp.strict.database.RoomDB;
9 | import org.itoapp.strict.network.NetworkHelper;
10 |
11 | import java.io.IOException;
12 | import java.util.List;
13 |
14 | class PublishBeaconsTask extends AsyncTask {
15 | private static final String LOG_TAG = "PublishBeaconsTask";
16 | private List report;
17 | private long from;
18 | private long to;
19 | private PublishUUIDsCallback callback;
20 |
21 | public PublishBeaconsTask(List report, PublishUUIDsCallback callback) {
22 | this.report = report;
23 | this.callback = callback;
24 | }
25 |
26 | @Override
27 | protected Void doInBackground(Void... voids) {
28 | try {
29 | NetworkHelper.publishReports(report);
30 | try {
31 | RoomDB.db.localKeyDao().deleteAll(); // remove all Keys that we have sent
32 | callback.onSuccess();
33 | } catch (RemoteException e) {
34 | Log.e(LOG_TAG, "._.", e);
35 | }
36 | } catch (IOException e) {
37 | Log.e(LOG_TAG, "Could not publish UUIDs!", e);
38 | try {
39 | callback.onFailure();
40 | } catch (RemoteException ex) {
41 | Log.e(LOG_TAG, "._.", e);
42 | }
43 | }
44 | return null;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/android/src/main/java/org/itoapp/strict/service/StartupListener.java:
--------------------------------------------------------------------------------
1 | package org.itoapp.strict.service;
2 |
3 | import android.content.BroadcastReceiver;
4 | import android.content.Context;
5 | import android.content.Intent;
6 |
7 | /*
8 | This BroadcastReceiver starts the tracing service when the system boots
9 | */
10 | public class StartupListener extends BroadcastReceiver {
11 | @Override
12 | public void onReceive(Context context, Intent intent) {
13 | context.startService(new Intent(context, TracingService.class));
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/android/src/main/java/org/itoapp/strict/service/TCNProtoGen.java:
--------------------------------------------------------------------------------
1 | package org.itoapp.strict.service;
2 |
3 |
4 | import java.nio.ByteBuffer;
5 | import java.nio.ByteOrder;
6 | import java.security.MessageDigest;
7 | import java.security.NoSuchAlgorithmException;
8 | import java.security.SecureRandom;
9 |
10 | import cafe.cryptography.ed25519.Ed25519PrivateKey;
11 |
12 | public class TCNProtoGen {
13 |
14 | private static final SecureRandom RANDOM = new SecureRandom();
15 | private static final String SHA256 = "SHA-256";
16 | private static final byte[] H_TCK = "H_TCK".getBytes(); // Pin charset?
17 | private static final byte[] H_TCN = "H_TCN".getBytes(); // Pin charset?
18 | public static final byte TCN_ID_ITO = 0x2;
19 | public static final byte TCN_ID_COEPI = 0x0;
20 |
21 | byte memotype = TCN_ID_ITO;
22 |
23 | byte[] rak = new byte[32];
24 | byte[] rvk = new byte[32];
25 |
26 | byte[] startTCK;
27 | byte[] currentTCK;
28 | int currentTCKpos = 0;
29 |
30 | public TCNProtoGen() {
31 | RANDOM.nextBytes(rak);
32 | genRVKandTck0();
33 | }
34 |
35 | public TCNProtoGen(byte[] rak, int currentTCKpos) {
36 | this.rak = rak;
37 | genRVKandTck0();
38 | this.currentTCKpos = currentTCKpos;
39 | }
40 |
41 | public TCNProtoGen(byte memotype) {
42 | RANDOM.nextBytes(rak);
43 | genRVKandTck0();
44 | this.memotype = memotype;
45 | }
46 |
47 | public TCNProtoGen(byte memotype, byte[] rvk, byte[] startTCK, int startTCKpos) {
48 | this.rvk = rvk;
49 | this.startTCK = startTCK;
50 | currentTCK = startTCK;
51 | currentTCKpos = startTCKpos;
52 | this.memotype = memotype;
53 | }
54 |
55 | void genRVKandTck0() {
56 | Ed25519PrivateKey sk = Ed25519PrivateKey.fromByteArray(rak);
57 | rvk = sk.derivePublic().toByteArray();
58 | MessageDigest h_tck0 = getSHA256();
59 | h_tck0.update(H_TCK);
60 | h_tck0.update(rak); // why do we use this ???
61 | startTCK = h_tck0.digest();
62 | currentTCK = startTCK;
63 | currentTCKpos = 0;
64 | }
65 |
66 | public synchronized byte[] getNewTCN() {
67 | currentTCK = genNextTCK(currentTCK);
68 | currentTCKpos++;
69 | return getCurrentTCN();
70 | }
71 |
72 | byte[] getCurrentTCN() {
73 | MessageDigest h_tcnj = getSHA256();
74 | h_tcnj.update(H_TCN);
75 | ByteBuffer length = ByteBuffer.allocate(2);
76 | length.order(ByteOrder.LITTLE_ENDIAN);
77 | length.putShort((short) (currentTCKpos));
78 | h_tcnj.update(length.array());
79 | h_tcnj.update(currentTCK);
80 |
81 | byte[] ret = new byte[16];
82 | System.arraycopy(h_tcnj.digest(), 0, ret, 0, 16);
83 | return ret;
84 | }
85 |
86 | private byte[] genNextTCK(byte[] current) {
87 | MessageDigest h_tckj = getSHA256();
88 | h_tckj.update(H_TCK);
89 | h_tckj.update(rvk);
90 | h_tckj.update(current);
91 | return h_tckj.digest();
92 | }
93 |
94 | public synchronized byte[] generateReport(int previousRatchetTicks) {
95 | // todo fail if no rak present
96 | int end = currentTCKpos;
97 | int start = currentTCKpos - previousRatchetTicks - 1;
98 | if (currentTCKpos <= 0) { // have we got more than only tck_0?
99 | throw new RuntimeException("no Keys to report about");
100 | }
101 | if (previousRatchetTicks < 0)
102 | throw new RuntimeException("daysBefore can not be negative");
103 | if (start < 1) { // give em everything we've got (except tck_0)
104 | start = 1;
105 | }
106 | byte[] memo = createMemo();
107 | final int totalPayloadbytes = 32 + 32 + 4 + memo.length;
108 |
109 | ByteBuffer payload = ByteBuffer.allocate(totalPayloadbytes);
110 | payload.put(rvk);
111 | payload.put(generateTCKAtPosition(start));
112 |
113 | ByteBuffer beginAndEnd = ByteBuffer.allocate(4);
114 | beginAndEnd.order(ByteOrder.LITTLE_ENDIAN);
115 | beginAndEnd.putShort((short) (start + 1));
116 | beginAndEnd.putShort((short) (end + 1));
117 | payload.put(beginAndEnd.array());
118 |
119 | payload.put(memo);
120 |
121 | byte[] sig = Ed25519PrivateKey.fromByteArray(rak).expand().sign(payload.array(), Ed25519PrivateKey.fromByteArray(rak).derivePublic()).toByteArray();
122 | ByteBuffer ret = ByteBuffer.allocate(totalPayloadbytes + sig.length);
123 | ret.put(payload.array());
124 | ret.put(sig);
125 | return ret.array();
126 | }
127 |
128 | private byte[] generateTCKAtPosition(int start) {
129 | byte[] tmp = startTCK;
130 | for (int i = 0; i < start; i++) {
131 | tmp = genNextTCK(tmp);
132 | }
133 | return tmp;
134 | }
135 |
136 | public int getRatchetTickCount() {
137 | return currentTCKpos;
138 | }
139 |
140 | private byte[] createMemo() {
141 | byte[] symptomData = "symptom data".getBytes();
142 | ByteBuffer memo = ByteBuffer.allocate(2 + symptomData.length);
143 | memo.order(ByteOrder.LITTLE_ENDIAN);
144 | memo.put((byte) this.memotype); // 0x2: ITO symptom report v1;
145 | memo.put((byte) symptomData.length);
146 | memo.put(symptomData);
147 | return memo.array();
148 | }
149 |
150 |
151 | private MessageDigest getSHA256() {
152 | try {
153 | return MessageDigest.getInstance(SHA256);
154 | } catch (NoSuchAlgorithmException ex) {
155 | throw new RuntimeException(ex);
156 | }
157 | }
158 |
159 | }
160 |
--------------------------------------------------------------------------------
/android/src/main/java/org/itoapp/strict/service/TCNProtoUtil.java:
--------------------------------------------------------------------------------
1 | package org.itoapp.strict.service;
2 |
3 | import org.itoapp.strict.database.RoomDB;
4 | import org.itoapp.strict.database.entities.LocalKey;
5 |
6 | import java.nio.ByteBuffer;
7 | import java.nio.ByteOrder;
8 | import java.util.Arrays;
9 | import java.util.Date;
10 | import java.util.List;
11 | import java.util.stream.Collectors;
12 |
13 | import androidx.annotation.RequiresApi;
14 | import cafe.cryptography.ed25519.Ed25519PublicKey;
15 | import cafe.cryptography.ed25519.Ed25519Signature;
16 |
17 | import static org.itoapp.strict.Helper.byte2Hex;
18 | import static org.itoapp.strict.Helper.hex2Byte;
19 |
20 | public class TCNProtoUtil {
21 |
22 | public static boolean verifySignatureOfReportCorrect(byte[] report) {
23 | byte[] bsignature = Arrays.copyOfRange(report, report.length - 64, report.length);
24 | byte[] brvk = getRvkfromReport(report);
25 | byte[] breport = Arrays.copyOfRange(report, 0, report.length - 64);
26 | try {
27 | Ed25519PublicKey rvk = Ed25519PublicKey.fromByteArray(brvk);
28 | Ed25519Signature signature = Ed25519Signature.fromByteArray(bsignature);
29 | return rvk.verify(breport, signature);
30 |
31 | } catch (Exception e) {
32 | return false;
33 | }
34 | }
35 |
36 |
37 | public static void generateAllTCNsFromReport(byte[] report, INextTCNCallback callback) {
38 | int from = readUShort(report, 64);
39 | byte[] bstartTCK = Arrays.copyOfRange(report, 32, 64);
40 | TCNProtoGen ratchet = new TCNProtoGen(report[68], getRvkfromReport(report), bstartTCK, from - 1);
41 | int to = readUShort(report, 66);
42 | callback.next(ratchet.getCurrentTCN());
43 | for (int i = from; i < to; i++) {
44 | callback.next(ratchet.getNewTCN());
45 | }
46 |
47 | }
48 |
49 |
50 | public static void persistRatchet(TCNProtoGen ratchet) {
51 | LocalKey lk = new LocalKey();
52 | lk.lastGenerated = new Date();
53 | lk.rak = byte2Hex(ratchet.rak);
54 | lk.currentTCKpos = ratchet.currentTCKpos;
55 | RoomDB.db.localKeyDao().saveOrUpdate(lk);
56 | }
57 |
58 | @RequiresApi(api = 24)
59 | public static List loadAllRatchets() {
60 | return RoomDB.db.localKeyDao().getAll().stream().map(x -> new TCNProtoGen(hex2Byte(x.rak), x.currentTCKpos)).collect(Collectors.toList());
61 | }
62 |
63 |
64 | private static byte[] getRvkfromReport(byte[] report) {
65 |
66 | return Arrays.copyOfRange(report, 0, 32);
67 | }
68 |
69 | static int readUShort(byte[] report, int index) {
70 | final ByteBuffer bb = ByteBuffer.wrap(report);
71 | bb.order(ByteOrder.LITTLE_ENDIAN);
72 | int i = bb.getShort(index);
73 | if (i < 0) {
74 | i = i - Short.MIN_VALUE * 2;
75 | }
76 | return i;
77 | }
78 |
79 |
80 | }
81 |
--------------------------------------------------------------------------------
/android/src/main/java/org/itoapp/strict/service/TracingService.java:
--------------------------------------------------------------------------------
1 | package org.itoapp.strict.service;
2 |
3 | import android.annotation.TargetApi;
4 | import android.app.Notification;
5 | import android.app.NotificationChannel;
6 | import android.app.NotificationManager;
7 | import android.app.PendingIntent;
8 | import android.app.Service;
9 | import android.bluetooth.BluetoothAdapter;
10 | import android.bluetooth.BluetoothManager;
11 | import android.content.BroadcastReceiver;
12 | import android.content.Context;
13 | import android.content.Intent;
14 | import android.content.IntentFilter;
15 | import android.graphics.Color;
16 | import android.location.LocationManager;
17 | import android.os.AsyncTask;
18 | import android.os.Build;
19 | import android.os.Handler;
20 | import android.os.HandlerThread;
21 | import android.os.IBinder;
22 | import android.os.Looper;
23 | import android.util.Log;
24 |
25 | import org.itoapp.DistanceCallback;
26 | import org.itoapp.PublishUUIDsCallback;
27 | import org.itoapp.TracingServiceInterface;
28 | import org.itoapp.strict.Constants;
29 | import org.itoapp.strict.Helper;
30 | import org.itoapp.strict.Preconditions;
31 | import org.itoapp.strict.database.ItoDBHelper;
32 | import org.itoapp.strict.database.RoomDB;
33 |
34 | import java.security.SecureRandom;
35 | import java.util.List;
36 | import java.util.stream.Collectors;
37 |
38 | import androidx.annotation.RequiresApi;
39 | import androidx.core.app.NotificationCompat;
40 |
41 | import static org.itoapp.strict.Constants.RATCHET_EXCHANGE_INTERVAL;
42 |
43 | public class TracingService extends Service {
44 | private static final String LOG_TAG = "ITOTracingService";
45 | private static final String DEFAULT_NOTIFICATION_CHANNEL = "ContactTracing";
46 | private static final int NOTIFICATION_ID = 1;
47 | private TCNProtoGen tcnProto;
48 | private SecureRandom uuidGenerator;
49 | private Looper serviceLooper;
50 | private Handler serviceHandler;
51 | private BleScanner bleScanner;
52 | private BleAdvertiser bleAdvertiser;
53 | private ContactCache contactCache;
54 | private ItoDBHelper dbHelper;
55 |
56 | private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
57 |
58 | @Override
59 | public void onReceive(Context context, android.content.Intent intent) {
60 | if (!isBluetoothRunning()) {
61 | startBluetooth();
62 | } else if (!Preconditions.canScanBluetooth(context)) {
63 | stopBluetooth();
64 | }
65 | }
66 | };
67 |
68 | private TracingServiceInterface.Stub binder = new TracingServiceInterface.Stub() {
69 | @Override
70 | public void setDistanceCallback(DistanceCallback distanceCallback) {
71 | contactCache.setDistanceCallback(distanceCallback);
72 | }
73 |
74 |
75 | @RequiresApi(api = 24)
76 | @Override
77 | public void publishBeaconUUIDs(long from, long to, PublishUUIDsCallback callback) {
78 | // todo use from & to ?
79 | List reports = TCNProtoUtil.loadAllRatchets().stream().map(ratchet -> ratchet.generateReport(ratchet.getRatchetTickCount())).collect(Collectors.toList());
80 |
81 | new PublishBeaconsTask(reports, callback).execute();
82 | }
83 |
84 | @RequiresApi(api = 24)
85 | @Override
86 | public boolean isPossiblyInfected() {
87 | //TODO do async
88 | Long totalExposureDuration = RoomDB.db.seenTCNDao().findSickTCNs().stream().map(x -> x.duration).reduce(0L, (a, b) -> a + b);
89 | return totalExposureDuration > Constants.MIN_EXPOSURE_DURATION;
90 | }
91 |
92 | @Override
93 | public void restartTracingService() {
94 | stopBluetooth();
95 | startBluetooth();
96 | }
97 |
98 | @Override
99 | public int getLatestFetchTime() {
100 | return dbHelper.getLatestFetchTime();
101 | }
102 | };
103 |
104 | private Runnable regenerateUUID = () -> {
105 | Log.i(LOG_TAG, "Regenerating TCN");
106 |
107 | /* byte[] uuid = new byte[Constants.UUID_LENGTH];
108 | uuidGenerator.nextBytes(uuid);
109 | byte[] hashedUUID = Helper.calculateTruncatedSHA256(uuid);
110 |
111 | dbHelper.insertBeacon(uuid);
112 |
113 | byte[] broadcastData = new byte[Constants.BROADCAST_LENGTH];
114 | broadcastData[Constants.BROADCAST_LENGTH - 1] = getTransmitPower();
115 | System.arraycopy(hashedUUID, 0, broadcastData, 0, Constants.HASH_LENGTH);
116 | */
117 |
118 |
119 | if (tcnProto != null && tcnProto.currentTCKpos == RATCHET_EXCHANGE_INTERVAL) {
120 | tcnProto = null;
121 | }
122 | if (tcnProto == null) {
123 | Log.i(LOG_TAG, "Regenerating Ratchet");
124 | tcnProto = new TCNProtoGen();
125 | }
126 | byte[] tcn = tcnProto.getNewTCN();
127 | Log.i(LOG_TAG, "Advertising " + Helper.byte2Hex(tcn));
128 | bleAdvertiser.setBroadcastData(tcn);
129 |
130 |
131 | AsyncTask.execute(new Runnable() { // FIXME make everything async and get aligned with sendReport etc.
132 | @Override
133 | public void run() {
134 | TCNProtoUtil.persistRatchet(tcnProto);
135 | }
136 | });
137 |
138 | serviceHandler.postDelayed(this.regenerateUUID, Constants.TCN_VALID_INTERVAL);
139 | };
140 | //TODO move this to some alarmManager governed section.
141 | // Also ideally check the server when connected to WIFI and charger
142 | private Runnable checkServer = () -> {
143 | new CheckServerTask(dbHelper).execute();
144 | serviceHandler.postDelayed(this.checkServer, Constants.CHECK_SERVER_INTERVAL);
145 | };
146 |
147 |
148 | private boolean isBluetoothRunning() {
149 | return bleScanner != null;
150 | }
151 |
152 | private void stopBluetooth() {
153 | Log.i(LOG_TAG, "Stopping Bluetooth");
154 | contactCache.flush();
155 | if (bleScanner != null)
156 | try {
157 | bleScanner.stopScanning();
158 | } catch (Exception ignored) {
159 | }
160 | if (bleAdvertiser != null)
161 | try {
162 | bleAdvertiser.stopAdvertising();
163 | } catch (Exception ignored) {
164 | }
165 |
166 | serviceHandler.removeCallbacks(regenerateUUID);
167 |
168 | bleScanner = null;
169 | bleAdvertiser = null;
170 | }
171 |
172 | private void startBluetooth() {
173 | Log.i(LOG_TAG, "Starting Bluetooth");
174 | if (!Preconditions.canScanBluetooth(this)) {
175 | Log.w(LOG_TAG, "Preconditions for starting Bluetooth not met");
176 | return;
177 | }
178 | BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
179 | assert bluetoothManager != null;
180 | BluetoothAdapter bluetoothAdapter = bluetoothManager.getAdapter();
181 |
182 | bleScanner = new BleScanner(bluetoothAdapter, contactCache);
183 | bleAdvertiser = new BleAdvertiser(bluetoothAdapter, serviceHandler);
184 |
185 | regenerateUUID.run();
186 | bleAdvertiser.startAdvertising();
187 | bleScanner.startScanning();
188 | }
189 |
190 | @Override
191 | public void onCreate() {
192 | super.onCreate();
193 | uuidGenerator = new SecureRandom();
194 | dbHelper = new ItoDBHelper();
195 | HandlerThread thread = new HandlerThread("TracingServiceHandler", Thread.NORM_PRIORITY);
196 | thread.start();
197 |
198 | // Get the HandlerThread's Looper and use it for our Handler
199 | serviceLooper = thread.getLooper();
200 | serviceHandler = new Handler(serviceLooper);
201 | serviceHandler.post(this.checkServer);
202 | contactCache = new ContactCache(dbHelper, serviceHandler);
203 |
204 | startBluetooth();
205 |
206 | IntentFilter filter = new IntentFilter();
207 | filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
208 | filter.addAction(LocationManager.MODE_CHANGED_ACTION);
209 | registerReceiver(broadcastReceiver, filter);
210 | }
211 |
212 | @TargetApi(26)
213 | private void createNotificationChannel(NotificationManager notificationManager) {
214 | int importance = NotificationManager.IMPORTANCE_DEFAULT;
215 |
216 | NotificationChannel mChannel = new NotificationChannel(DEFAULT_NOTIFICATION_CHANNEL, DEFAULT_NOTIFICATION_CHANNEL, importance);
217 | mChannel.enableLights(true);
218 | mChannel.setLightColor(Color.BLUE);
219 | mChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
220 | notificationManager.createNotificationChannel(mChannel);
221 | }
222 |
223 | private void runAsForgroundService() {
224 | NotificationManager notificationManager = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
225 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
226 | createNotificationChannel(notificationManager);
227 |
228 | Intent notificationIntent = new Intent();
229 |
230 | PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
231 | notificationIntent, 0);
232 |
233 | Notification notification = new NotificationCompat.Builder(this,
234 | DEFAULT_NOTIFICATION_CHANNEL)
235 | .setContentIntent(pendingIntent)
236 | .setPriority(NotificationManager.IMPORTANCE_LOW)
237 | .setVibrate(null)
238 | .build();
239 |
240 | startForeground(NOTIFICATION_ID, notification);
241 | }
242 |
243 | @Override
244 | public void onDestroy() {
245 | bleAdvertiser.stopAdvertising();
246 | bleScanner.stopScanning();
247 | contactCache.flush();
248 | unregisterReceiver(broadcastReceiver);
249 | super.onDestroy();
250 | }
251 |
252 | @Override
253 | public int onStartCommand(Intent intent, int flags, int startId) {
254 | runAsForgroundService();
255 | return START_STICKY;
256 | }
257 |
258 | /*
259 | Don't do anything here, because the service doesn't have to communicate to other apps
260 | */
261 | @Override
262 | public IBinder onBind(Intent intent) {
263 | return (IBinder) binder;
264 | }
265 | }
266 |
--------------------------------------------------------------------------------
/android/src/test/java/org/itoapp/strict/service/TCNProtoGenTest.java:
--------------------------------------------------------------------------------
1 | package org.itoapp.strict.service;
2 |
3 | import org.junit.Test;
4 |
5 | import java.util.LinkedList;
6 | import java.util.List;
7 | import java.util.stream.IntStream;
8 |
9 | import static org.itoapp.strict.Helper.byte2Hex;
10 | import static org.itoapp.strict.Helper.hex2Byte;
11 | import static org.junit.Assert.assertArrayEquals;
12 | import static org.junit.Assert.assertEquals;
13 |
14 | public class TCNProtoGenTest {
15 |
16 |
17 | public static final String VALIDREPORT = "fd8deb9d91a13e144ca5b0ce14e289532e040fe0bf922c6e3dadb1e4e2333c78df535b90ac99bec8be3a8add45ce77897b1e7cb1906b5cff1097d3cb142fd9d002000a00000c73796d70746f6d206461746131078ec5367b67a8c793b740626d81ba904789363137b5a313419c0f50b180d8226ecc984bf073ff89cbd9c88fea06bda1f0f368b0e7e88bbe68f15574482904";
18 |
19 |
20 | public static final String[] REPORTTCNS = new String[]{
21 | "f4350a4a33e30f2f568898fbe4c4cf34",
22 | "135eeaa6482b8852fea3544edf6eabf0",
23 | "d713ce68cf4127bcebde6874c4991e4b",
24 | "5174e6514d2086565e4ea09a45995191",
25 | "ccae4f2c3144ad1ed0c2a39613ef0342",
26 | "3b9e600991369bba3944b6e9d8fda370",
27 | "dc06a8625c08e946317ad4c89e6ee8a1",
28 | "9d671457835f2c254722bfd0de76dffc",
29 | "8b454d28430d3153a500359d9a49ec88"};
30 |
31 | /**
32 | * Test Generate Report ~henry's vector from rust
33 | */
34 | @Test
35 | public void testReportVector() throws Exception {
36 | String rak = "577cfdae21fee71579211ab02c418ee0948bacab613cf69d0a4a5ae5a1557dbb";
37 | TCNProtoGen demo = new TCNProtoGen(TCNProtoGen.TCN_ID_COEPI);
38 | demo.rak = hex2Byte(rak);
39 | demo.genRVKandTck0();
40 | List tcns = new LinkedList<>();
41 | IntStream.range(0, 9).forEach(i
42 | -> tcns.add(byte2Hex(demo.getNewTCN()))
43 | );
44 | assertArrayEquals(REPORTTCNS, tcns.toArray());
45 | assertEquals(VALIDREPORT, byte2Hex(demo.generateReport(8)));
46 | }
47 |
48 |
49 | @Test
50 | public void testReport() {
51 | TCNProtoGen tcnGen = new TCNProtoGen();
52 | tcnGen.getNewTCN();
53 | tcnGen.generateReport(0);
54 | tcnGen.generateReport(1);
55 | tcnGen.generateReport(2);
56 | }
57 |
58 | @Test(expected = RuntimeException.class)
59 | public void testReportNotNegative() {
60 | TCNProtoGen tcnGen = new TCNProtoGen();
61 | tcnGen.getNewTCN();
62 |
63 | tcnGen.generateReport(-1);
64 |
65 | }
66 |
67 | @Test
68 | public void testReportSizing() {
69 | // "public report" tck start at 0x2 (first tck to follow tck0) in count = 2
70 |
71 | String rak = "577cfdae21fee71579211ab02c418ee0948bacab613cf69d0a4a5ae5a1557dbb";
72 | TCNProtoGen tcnGen = new TCNProtoGen(TCNProtoGen.TCN_ID_COEPI);
73 | tcnGen.rak = hex2Byte(rak);
74 | tcnGen.genRVKandTck0();
75 |
76 |
77 | String r0 = "fd8deb9d91a13e144ca5b0ce14e289532e040fe0bf922c6e3dadb1e4e2333c78df535b90ac99bec8be3a8add45ce77897b1e7cb1906b5cff1097d3cb142fd9d002000200000c73796d70746f6d2064617461400c2b0049c2345c2f91385ab053db5605cf9e8910348efbcfcc67dc505454d93579792742aab9a4343243bb6595c1d2ae6b824daa18eb4e7b64ce45e3b1260b";
78 | String r1 = "fd8deb9d91a13e144ca5b0ce14e289532e040fe0bf922c6e3dadb1e4e2333c78df535b90ac99bec8be3a8add45ce77897b1e7cb1906b5cff1097d3cb142fd9d002000300000c73796d70746f6d206461746110c6d4c0ed3033c41ba85315758aa9aed18db9d142fc40d408f3df3095357f3daacb36cc76da8da841ef22cd785b4ab9f4e2277013e88738d253ef7ea9965509";
79 | String r2 = "fd8deb9d91a13e144ca5b0ce14e289532e040fe0bf922c6e3dadb1e4e2333c78df535b90ac99bec8be3a8add45ce77897b1e7cb1906b5cff1097d3cb142fd9d002000400000c73796d70746f6d2064617461046abf5b87d61b29c498b0cf7976a9132ed14046656f36c14c7336c3f9130fc4267015560c3a6564d24c56cfa5c1350690026818e36d6fba20771f9e41954c03";
80 | String r3 = "fd8deb9d91a13e144ca5b0ce14e289532e040fe0bf922c6e3dadb1e4e2333c78df535b90ac99bec8be3a8add45ce77897b1e7cb1906b5cff1097d3cb142fd9d002000500000c73796d70746f6d20646174614f82be44f2b6dcf98732ff05a302c0da13e35a14ad610c4fd8fbbfa33d7f969ac744c85a9adc223749bafb5ab2db4030042ef4c9b599a358050d5c2cce49e905";
81 |
82 |
83 | tcnGen.getNewTCN();
84 | assertEquals(tcnGen.getRatchetTickCount(), 1);
85 | assertEquals(r0, byte2Hex(tcnGen.generateReport(tcnGen.getRatchetTickCount())));
86 | tcnGen.getNewTCN();
87 | assertEquals(tcnGen.getRatchetTickCount(), 2);
88 | assertEquals(r1, byte2Hex(tcnGen.generateReport(tcnGen.getRatchetTickCount())));
89 | tcnGen.getNewTCN();
90 | assertEquals(tcnGen.getRatchetTickCount(), 3);
91 | assertEquals(r2, byte2Hex(tcnGen.generateReport(tcnGen.getRatchetTickCount())));
92 | tcnGen.getNewTCN();
93 | //getRatchetTickCount()
94 | // fixme
95 | assertEquals(r3, byte2Hex(tcnGen.generateReport(tcnGen.getRatchetTickCount())));
96 |
97 | }
98 |
99 |
100 |
101 |
102 | }
103 |
--------------------------------------------------------------------------------
/android/src/test/java/org/itoapp/strict/service/TCNProtoUtilTest.java:
--------------------------------------------------------------------------------
1 | package org.itoapp.strict.service;
2 |
3 | import org.itoapp.strict.database.RoomDB;
4 | import org.itoapp.strict.database.dao.LastReportDao;
5 | import org.itoapp.strict.database.dao.LocalKeyDao;
6 | import org.itoapp.strict.database.dao.SeenTCNDao;
7 | import org.itoapp.strict.database.entities.LocalKey;
8 | import org.junit.Test;
9 |
10 | import java.util.ArrayList;
11 | import java.util.HashMap;
12 | import java.util.HashSet;
13 | import java.util.LinkedList;
14 | import java.util.List;
15 | import java.util.Map;
16 | import java.util.Set;
17 | import java.util.stream.IntStream;
18 |
19 | import androidx.annotation.NonNull;
20 | import androidx.room.DatabaseConfiguration;
21 | import androidx.room.InvalidationTracker;
22 | import androidx.sqlite.db.SupportSQLiteOpenHelper;
23 |
24 | import static org.itoapp.strict.service.TCNProtoGenTest.REPORTTCNS;
25 | import static org.itoapp.strict.Helper.byte2Hex;
26 | import static org.itoapp.strict.Helper.hex2Byte;
27 | import static org.junit.Assert.assertArrayEquals;
28 | import static org.junit.Assert.assertEquals;
29 |
30 | public class TCNProtoUtilTest {
31 |
32 |
33 | @Test
34 | public void testVerifySig() throws Exception {
35 | byte[] report = hex2Byte(TCNProtoGenTest.VALIDREPORT);
36 | assertEquals(true, TCNProtoUtil.verifySignatureOfReportCorrect(report));
37 |
38 | }
39 |
40 | @Test
41 | public void generateFromTo() throws Exception {
42 | byte[] report = hex2Byte(TCNProtoGenTest.VALIDREPORT);
43 | List tcns = new LinkedList<>();
44 | TCNProtoUtil.generateAllTCNsFromReport(report, x ->
45 | tcns.add(byte2Hex(x))
46 | );
47 | assertArrayEquals(REPORTTCNS, tcns.toArray());
48 | }
49 |
50 | @Test // not too much of a unit test
51 | public void testBounds() throws Exception {
52 | TCNProtoGen tcnGen = new TCNProtoGen();
53 | int ulimit = Short.MAX_VALUE * 2;
54 | List tcnsSource = new LinkedList<>();
55 | IntStream.range(0, ulimit).forEach(i
56 | -> tcnsSource.add(byte2Hex(tcnGen.getNewTCN())));
57 |
58 | List tcnRegenerated = new LinkedList<>();
59 | TCNProtoUtil.generateAllTCNsFromReport(tcnGen.generateReport(tcnGen.getRatchetTickCount()), x ->
60 | tcnRegenerated.add(byte2Hex(x))
61 | );
62 | assertEquals(ulimit, tcnRegenerated.size());
63 | assertArrayEquals(tcnsSource.toArray(), tcnRegenerated.toArray());
64 | }
65 |
66 |
67 | @Test
68 | public void testSaveAndLoad() throws Exception {
69 |
70 | // 0 Mock DB
71 | Map lk = new HashMap<>();
72 | RoomDB.db = new RoomDB() {
73 | @Override
74 | public LastReportDao lastReportDao() {
75 | return null;
76 | }
77 |
78 | @Override
79 | public SeenTCNDao seenTCNDao() {
80 | return null;
81 | }
82 |
83 | @Override
84 | public LocalKeyDao localKeyDao() {
85 | return new LocalKeyDao() {
86 |
87 |
88 | @Override
89 | public List getAll() {
90 | return new ArrayList(lk.values());
91 | }
92 |
93 | @Override
94 | public void saveOrUpdate(LocalKey localKey) {
95 | lk.put(localKey.rak, localKey);
96 | }
97 |
98 | @Override
99 | public void deleteAll() {
100 | lk.clear();
101 | }
102 | };
103 | }
104 |
105 | @NonNull
106 | @Override
107 | protected SupportSQLiteOpenHelper createOpenHelper(DatabaseConfiguration config) {
108 | return null;
109 | }
110 |
111 | @NonNull
112 | @Override
113 | protected InvalidationTracker createInvalidationTracker() {
114 | return null;
115 | }
116 |
117 | @Override
118 | public void clearAllTables() {
119 |
120 | }
121 | };
122 |
123 |
124 | // 1) Setup Sample data
125 | // 10 Days with 96 TCN's per Day
126 | Set tcnsSource = new HashSet<>();
127 | IntStream.range(0, 10).forEach(i
128 | -> {
129 | TCNProtoGen tcnGen = new TCNProtoGen();
130 | IntStream.range(0, 96).forEach(i2
131 | -> {
132 | tcnsSource.add(byte2Hex(tcnGen.getNewTCN()));
133 | TCNProtoUtil.persistRatchet(tcnGen);
134 | });
135 | });
136 |
137 | // 2) verify every TCN is found in the reports
138 | TCNProtoUtil.loadAllRatchets().stream().map(ratchet ->
139 | ratchet.generateReport(ratchet.getRatchetTickCount()))
140 | .filter(x -> TCNProtoUtil.verifySignatureOfReportCorrect(x)).forEach(x -> TCNProtoUtil.generateAllTCNsFromReport(x, tcn -> tcnsSource.remove(byte2Hex(tcn))));
141 | assertEquals(0, tcnsSource.size());
142 |
143 | }
144 | }
145 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | import { NativeModules } from 'react-native';
2 |
3 | const { ItoBluetooth } = NativeModules;
4 |
5 | export default ItoBluetooth;
6 |
--------------------------------------------------------------------------------
/ios/ItoBluetooth-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | //
2 | // Use this file to import your target's public headers that you would like to expose to Swift.
3 | //
4 |
5 |
--------------------------------------------------------------------------------
/ios/ItoBluetooth.m:
--------------------------------------------------------------------------------
1 | #import
2 | // #import
3 |
4 |
5 | // #import "ItoBluetooth-Bridging-Header.h"
6 | // @interface RCT_EXTERN_MODULE(ItoBluetooth, NSObject)
7 |
8 | // RCT_EXPORT_METHOD(callback:(RCTResponseSenderBlock)onDistanceMeasurements)
9 | // {
10 | // onDistanceMeasurements([NSNumber new]);
11 | // }
12 |
13 | // RCT_EXPORT_METHOD(publishBeaconUUIDs:(NSNumber *) from:(NSNumber*) to: callback:(RCTResponseSenderBlock)onSuccess){
14 |
15 | // }
16 |
17 | // RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(isPossiblyInfected){
18 | // SwItoBluetooth *sbt = [SwItoBluetooth new] ;
19 | // NSNumber *retval = sbt.isPossiblyInfected;
20 | // return retval;
21 | // }
22 |
23 | // RCT_EXPORT_METHOD(restartTracingService){
24 | // SwItoBluetooth *sbt = [SwItoBluetooth new] ;
25 | // sbt.restartTracingService;
26 | // }
27 |
28 | // RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(getLatestFetchTime){
29 | // SwItoBluetooth *sbt = [SwItoBluetooth new] ;
30 | // NSNumber *retval = sbt.getLatestFetchTime;
31 | // return retval;
32 | // }
33 | // @end
34 |
35 |
--------------------------------------------------------------------------------
/ios/ItoBluetooth.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ItoBluetooth.swift
3 | // ItoBluetooth
4 | //
5 | // Created by Dieder Timmers on 18/04/2020.
6 | // Copyright © 2020 Facebook. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | @objc(ItoBluetooth)
12 | public class ItoBluetooth: NSObject {
13 | @objc
14 | public func isPossiblyInfected() -> Bool{
15 | return true;
16 | }
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/ios/ItoBluetooth.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 52;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 7BE90D4FB255B68DDBC03678 /* Pods_ItoBluetooth.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7EC1987031BCF80FBAC73D9C /* Pods_ItoBluetooth.framework */; };
11 | B3E7B58A1CC2AC0600A0062D /* ItoBluetooth.m in Sources */ = {isa = PBXBuildFile; fileRef = B3E7B5891CC2AC0600A0062D /* ItoBluetooth.m */; platformFilter = ios; };
12 | C2FFF12D244B49A800067451 /* SwItoBluetooth.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2FFF12C244B49A800067451 /* SwItoBluetooth.swift */; };
13 | /* End PBXBuildFile section */
14 |
15 | /* Begin PBXCopyFilesBuildPhase section */
16 | 58B511D91A9E6C8500147676 /* CopyFiles */ = {
17 | isa = PBXCopyFilesBuildPhase;
18 | buildActionMask = 2147483647;
19 | dstPath = "include/$(PRODUCT_NAME)";
20 | dstSubfolderSpec = 16;
21 | files = (
22 | );
23 | runOnlyForDeploymentPostprocessing = 0;
24 | };
25 | /* End PBXCopyFilesBuildPhase section */
26 |
27 | /* Begin PBXFileReference section */
28 | 134814201AA4EA6300B7C361 /* libItoBluetooth.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libItoBluetooth.a; sourceTree = BUILT_PRODUCTS_DIR; };
29 | 742754495354FD816D4E8E4A /* Pods-ItoBluetooth.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ItoBluetooth.release.xcconfig"; path = "Target Support Files/Pods-ItoBluetooth/Pods-ItoBluetooth.release.xcconfig"; sourceTree = ""; };
30 | 7EC1987031BCF80FBAC73D9C /* Pods_ItoBluetooth.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ItoBluetooth.framework; sourceTree = BUILT_PRODUCTS_DIR; };
31 | B3E7B5891CC2AC0600A0062D /* ItoBluetooth.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ItoBluetooth.m; sourceTree = ""; };
32 | C2FFF12C244B49A800067451 /* SwItoBluetooth.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwItoBluetooth.swift; sourceTree = ""; };
33 | C2FFF12E244B59A100067451 /* ItoBluetooth-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ItoBluetooth-Bridging-Header.h"; sourceTree = ""; };
34 | DD3AAB4ECDB5003D42DAF57C /* Pods-ItoBluetooth.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ItoBluetooth.debug.xcconfig"; path = "Target Support Files/Pods-ItoBluetooth/Pods-ItoBluetooth.debug.xcconfig"; sourceTree = ""; };
35 | /* End PBXFileReference section */
36 |
37 | /* Begin PBXFrameworksBuildPhase section */
38 | 58B511D81A9E6C8500147676 /* Frameworks */ = {
39 | isa = PBXFrameworksBuildPhase;
40 | buildActionMask = 2147483647;
41 | files = (
42 | 7BE90D4FB255B68DDBC03678 /* Pods_ItoBluetooth.framework in Frameworks */,
43 | );
44 | runOnlyForDeploymentPostprocessing = 0;
45 | };
46 | /* End PBXFrameworksBuildPhase section */
47 |
48 | /* Begin PBXGroup section */
49 | 134814211AA4EA7D00B7C361 /* Products */ = {
50 | isa = PBXGroup;
51 | children = (
52 | 134814201AA4EA6300B7C361 /* libItoBluetooth.a */,
53 | );
54 | name = Products;
55 | sourceTree = "";
56 | };
57 | 58B511D21A9E6C8500147676 = {
58 | isa = PBXGroup;
59 | children = (
60 | C2FFF12E244B59A100067451 /* ItoBluetooth-Bridging-Header.h */,
61 | C2FFF12C244B49A800067451 /* SwItoBluetooth.swift */,
62 | B3E7B5891CC2AC0600A0062D /* ItoBluetooth.m */,
63 | 134814211AA4EA7D00B7C361 /* Products */,
64 | FCA88AA95B551F7F4223E6AE /* Pods */,
65 | AEB34772B03280170E4EF634 /* Frameworks */,
66 | );
67 | sourceTree = "";
68 | };
69 | AEB34772B03280170E4EF634 /* Frameworks */ = {
70 | isa = PBXGroup;
71 | children = (
72 | 7EC1987031BCF80FBAC73D9C /* Pods_ItoBluetooth.framework */,
73 | );
74 | name = Frameworks;
75 | sourceTree = "";
76 | };
77 | FCA88AA95B551F7F4223E6AE /* Pods */ = {
78 | isa = PBXGroup;
79 | children = (
80 | DD3AAB4ECDB5003D42DAF57C /* Pods-ItoBluetooth.debug.xcconfig */,
81 | 742754495354FD816D4E8E4A /* Pods-ItoBluetooth.release.xcconfig */,
82 | );
83 | path = Pods;
84 | sourceTree = "";
85 | };
86 | /* End PBXGroup section */
87 |
88 | /* Begin PBXNativeTarget section */
89 | 58B511DA1A9E6C8500147676 /* ItoBluetooth */ = {
90 | isa = PBXNativeTarget;
91 | buildConfigurationList = 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "ItoBluetooth" */;
92 | buildPhases = (
93 | 07AA11322CC909FC40CF9B9B /* [CP] Check Pods Manifest.lock */,
94 | 58B511D71A9E6C8500147676 /* Sources */,
95 | 58B511D81A9E6C8500147676 /* Frameworks */,
96 | 58B511D91A9E6C8500147676 /* CopyFiles */,
97 | );
98 | buildRules = (
99 | );
100 | dependencies = (
101 | );
102 | name = ItoBluetooth;
103 | productName = RCTDataManager;
104 | productReference = 134814201AA4EA6300B7C361 /* libItoBluetooth.a */;
105 | productType = "com.apple.product-type.library.static";
106 | };
107 | /* End PBXNativeTarget section */
108 |
109 | /* Begin PBXProject section */
110 | 58B511D31A9E6C8500147676 /* Project object */ = {
111 | isa = PBXProject;
112 | attributes = {
113 | LastUpgradeCheck = 0920;
114 | ORGANIZATIONNAME = Facebook;
115 | TargetAttributes = {
116 | 58B511DA1A9E6C8500147676 = {
117 | CreatedOnToolsVersion = 6.1.1;
118 | LastSwiftMigration = 1120;
119 | };
120 | };
121 | };
122 | buildConfigurationList = 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "ItoBluetooth" */;
123 | compatibilityVersion = "Xcode 3.2";
124 | developmentRegion = English;
125 | hasScannedForEncodings = 0;
126 | knownRegions = (
127 | English,
128 | en,
129 | );
130 | mainGroup = 58B511D21A9E6C8500147676;
131 | productRefGroup = 58B511D21A9E6C8500147676;
132 | projectDirPath = "";
133 | projectRoot = "";
134 | targets = (
135 | 58B511DA1A9E6C8500147676 /* ItoBluetooth */,
136 | );
137 | };
138 | /* End PBXProject section */
139 |
140 | /* Begin PBXShellScriptBuildPhase section */
141 | 07AA11322CC909FC40CF9B9B /* [CP] Check Pods Manifest.lock */ = {
142 | isa = PBXShellScriptBuildPhase;
143 | buildActionMask = 2147483647;
144 | files = (
145 | );
146 | inputFileListPaths = (
147 | );
148 | inputPaths = (
149 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
150 | "${PODS_ROOT}/Manifest.lock",
151 | );
152 | name = "[CP] Check Pods Manifest.lock";
153 | outputFileListPaths = (
154 | );
155 | outputPaths = (
156 | "$(DERIVED_FILE_DIR)/Pods-ItoBluetooth-checkManifestLockResult.txt",
157 | );
158 | runOnlyForDeploymentPostprocessing = 0;
159 | shellPath = /bin/sh;
160 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
161 | showEnvVarsInLog = 0;
162 | };
163 | /* End PBXShellScriptBuildPhase section */
164 |
165 | /* Begin PBXSourcesBuildPhase section */
166 | 58B511D71A9E6C8500147676 /* Sources */ = {
167 | isa = PBXSourcesBuildPhase;
168 | buildActionMask = 2147483647;
169 | files = (
170 | C2FFF12D244B49A800067451 /* SwItoBluetooth.swift in Sources */,
171 | B3E7B58A1CC2AC0600A0062D /* ItoBluetooth.m in Sources */,
172 | );
173 | runOnlyForDeploymentPostprocessing = 0;
174 | };
175 | /* End PBXSourcesBuildPhase section */
176 |
177 | /* Begin XCBuildConfiguration section */
178 | 58B511ED1A9E6C8500147676 /* Debug */ = {
179 | isa = XCBuildConfiguration;
180 | buildSettings = {
181 | ALWAYS_SEARCH_USER_PATHS = NO;
182 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
183 | CLANG_CXX_LIBRARY = "libc++";
184 | CLANG_ENABLE_MODULES = YES;
185 | CLANG_ENABLE_OBJC_ARC = YES;
186 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
187 | CLANG_WARN_BOOL_CONVERSION = YES;
188 | CLANG_WARN_COMMA = YES;
189 | CLANG_WARN_CONSTANT_CONVERSION = YES;
190 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
191 | CLANG_WARN_EMPTY_BODY = YES;
192 | CLANG_WARN_ENUM_CONVERSION = YES;
193 | CLANG_WARN_INFINITE_RECURSION = YES;
194 | CLANG_WARN_INT_CONVERSION = YES;
195 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
196 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
197 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
198 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
199 | CLANG_WARN_STRICT_PROTOTYPES = YES;
200 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
201 | CLANG_WARN_UNREACHABLE_CODE = YES;
202 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
203 | COPY_PHASE_STRIP = NO;
204 | ENABLE_STRICT_OBJC_MSGSEND = YES;
205 | ENABLE_TESTABILITY = YES;
206 | GCC_C_LANGUAGE_STANDARD = gnu99;
207 | GCC_DYNAMIC_NO_PIC = NO;
208 | GCC_NO_COMMON_BLOCKS = YES;
209 | GCC_OPTIMIZATION_LEVEL = 0;
210 | GCC_PREPROCESSOR_DEFINITIONS = (
211 | "DEBUG=1",
212 | "$(inherited)",
213 | );
214 | GCC_SYMBOLS_PRIVATE_EXTERN = NO;
215 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
216 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
217 | GCC_WARN_UNDECLARED_SELECTOR = YES;
218 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
219 | GCC_WARN_UNUSED_FUNCTION = YES;
220 | GCC_WARN_UNUSED_VARIABLE = YES;
221 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
222 | MTL_ENABLE_DEBUG_INFO = YES;
223 | ONLY_ACTIVE_ARCH = YES;
224 | SDKROOT = iphoneos;
225 | };
226 | name = Debug;
227 | };
228 | 58B511EE1A9E6C8500147676 /* Release */ = {
229 | isa = XCBuildConfiguration;
230 | buildSettings = {
231 | ALWAYS_SEARCH_USER_PATHS = NO;
232 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
233 | CLANG_CXX_LIBRARY = "libc++";
234 | CLANG_ENABLE_MODULES = YES;
235 | CLANG_ENABLE_OBJC_ARC = YES;
236 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
237 | CLANG_WARN_BOOL_CONVERSION = YES;
238 | CLANG_WARN_COMMA = YES;
239 | CLANG_WARN_CONSTANT_CONVERSION = YES;
240 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
241 | CLANG_WARN_EMPTY_BODY = YES;
242 | CLANG_WARN_ENUM_CONVERSION = YES;
243 | CLANG_WARN_INFINITE_RECURSION = YES;
244 | CLANG_WARN_INT_CONVERSION = YES;
245 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
246 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
247 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
248 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
249 | CLANG_WARN_STRICT_PROTOTYPES = YES;
250 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
251 | CLANG_WARN_UNREACHABLE_CODE = YES;
252 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
253 | COPY_PHASE_STRIP = YES;
254 | ENABLE_NS_ASSERTIONS = NO;
255 | ENABLE_STRICT_OBJC_MSGSEND = YES;
256 | GCC_C_LANGUAGE_STANDARD = gnu99;
257 | GCC_NO_COMMON_BLOCKS = YES;
258 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
259 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
260 | GCC_WARN_UNDECLARED_SELECTOR = YES;
261 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
262 | GCC_WARN_UNUSED_FUNCTION = YES;
263 | GCC_WARN_UNUSED_VARIABLE = YES;
264 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
265 | MTL_ENABLE_DEBUG_INFO = NO;
266 | SDKROOT = iphoneos;
267 | VALIDATE_PRODUCT = YES;
268 | };
269 | name = Release;
270 | };
271 | 58B511F01A9E6C8500147676 /* Debug */ = {
272 | isa = XCBuildConfiguration;
273 | baseConfigurationReference = DD3AAB4ECDB5003D42DAF57C /* Pods-ItoBluetooth.debug.xcconfig */;
274 | buildSettings = {
275 | CLANG_ENABLE_MODULES = YES;
276 | HEADER_SEARCH_PATHS = (
277 | "$(inherited)",
278 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
279 | "$(SRCROOT)/../../../React/**",
280 | "$(SRCROOT)/../../react-native/React/**",
281 | "$(SRCROOT)/../../node_modules/react-native/React/**",
282 | );
283 | LD_RUNPATH_SEARCH_PATHS = (
284 | "$(inherited)",
285 | "@executable_path/Frameworks",
286 | "@loader_path/Frameworks",
287 | );
288 | LIBRARY_SEARCH_PATHS = "$(inherited)";
289 | OTHER_LDFLAGS = "-ObjC";
290 | PRODUCT_NAME = ItoBluetooth;
291 | SKIP_INSTALL = YES;
292 | SWIFT_OBJC_BRIDGING_HEADER = "ItoBluetooth-Bridging-Header.h";
293 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
294 | SWIFT_VERSION = 5.0;
295 | };
296 | name = Debug;
297 | };
298 | 58B511F11A9E6C8500147676 /* Release */ = {
299 | isa = XCBuildConfiguration;
300 | baseConfigurationReference = 742754495354FD816D4E8E4A /* Pods-ItoBluetooth.release.xcconfig */;
301 | buildSettings = {
302 | CLANG_ENABLE_MODULES = YES;
303 | HEADER_SEARCH_PATHS = (
304 | "$(inherited)",
305 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
306 | "$(SRCROOT)/../../../React/**",
307 | "$(SRCROOT)/../../react-native/React/**",
308 | "$(SRCROOT)/../../node_modules/react-native/React/**",
309 | );
310 | LD_RUNPATH_SEARCH_PATHS = (
311 | "$(inherited)",
312 | "@executable_path/Frameworks",
313 | "@loader_path/Frameworks",
314 | );
315 | LIBRARY_SEARCH_PATHS = "$(inherited)";
316 | OTHER_LDFLAGS = "-ObjC";
317 | PRODUCT_NAME = ItoBluetooth;
318 | SKIP_INSTALL = YES;
319 | SWIFT_OBJC_BRIDGING_HEADER = "ItoBluetooth-Bridging-Header.h";
320 | SWIFT_VERSION = 5.0;
321 | };
322 | name = Release;
323 | };
324 | /* End XCBuildConfiguration section */
325 |
326 | /* Begin XCConfigurationList section */
327 | 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "ItoBluetooth" */ = {
328 | isa = XCConfigurationList;
329 | buildConfigurations = (
330 | 58B511ED1A9E6C8500147676 /* Debug */,
331 | 58B511EE1A9E6C8500147676 /* Release */,
332 | );
333 | defaultConfigurationIsVisible = 0;
334 | defaultConfigurationName = Release;
335 | };
336 | 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "ItoBluetooth" */ = {
337 | isa = XCConfigurationList;
338 | buildConfigurations = (
339 | 58B511F01A9E6C8500147676 /* Debug */,
340 | 58B511F11A9E6C8500147676 /* Release */,
341 | );
342 | defaultConfigurationIsVisible = 0;
343 | defaultConfigurationName = Release;
344 | };
345 | /* End XCConfigurationList section */
346 | };
347 | rootObject = 58B511D31A9E6C8500147676 /* Project object */;
348 | }
349 |
--------------------------------------------------------------------------------
/ios/ItoBluetooth.xcodeproj/xcshareddata/xcschemes/ItoBluetooth.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
43 |
44 |
50 |
51 |
57 |
58 |
59 |
60 |
62 |
63 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/ios/ItoBluetooth.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/ios/ItoBluetooth.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Podfile:
--------------------------------------------------------------------------------
1 | platform :ios, '9.0'
2 | use_frameworks!
3 |
4 | target 'ItoBluetooth' do
5 | pod 'FBLazyVector', :path => "../node_modules/react-native/Libraries/FBLazyVector"
6 | pod 'FBReactNativeSpec', :path => "../node_modules/react-native/Libraries/FBReactNativeSpec"
7 | pod 'RCTRequired', :path => "../node_modules/react-native/Libraries/RCTRequired"
8 | pod 'RCTTypeSafety', :path => "../node_modules/react-native/Libraries/TypeSafety"
9 | pod 'React', :path => '../node_modules/react-native/'
10 | pod 'React-Core', :path => '../node_modules/react-native/'
11 | pod 'React-CoreModules', :path => '../node_modules/react-native/React/CoreModules'
12 | pod 'React-Core/DevSupport', :path => '../node_modules/react-native/'
13 | pod 'React-RCTActionSheet', :path => '../node_modules/react-native/Libraries/ActionSheetIOS'
14 | pod 'React-RCTAnimation', :path => '../node_modules/react-native/Libraries/NativeAnimation'
15 | pod 'React-RCTBlob', :path => '../node_modules/react-native/Libraries/Blob'
16 | pod 'React-RCTImage', :path => '../node_modules/react-native/Libraries/Image'
17 | pod 'React-RCTLinking', :path => '../node_modules/react-native/Libraries/LinkingIOS'
18 | pod 'React-RCTNetwork', :path => '../node_modules/react-native/Libraries/Network'
19 | pod 'React-RCTSettings', :path => '../node_modules/react-native/Libraries/Settings'
20 | pod 'React-RCTText', :path => '../node_modules/react-native/Libraries/Text'
21 | pod 'React-RCTVibration', :path => '../node_modules/react-native/Libraries/Vibration'
22 | pod 'React-Core/RCTWebSocket', :path => '../node_modules/react-native/'
23 |
24 | pod 'React-cxxreact', :path => '../node_modules/react-native/ReactCommon/cxxreact'
25 | pod 'React-jsi', :path => '../node_modules/react-native/ReactCommon/jsi'
26 | pod 'React-jsiexecutor', :path => '../node_modules/react-native/ReactCommon/jsiexecutor'
27 | pod 'React-jsinspector', :path => '../node_modules/react-native/ReactCommon/jsinspector'
28 | pod 'ReactCommon/callinvoker', :path => "../node_modules/react-native/ReactCommon"
29 | pod 'ReactCommon/turbomodule/core', :path => "../node_modules/react-native/ReactCommon"
30 | pod 'Yoga', :path => '../node_modules/react-native/ReactCommon/yoga'
31 |
32 | pod 'DoubleConversion', :podspec => '../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec'
33 | pod 'glog', :podspec => '../node_modules/react-native/third-party-podspecs/glog.podspec'
34 | pod 'Folly', :podspec => '../node_modules/react-native/third-party-podspecs/Folly.podspec'
35 | end
36 |
--------------------------------------------------------------------------------
/ios/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - boost-for-react-native (1.63.0)
3 | - DoubleConversion (1.1.6)
4 | - FBLazyVector (0.62.2)
5 | - FBReactNativeSpec (0.62.2):
6 | - Folly (= 2018.10.22.00)
7 | - RCTRequired (= 0.62.2)
8 | - RCTTypeSafety (= 0.62.2)
9 | - React-Core (= 0.62.2)
10 | - React-jsi (= 0.62.2)
11 | - ReactCommon/turbomodule/core (= 0.62.2)
12 | - Folly (2018.10.22.00):
13 | - boost-for-react-native
14 | - DoubleConversion
15 | - Folly/Default (= 2018.10.22.00)
16 | - glog
17 | - Folly/Default (2018.10.22.00):
18 | - boost-for-react-native
19 | - DoubleConversion
20 | - glog
21 | - glog (0.3.5)
22 | - RCTRequired (0.62.2)
23 | - RCTTypeSafety (0.62.2):
24 | - FBLazyVector (= 0.62.2)
25 | - Folly (= 2018.10.22.00)
26 | - RCTRequired (= 0.62.2)
27 | - React-Core (= 0.62.2)
28 | - React (0.62.2):
29 | - React-Core (= 0.62.2)
30 | - React-Core/DevSupport (= 0.62.2)
31 | - React-Core/RCTWebSocket (= 0.62.2)
32 | - React-RCTActionSheet (= 0.62.2)
33 | - React-RCTAnimation (= 0.62.2)
34 | - React-RCTBlob (= 0.62.2)
35 | - React-RCTImage (= 0.62.2)
36 | - React-RCTLinking (= 0.62.2)
37 | - React-RCTNetwork (= 0.62.2)
38 | - React-RCTSettings (= 0.62.2)
39 | - React-RCTText (= 0.62.2)
40 | - React-RCTVibration (= 0.62.2)
41 | - React-Core (0.62.2):
42 | - Folly (= 2018.10.22.00)
43 | - glog
44 | - React-Core/Default (= 0.62.2)
45 | - React-cxxreact (= 0.62.2)
46 | - React-jsi (= 0.62.2)
47 | - React-jsiexecutor (= 0.62.2)
48 | - Yoga
49 | - React-Core/CoreModulesHeaders (0.62.2):
50 | - Folly (= 2018.10.22.00)
51 | - glog
52 | - React-Core/Default
53 | - React-cxxreact (= 0.62.2)
54 | - React-jsi (= 0.62.2)
55 | - React-jsiexecutor (= 0.62.2)
56 | - Yoga
57 | - React-Core/Default (0.62.2):
58 | - Folly (= 2018.10.22.00)
59 | - glog
60 | - React-cxxreact (= 0.62.2)
61 | - React-jsi (= 0.62.2)
62 | - React-jsiexecutor (= 0.62.2)
63 | - Yoga
64 | - React-Core/DevSupport (0.62.2):
65 | - Folly (= 2018.10.22.00)
66 | - glog
67 | - React-Core/Default (= 0.62.2)
68 | - React-Core/RCTWebSocket (= 0.62.2)
69 | - React-cxxreact (= 0.62.2)
70 | - React-jsi (= 0.62.2)
71 | - React-jsiexecutor (= 0.62.2)
72 | - React-jsinspector (= 0.62.2)
73 | - Yoga
74 | - React-Core/RCTActionSheetHeaders (0.62.2):
75 | - Folly (= 2018.10.22.00)
76 | - glog
77 | - React-Core/Default
78 | - React-cxxreact (= 0.62.2)
79 | - React-jsi (= 0.62.2)
80 | - React-jsiexecutor (= 0.62.2)
81 | - Yoga
82 | - React-Core/RCTAnimationHeaders (0.62.2):
83 | - Folly (= 2018.10.22.00)
84 | - glog
85 | - React-Core/Default
86 | - React-cxxreact (= 0.62.2)
87 | - React-jsi (= 0.62.2)
88 | - React-jsiexecutor (= 0.62.2)
89 | - Yoga
90 | - React-Core/RCTBlobHeaders (0.62.2):
91 | - Folly (= 2018.10.22.00)
92 | - glog
93 | - React-Core/Default
94 | - React-cxxreact (= 0.62.2)
95 | - React-jsi (= 0.62.2)
96 | - React-jsiexecutor (= 0.62.2)
97 | - Yoga
98 | - React-Core/RCTImageHeaders (0.62.2):
99 | - Folly (= 2018.10.22.00)
100 | - glog
101 | - React-Core/Default
102 | - React-cxxreact (= 0.62.2)
103 | - React-jsi (= 0.62.2)
104 | - React-jsiexecutor (= 0.62.2)
105 | - Yoga
106 | - React-Core/RCTLinkingHeaders (0.62.2):
107 | - Folly (= 2018.10.22.00)
108 | - glog
109 | - React-Core/Default
110 | - React-cxxreact (= 0.62.2)
111 | - React-jsi (= 0.62.2)
112 | - React-jsiexecutor (= 0.62.2)
113 | - Yoga
114 | - React-Core/RCTNetworkHeaders (0.62.2):
115 | - Folly (= 2018.10.22.00)
116 | - glog
117 | - React-Core/Default
118 | - React-cxxreact (= 0.62.2)
119 | - React-jsi (= 0.62.2)
120 | - React-jsiexecutor (= 0.62.2)
121 | - Yoga
122 | - React-Core/RCTSettingsHeaders (0.62.2):
123 | - Folly (= 2018.10.22.00)
124 | - glog
125 | - React-Core/Default
126 | - React-cxxreact (= 0.62.2)
127 | - React-jsi (= 0.62.2)
128 | - React-jsiexecutor (= 0.62.2)
129 | - Yoga
130 | - React-Core/RCTTextHeaders (0.62.2):
131 | - Folly (= 2018.10.22.00)
132 | - glog
133 | - React-Core/Default
134 | - React-cxxreact (= 0.62.2)
135 | - React-jsi (= 0.62.2)
136 | - React-jsiexecutor (= 0.62.2)
137 | - Yoga
138 | - React-Core/RCTVibrationHeaders (0.62.2):
139 | - Folly (= 2018.10.22.00)
140 | - glog
141 | - React-Core/Default
142 | - React-cxxreact (= 0.62.2)
143 | - React-jsi (= 0.62.2)
144 | - React-jsiexecutor (= 0.62.2)
145 | - Yoga
146 | - React-Core/RCTWebSocket (0.62.2):
147 | - Folly (= 2018.10.22.00)
148 | - glog
149 | - React-Core/Default (= 0.62.2)
150 | - React-cxxreact (= 0.62.2)
151 | - React-jsi (= 0.62.2)
152 | - React-jsiexecutor (= 0.62.2)
153 | - Yoga
154 | - React-CoreModules (0.62.2):
155 | - FBReactNativeSpec (= 0.62.2)
156 | - Folly (= 2018.10.22.00)
157 | - RCTTypeSafety (= 0.62.2)
158 | - React-Core/CoreModulesHeaders (= 0.62.2)
159 | - React-RCTImage (= 0.62.2)
160 | - ReactCommon/turbomodule/core (= 0.62.2)
161 | - React-cxxreact (0.62.2):
162 | - boost-for-react-native (= 1.63.0)
163 | - DoubleConversion
164 | - Folly (= 2018.10.22.00)
165 | - glog
166 | - React-jsinspector (= 0.62.2)
167 | - React-jsi (0.62.2):
168 | - boost-for-react-native (= 1.63.0)
169 | - DoubleConversion
170 | - Folly (= 2018.10.22.00)
171 | - glog
172 | - React-jsi/Default (= 0.62.2)
173 | - React-jsi/Default (0.62.2):
174 | - boost-for-react-native (= 1.63.0)
175 | - DoubleConversion
176 | - Folly (= 2018.10.22.00)
177 | - glog
178 | - React-jsiexecutor (0.62.2):
179 | - DoubleConversion
180 | - Folly (= 2018.10.22.00)
181 | - glog
182 | - React-cxxreact (= 0.62.2)
183 | - React-jsi (= 0.62.2)
184 | - React-jsinspector (0.62.2)
185 | - React-RCTActionSheet (0.62.2):
186 | - React-Core/RCTActionSheetHeaders (= 0.62.2)
187 | - React-RCTAnimation (0.62.2):
188 | - FBReactNativeSpec (= 0.62.2)
189 | - Folly (= 2018.10.22.00)
190 | - RCTTypeSafety (= 0.62.2)
191 | - React-Core/RCTAnimationHeaders (= 0.62.2)
192 | - ReactCommon/turbomodule/core (= 0.62.2)
193 | - React-RCTBlob (0.62.2):
194 | - FBReactNativeSpec (= 0.62.2)
195 | - Folly (= 2018.10.22.00)
196 | - React-Core/RCTBlobHeaders (= 0.62.2)
197 | - React-Core/RCTWebSocket (= 0.62.2)
198 | - React-jsi (= 0.62.2)
199 | - React-RCTNetwork (= 0.62.2)
200 | - ReactCommon/turbomodule/core (= 0.62.2)
201 | - React-RCTImage (0.62.2):
202 | - FBReactNativeSpec (= 0.62.2)
203 | - Folly (= 2018.10.22.00)
204 | - RCTTypeSafety (= 0.62.2)
205 | - React-Core/RCTImageHeaders (= 0.62.2)
206 | - React-RCTNetwork (= 0.62.2)
207 | - ReactCommon/turbomodule/core (= 0.62.2)
208 | - React-RCTLinking (0.62.2):
209 | - FBReactNativeSpec (= 0.62.2)
210 | - React-Core/RCTLinkingHeaders (= 0.62.2)
211 | - ReactCommon/turbomodule/core (= 0.62.2)
212 | - React-RCTNetwork (0.62.2):
213 | - FBReactNativeSpec (= 0.62.2)
214 | - Folly (= 2018.10.22.00)
215 | - RCTTypeSafety (= 0.62.2)
216 | - React-Core/RCTNetworkHeaders (= 0.62.2)
217 | - ReactCommon/turbomodule/core (= 0.62.2)
218 | - React-RCTSettings (0.62.2):
219 | - FBReactNativeSpec (= 0.62.2)
220 | - Folly (= 2018.10.22.00)
221 | - RCTTypeSafety (= 0.62.2)
222 | - React-Core/RCTSettingsHeaders (= 0.62.2)
223 | - ReactCommon/turbomodule/core (= 0.62.2)
224 | - React-RCTText (0.62.2):
225 | - React-Core/RCTTextHeaders (= 0.62.2)
226 | - React-RCTVibration (0.62.2):
227 | - FBReactNativeSpec (= 0.62.2)
228 | - Folly (= 2018.10.22.00)
229 | - React-Core/RCTVibrationHeaders (= 0.62.2)
230 | - ReactCommon/turbomodule/core (= 0.62.2)
231 | - ReactCommon/callinvoker (0.62.2):
232 | - DoubleConversion
233 | - Folly (= 2018.10.22.00)
234 | - glog
235 | - React-cxxreact (= 0.62.2)
236 | - ReactCommon/turbomodule/core (0.62.2):
237 | - DoubleConversion
238 | - Folly (= 2018.10.22.00)
239 | - glog
240 | - React-Core (= 0.62.2)
241 | - React-cxxreact (= 0.62.2)
242 | - React-jsi (= 0.62.2)
243 | - ReactCommon/callinvoker (= 0.62.2)
244 | - Yoga (1.14.0)
245 |
246 | DEPENDENCIES:
247 | - DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
248 | - FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`)
249 | - FBReactNativeSpec (from `../node_modules/react-native/Libraries/FBReactNativeSpec`)
250 | - Folly (from `../node_modules/react-native/third-party-podspecs/Folly.podspec`)
251 | - glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`)
252 | - RCTRequired (from `../node_modules/react-native/Libraries/RCTRequired`)
253 | - RCTTypeSafety (from `../node_modules/react-native/Libraries/TypeSafety`)
254 | - React (from `../node_modules/react-native/`)
255 | - React-Core (from `../node_modules/react-native/`)
256 | - React-Core/DevSupport (from `../node_modules/react-native/`)
257 | - React-Core/RCTWebSocket (from `../node_modules/react-native/`)
258 | - React-CoreModules (from `../node_modules/react-native/React/CoreModules`)
259 | - React-cxxreact (from `../node_modules/react-native/ReactCommon/cxxreact`)
260 | - React-jsi (from `../node_modules/react-native/ReactCommon/jsi`)
261 | - React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`)
262 | - React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector`)
263 | - React-RCTActionSheet (from `../node_modules/react-native/Libraries/ActionSheetIOS`)
264 | - React-RCTAnimation (from `../node_modules/react-native/Libraries/NativeAnimation`)
265 | - React-RCTBlob (from `../node_modules/react-native/Libraries/Blob`)
266 | - React-RCTImage (from `../node_modules/react-native/Libraries/Image`)
267 | - React-RCTLinking (from `../node_modules/react-native/Libraries/LinkingIOS`)
268 | - React-RCTNetwork (from `../node_modules/react-native/Libraries/Network`)
269 | - React-RCTSettings (from `../node_modules/react-native/Libraries/Settings`)
270 | - React-RCTText (from `../node_modules/react-native/Libraries/Text`)
271 | - React-RCTVibration (from `../node_modules/react-native/Libraries/Vibration`)
272 | - ReactCommon/callinvoker (from `../node_modules/react-native/ReactCommon`)
273 | - ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`)
274 | - Yoga (from `../node_modules/react-native/ReactCommon/yoga`)
275 |
276 | SPEC REPOS:
277 | https://github.com/cocoapods/specs.git:
278 | - boost-for-react-native
279 |
280 | EXTERNAL SOURCES:
281 | DoubleConversion:
282 | :podspec: "../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec"
283 | FBLazyVector:
284 | :path: "../node_modules/react-native/Libraries/FBLazyVector"
285 | FBReactNativeSpec:
286 | :path: "../node_modules/react-native/Libraries/FBReactNativeSpec"
287 | Folly:
288 | :podspec: "../node_modules/react-native/third-party-podspecs/Folly.podspec"
289 | glog:
290 | :podspec: "../node_modules/react-native/third-party-podspecs/glog.podspec"
291 | RCTRequired:
292 | :path: "../node_modules/react-native/Libraries/RCTRequired"
293 | RCTTypeSafety:
294 | :path: "../node_modules/react-native/Libraries/TypeSafety"
295 | React:
296 | :path: "../node_modules/react-native/"
297 | React-Core:
298 | :path: "../node_modules/react-native/"
299 | React-CoreModules:
300 | :path: "../node_modules/react-native/React/CoreModules"
301 | React-cxxreact:
302 | :path: "../node_modules/react-native/ReactCommon/cxxreact"
303 | React-jsi:
304 | :path: "../node_modules/react-native/ReactCommon/jsi"
305 | React-jsiexecutor:
306 | :path: "../node_modules/react-native/ReactCommon/jsiexecutor"
307 | React-jsinspector:
308 | :path: "../node_modules/react-native/ReactCommon/jsinspector"
309 | React-RCTActionSheet:
310 | :path: "../node_modules/react-native/Libraries/ActionSheetIOS"
311 | React-RCTAnimation:
312 | :path: "../node_modules/react-native/Libraries/NativeAnimation"
313 | React-RCTBlob:
314 | :path: "../node_modules/react-native/Libraries/Blob"
315 | React-RCTImage:
316 | :path: "../node_modules/react-native/Libraries/Image"
317 | React-RCTLinking:
318 | :path: "../node_modules/react-native/Libraries/LinkingIOS"
319 | React-RCTNetwork:
320 | :path: "../node_modules/react-native/Libraries/Network"
321 | React-RCTSettings:
322 | :path: "../node_modules/react-native/Libraries/Settings"
323 | React-RCTText:
324 | :path: "../node_modules/react-native/Libraries/Text"
325 | React-RCTVibration:
326 | :path: "../node_modules/react-native/Libraries/Vibration"
327 | ReactCommon:
328 | :path: "../node_modules/react-native/ReactCommon"
329 | Yoga:
330 | :path: "../node_modules/react-native/ReactCommon/yoga"
331 |
332 | SPEC CHECKSUMS:
333 | boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c
334 | DoubleConversion: 5805e889d232975c086db112ece9ed034df7a0b2
335 | FBLazyVector: 4aab18c93cd9546e4bfed752b4084585eca8b245
336 | FBReactNativeSpec: 5465d51ccfeecb7faa12f9ae0024f2044ce4044e
337 | Folly: 30e7936e1c45c08d884aa59369ed951a8e68cf51
338 | glog: 1f3da668190260b06b429bb211bfbee5cd790c28
339 | RCTRequired: cec6a34b3ac8a9915c37e7e4ad3aa74726ce4035
340 | RCTTypeSafety: 93006131180074cffa227a1075802c89a49dd4ce
341 | React: 29a8b1a02bd764fb7644ef04019270849b9a7ac3
342 | React-Core: b12bffb3f567fdf99510acb716ef1abd426e0e05
343 | React-CoreModules: 4a9b87bbe669d6c3173c0132c3328e3b000783d0
344 | React-cxxreact: e65f9c2ba0ac5be946f53548c1aaaee5873a8103
345 | React-jsi: b6dc94a6a12ff98e8877287a0b7620d365201161
346 | React-jsiexecutor: 1540d1c01bb493ae3124ed83351b1b6a155db7da
347 | React-jsinspector: 512e560d0e985d0e8c479a54a4e5c147a9c83493
348 | React-RCTActionSheet: f41ea8a811aac770e0cc6e0ad6b270c644ea8b7c
349 | React-RCTAnimation: 49ab98b1c1ff4445148b72a3d61554138565bad0
350 | React-RCTBlob: a332773f0ebc413a0ce85942a55b064471587a71
351 | React-RCTImage: e70be9b9c74fe4e42d0005f42cace7981c994ac3
352 | React-RCTLinking: c1b9739a88d56ecbec23b7f63650e44672ab2ad2
353 | React-RCTNetwork: 73138b6f45e5a2768ad93f3d57873c2a18d14b44
354 | React-RCTSettings: 6e3738a87e21b39a8cb08d627e68c44acf1e325a
355 | React-RCTText: fae545b10cfdb3d247c36c56f61a94cfd6dba41d
356 | React-RCTVibration: 4356114dbcba4ce66991096e51a66e61eda51256
357 | ReactCommon: ed4e11d27609d571e7eee8b65548efc191116eb3
358 | Yoga: 3ebccbdd559724312790e7742142d062476b698e
359 |
360 | PODFILE CHECKSUM: 2268b8c40ebfbed7816bf4a820665453ec09fecc
361 |
362 | COCOAPODS: 1.6.1
363 |
--------------------------------------------------------------------------------
/ios/SwItoBluetooth.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwItoBluetooth.swift
3 | //
4 | //
5 | // Created by Dieder Timmers on 18/04/2020.
6 | //
7 |
8 | import Foundation
9 | @objc
10 | public class SwItoBluetooth :NSObject {
11 | @objc
12 | func isPossiblyInfected() -> NSNumber{
13 | return true;
14 | }
15 |
16 | @objc
17 | func restartTracingService(){
18 |
19 | }
20 |
21 | @objc
22 | func getLatestFetchTime() -> NSNumber{
23 | return 0;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-native-ito",
3 | "title": "React Native Ito Bluetooth",
4 | "version": "1.0.0",
5 | "description": "TODO",
6 | "main": "index.js",
7 | "scripts": {
8 | "test": "echo \"Error: no test specified\" && exit 1"
9 | },
10 | "repository": {
11 | "type": "git",
12 | "url": "git+https://github.com/ito-org/react-native-ito.git",
13 | "baseUrl": "https://github.com/ito-org/react-native-ito"
14 | },
15 | "keywords": [
16 | "react-native"
17 | ],
18 | "author": {
19 | "name": "Christian Romberg",
20 | "email": "info@ito-app.org"
21 | },
22 | "readmeFilename": "README.md",
23 | "peerDependencies": {
24 | "react": "^16.8.1",
25 | "react-native": ">=0.60.0-rc.0 <1.0.x"
26 | },
27 | "devDependencies": {
28 | "react": "^16.9.0",
29 | "react-native": "^0.62.2"
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/react-native-ito.podspec:
--------------------------------------------------------------------------------
1 | require "json"
2 |
3 | package = JSON.parse(File.read(File.join(__dir__, "package.json")))
4 |
5 | Pod::Spec.new do |s|
6 | s.name = "react-native-ito"
7 | s.version = package["version"]
8 | s.summary = package["description"]
9 | s.description = <<-DESC
10 | react-native-ito
11 | DESC
12 | s.homepage = "https://github.com/github_account/react-native-ito"
13 | s.license = "MIT"
14 | # s.license = { :type => "MIT", :file => "FILE_LICENSE" }
15 | s.authors = { "Your Name" => "yourname@email.com" }
16 | s.platforms = { :ios => "9.0" }
17 | s.source = { :git => "https://github.com/github_account/react-native-ito.git", :tag => "#{s.version}" }
18 |
19 | s.source_files = "ios/**/*.{h,m,swift}"
20 | s.requires_arc = true
21 |
22 | s.dependency "React"
23 | # ...
24 | # s.dependency "..."
25 | end
26 |
27 |
--------------------------------------------------------------------------------