diff --git a/drip-android-observer-master/.gitignore b/drip-android-observer-master/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..fc786aded73fbd2d8c4dd31c40c6757c4f127b54 --- /dev/null +++ b/drip-android-observer-master/.gitignore @@ -0,0 +1,2 @@ +Android/.idea +Android/app/src/main/res/values/google_maps_api.xml diff --git a/drip-android-observer-master/Android/.gitignore b/drip-android-observer-master/Android/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..1e0e9fc6134924bf07da4b8bfacd231f4c0dc66d --- /dev/null +++ b/drip-android-observer-master/Android/.gitignore @@ -0,0 +1,49 @@ +# Built application files +*.apk +*.ap_ + +# Files for the ART/Dalvik VM +*.dex + +# Java class files +*.class + +# Generated files +bin/ +gen/ +out/ +# from https://github.com/github/gitignore/blob/master/Android.gitignore + +# Gradle files +.gradle/ +build/ + +# Local configuration file (sdk path, etc) +local.properties + +# Proguard folder generated by Eclipse +proguard/ + +# Log Files +*.log + +# Android Studio Navigation editor temp files +.navigation/ + +# Android Studio captures folder +captures/ + +# IntelliJ +*.iml +.idea/workspace.xml +.idea/tasks.xml +.idea/gradle.xml +.idea/assetWizardSettings.xml +.idea/dictionaries +.idea/libraries +.idea/caches +.idea/vcs.xml + + +# os x junk +.DS_Store diff --git a/drip-android-observer-master/Android/app/.gitignore b/drip-android-observer-master/Android/app/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..796b96d1c402326528b4ba3c12ee9d92d0e212e9 --- /dev/null +++ b/drip-android-observer-master/Android/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/drip-android-observer-master/Android/app/build.gradle b/drip-android-observer-master/Android/app/build.gradle new file mode 100644 index 0000000000000000000000000000000000000000..d208798d2e2680b727b2606be341cc86868b7391 --- /dev/null +++ b/drip-android-observer-master/Android/app/build.gradle @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2019 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + * + */ +apply plugin: 'com.android.application' +android { + def versionMajor = 2 + def versionMinor = 3 + def versionPatch = 0 + + compileSdkVersion 30 + buildToolsVersion = '29.0.3' + + defaultConfig { + applicationId "org.opendroneid.android" + minSdkVersion 26 + targetSdkVersion 30 + versionCode versionMajor * 10000 + versionMinor * 100 + versionPatch + versionName "${versionMajor}.${versionMinor}.${versionPatch}" + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + buildTypes { + debug { + versionNameSuffix ".debug" + resValue "string", "app_version", "${defaultConfig.versionName}${versionNameSuffix}" + } + release { + resValue "string", "app_version", "${defaultConfig.versionName}" + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility = 1.8 + targetCompatibility = 1.8 + } +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + + def support_version = "29.0.0" + implementation 'androidx.legacy:legacy-support-v4:1.0.0' + implementation 'com.google.android.material:material:1.1.0' + implementation 'com.mikepenz:fastadapter:3.3.1' + implementation 'com.mikepenz:fastadapter-commons:3.2.8' + implementation 'com.android.support:recyclerview-v7:' + support_version + implementation 'com.android.support:appcompat-v7:' + support_version + implementation 'com.android.support:support-fragment:' + support_version + implementation 'com.google.android.gms:play-services-maps:17.0.0' + implementation 'com.google.android.gms:play-services-location:17.1.0' + implementation 'androidx.constraintlayout:constraintlayout:1.1.3' + implementation 'org.bouncycastle:bcprov-jdk15to18:1.69' + implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' + testImplementation 'junit:junit:4.12' + androidTestImplementation 'androidx.test.ext:junit:1.1.1' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' +} diff --git a/drip-android-observer-master/Android/app/proguard-rules.pro b/drip-android-observer-master/Android/app/proguard-rules.pro new file mode 100644 index 0000000000000000000000000000000000000000..f1b424510da51fd82143bc74a0a801ae5a1e2fcd --- /dev/null +++ b/drip-android-observer-master/Android/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/drip-android-observer-master/Android/app/src/main/AndroidManifest.xml b/drip-android-observer-master/Android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000000000000000000000000000000000000..468171b3a0ec57f96af2800e590bb551a1c6555c --- /dev/null +++ b/drip-android-observer-master/Android/app/src/main/AndroidManifest.xml @@ -0,0 +1,55 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + package="org.dripdronescanner.android"> + + <uses-permission android:name="android.permission.BLUETOOTH" /> + <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> + <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> + <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> + <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> + + <uses-feature android:name="android.hardware.bluetooth" /> + <uses-feature android:name="android.hardware.wifi" /> + + <application + android:networkSecurityConfig="@xml/network_security_config" + android:allowBackup="true" + android:icon="@mipmap/ic_launcher" + android:label="@string/app_name" + android:roundIcon="@mipmap/ic_launcher_round" + android:supportsRtl="true" + android:theme="@style/AppTheme" + tools:replace="android:icon" + tools:ignore="GoogleAppIndexingWarning"> + + <uses-library android:name="org.apache.http.legacy" android:required="false"/> + + <!-- + The API key for Google Maps-based APIs is defined as a string resource. + (See the file "res/values/google_maps_api.xml"). + Note that the API key is linked to the encryption key used to sign the APK. + You need a different API key for each encryption key, including the release key that is used to + sign the APK for publishing. + You can define the keys for the debug and release targets in src/debug/ and src/release/. + --> + <activity + android:name=".app.DebugActivity" + android:screenOrientation="portrait"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + <activity android:name=".app.SettingsActivity"></activity><!-- <activity android:name=".MainActivity"> --> + <!-- <intent-filter> --> + <!-- <action android:name="android.intent.action.MAIN" /> --> + <!-- <category android:name="android.intent.category.LAUNCHER" /> --> + <!-- </intent-filter> --> + <!-- </activity> --> + <meta-data + android:name="com.google.android.geo.API_KEY" + android:value="@string/google_maps_key" /> + </application> +</manifest> \ No newline at end of file diff --git a/drip-android-observer-master/Android/app/src/main/ic_launcher-playstore.png b/drip-android-observer-master/Android/app/src/main/ic_launcher-playstore.png new file mode 100644 index 0000000000000000000000000000000000000000..07cfe3a11e42d940eb835b1f19ffc44d916c4580 Binary files /dev/null and b/drip-android-observer-master/Android/app/src/main/ic_launcher-playstore.png differ diff --git a/drip-android-observer-master/Android/app/src/main/ic_launcher-web.png b/drip-android-observer-master/Android/app/src/main/ic_launcher-web.png new file mode 100644 index 0000000000000000000000000000000000000000..4e685cdf937e745f3c4a5238af8bfa0f0257cb8d Binary files /dev/null and b/drip-android-observer-master/Android/app/src/main/ic_launcher-web.png differ diff --git a/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/Constants.java b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/Constants.java new file mode 100644 index 0000000000000000000000000000000000000000..b26ddd0b78ca112841430b13715d71f762216ab1 --- /dev/null +++ b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/Constants.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2019 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + * + */ +package org.dripdronescanner.android; + +public class Constants { + public static final int REQUEST_ENABLE_BT = 1; + public static final int FINE_LOCATION_PERMISSION_REQUEST_CODE = 2; + public static final int REQUEST_ENABLE_WIFI = 3; + + public static final String DELIM = ","; + + public static final int MAX_ID_BYTE_SIZE = 20; + public static final int MAX_DRIP_ID_BYTE_SIZE = 16; + public final static int ED25519_SIG_LENGTH = 64; + public static final int MAX_STRING_BYTE_SIZE = 23; + public static final int MAX_AUTH_DATA_PAGES = 5; + public static final int MAX_AUTH_PAGE_ZERO_SIZE = 17; + public static final int MAX_AUTH_PAGE_NON_ZERO_SIZE = 23; + public static final int MAX_AUTH_DATA = MAX_AUTH_PAGE_ZERO_SIZE + (MAX_AUTH_DATA_PAGES - 1) * MAX_AUTH_PAGE_NON_ZERO_SIZE; + public static final int MAX_MESSAGE_SIZE = 25; + public static final int MAX_MESSAGES_IN_PACK = 10; + public static final int MAX_MESSAGE_PACK_SIZE = MAX_MESSAGE_SIZE * MAX_MESSAGES_IN_PACK; + public static final int MAX_DRIP_AUTH_DATA_PAGES = 9; + public static final int MAX_DRIP_AUTH_DATA = 200; //bytes + public final static byte[] HIPv2_PREFIX = {0x20, 0x01, 0x00, 0x20}; // 28-bit prefix + public final static byte[] HIPv2_CONTEXT_ID = { + (byte) 0xf0, (byte) 0xef, (byte) 0xf0, 0x2f, (byte) 0xbf, (byte) 0xf4, + 0x3d, 0x0f, (byte) 0xe7, (byte) 0x93, 0x0c, 0x3c, 0x6e, 0x61, 0x74, (byte) 0xea + };//"F0 EF F0 2F BF F4 3D 0F E7 93 0C 3C 6E 61 74 EA"; + public final static byte[] ED25519_CURVE_ID = {0x00, 0x01}; // 2 byte curve id for ed25519 curve. + +} diff --git a/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/PermissionUtils.java b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/PermissionUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..ab35a90f06cacc1f8a4a1a3eb249f7769ce82baf --- /dev/null +++ b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/PermissionUtils.java @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.dripdronescanner.android; + +import android.Manifest; +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.DialogInterface; +import android.content.pm.PackageManager; +import android.os.Bundle; +import android.widget.Toast; + +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.app.ActivityCompat; +import androidx.fragment.app.DialogFragment; + +/** + * Utility class for access to runtime permissions. + */ +public abstract class PermissionUtils { + + /** + * Requests the fine location permission. If a rationale with an additional explanation should + * be shown to the user, displays a dialog that triggers the request. + */ + public static void requestPermission(AppCompatActivity activity, int requestId, + String permission, boolean finishActivity) { + if (ActivityCompat.shouldShowRequestPermissionRationale(activity, permission)) { + // Display a dialog with rationale. + PermissionUtils.RationaleDialog.newInstance(requestId, finishActivity) + .show(activity.getSupportFragmentManager(), "dialog"); + } else { + // Location permission has not been granted yet, request it. + ActivityCompat.requestPermissions(activity, new String[]{permission}, requestId); + + } + } + + /** + * Checks if the result contains a {@link PackageManager#PERMISSION_GRANTED} result for a + * permission from a runtime permissions request. + * + * @see androidx.core.app.ActivityCompat.OnRequestPermissionsResultCallback + */ + public static boolean isPermissionGranted(String[] grantPermissions, int[] grantResults, + String permission) { + for (int i = 0; i < grantPermissions.length; i++) { + if (permission.equals(grantPermissions[i])) { + return grantResults[i] == PackageManager.PERMISSION_GRANTED; + } + } + return false; + } + + /** + * A dialog that displays a permission denied message. + */ + public static class PermissionDeniedDialog extends DialogFragment { + + private static final String ARGUMENT_FINISH_ACTIVITY = "finish"; + + private boolean mFinishActivity = false; + + /** + * Creates a new instance of this dialog and optionally finishes the calling Activity + * when the 'Ok' button is clicked. + */ + public static PermissionDeniedDialog newInstance(boolean finishActivity) { + Bundle arguments = new Bundle(); + arguments.putBoolean(ARGUMENT_FINISH_ACTIVITY, finishActivity); + + PermissionDeniedDialog dialog = new PermissionDeniedDialog(); + dialog.setArguments(arguments); + return dialog; + } + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + mFinishActivity = getArguments().getBoolean(ARGUMENT_FINISH_ACTIVITY); + + return new AlertDialog.Builder(getActivity()) + .setMessage(R.string.location_permission_denied) + .setPositiveButton(android.R.string.ok, null) + .create(); + } + + @Override + public void onDismiss(DialogInterface dialog) { + super.onDismiss(dialog); + if (mFinishActivity) { + Toast.makeText(getActivity(), R.string.permission_required_toast, + Toast.LENGTH_SHORT).show(); + getActivity().finish(); + } + } + } + + /** + * A dialog that explains the use of the location permission and requests the necessary + * permission. + * <p> + * The activity should implement + * {@link androidx.core.app.ActivityCompat.OnRequestPermissionsResultCallback} + * to handle permit or denial of this permission request. + */ + public static class RationaleDialog extends DialogFragment { + + private static final String ARGUMENT_PERMISSION_REQUEST_CODE = "requestCode"; + + private static final String ARGUMENT_FINISH_ACTIVITY = "finish"; + + private boolean mFinishActivity = false; + + /** + * Creates a new instance of a dialog displaying the rationale for the use of the location + * permission. + * <p> + * The permission is requested after clicking 'ok'. + * + * @param requestCode Id of the request that is used to request the permission. It is + * returned to the + * {@link androidx.core.app.ActivityCompat.OnRequestPermissionsResultCallback}. + * @param finishActivity Whether the calling Activity should be finished if the dialog is + * cancelled. + */ + public static RationaleDialog newInstance(int requestCode, boolean finishActivity) { + Bundle arguments = new Bundle(); + arguments.putInt(ARGUMENT_PERMISSION_REQUEST_CODE, requestCode); + arguments.putBoolean(ARGUMENT_FINISH_ACTIVITY, finishActivity); + RationaleDialog dialog = new RationaleDialog(); + dialog.setArguments(arguments); + return dialog; + } + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + Bundle arguments = getArguments(); + final int requestCode = arguments.getInt(ARGUMENT_PERMISSION_REQUEST_CODE); + mFinishActivity = arguments.getBoolean(ARGUMENT_FINISH_ACTIVITY); + + return new AlertDialog.Builder(getActivity()) + .setMessage(R.string.permission_rationale_location) + .setPositiveButton(android.R.string.ok, (dialog, which) -> { + // After click on Ok, request the permission. + ActivityCompat.requestPermissions(getActivity(), + new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, + requestCode); + // Do not finish the Activity while requesting permission. + mFinishActivity = false; + }) + .setNegativeButton(android.R.string.cancel, null) + .create(); + } + + @Override + public void onDismiss(DialogInterface dialog) { + super.onDismiss(dialog); + if (mFinishActivity) { + Toast.makeText(getActivity(), + R.string.permission_required_toast, + Toast.LENGTH_SHORT) + .show(); + getActivity().finish(); + } + } + } +} diff --git a/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/app/AircraftMapView.java b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/app/AircraftMapView.java new file mode 100644 index 0000000000000000000000000000000000000000..9b92798fd5977829b91d8036feac38b7cea7f7e3 --- /dev/null +++ b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/app/AircraftMapView.java @@ -0,0 +1,279 @@ +/* + * Copyright (C) 2019 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + * + */ +package org.dripdronescanner.android.app; + +import android.Manifest; +import android.content.pm.PackageManager; +import android.graphics.Color; +import android.os.Bundle; +import android.util.Log; +import android.util.Pair; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.Nullable; +import androidx.core.app.ActivityCompat; +import androidx.lifecycle.Observer; +import androidx.lifecycle.ViewModelProviders; + +import com.google.android.gms.maps.CameraUpdateFactory; +import com.google.android.gms.maps.GoogleMap; +import com.google.android.gms.maps.OnMapReadyCallback; +import com.google.android.gms.maps.SupportMapFragment; +import com.google.android.gms.maps.model.BitmapDescriptorFactory; +import com.google.android.gms.maps.model.CameraPosition; +import com.google.android.gms.maps.model.LatLng; +import com.google.android.gms.maps.model.Marker; +import com.google.android.gms.maps.model.MarkerOptions; +import com.google.android.gms.maps.model.Polyline; +import com.google.android.gms.maps.model.PolylineOptions; + +import org.dripdronescanner.android.data.AircraftObject; +import org.dripdronescanner.android.data.LocationData; +import org.dripdronescanner.android.data.SystemData; +import org.dripdronescanner.android.data.Util; + +import java.util.Collection; +import java.util.HashMap; + +public class AircraftMapView extends SupportMapFragment implements OnMapReadyCallback, GoogleMap.OnMarkerClickListener { + private static final String TAG = "AircraftMapView"; + private GoogleMap googleMap; + private AircraftViewModel model; + + private final HashMap<AircraftObject, MapObserver> aircraftObservers = new HashMap<>(); + + private final Util.DiffObserver<AircraftObject> allAircraftObserver = new Util.DiffObserver<AircraftObject>() { + @Override + public void onAdded(Collection<AircraftObject> added) { + for (AircraftObject aircraftObject : added) { + trackAircraft(aircraftObject); + } + } + + @Override + public void onRemoved(Collection<AircraftObject> removed) { + for (AircraftObject aircraftObject : removed) { + stopTrackingAircraft(aircraftObject); + } + } + }; + + private void trackAircraft(AircraftObject aircraftObject) { + MapObserver observer = new MapObserver(aircraftObject); + aircraftObservers.put(aircraftObject, observer); + } + + private void stopTrackingAircraft(AircraftObject aircraftObject) { + MapObserver observer = aircraftObservers.remove(aircraftObject); + if (observer == null) return; + observer.stop(); + } + + private static final int DESIRED_ZOOM = 17; + private static final int ALLOWED_ZOOM_MARGIN = 2; + + private void setupModel() { + if (getActivity() == null) + return; + + model = ViewModelProviders.of(getActivity()).get(AircraftViewModel.class); + + model.getAllAircraft().observe(getViewLifecycleOwner(), allAircraftObserver); + model.getActiveAircraft().observe(getViewLifecycleOwner(), new Observer<AircraftObject>() { + MapObserver last = null; + + @Override + public void onChanged(@Nullable AircraftObject object) { + if (object == null || object.getLocation() == null || googleMap == null) + return; + MapObserver observer = aircraftObservers.get(object); + if (observer == null) + return; + + if (object.getLocation().getLatitude() == 0.0 && object.getLocation().getLongitude() == 0.0) + return; + + LatLng ll = new LatLng(object.getLocation().getLatitude(), object.getLocation().getLongitude()); + Log.i(TAG, "centering on " + object + " at " + ll); + + if (last != null && last.marker != null) { + last.marker.setAlpha(0.5f); + if (last.markerPilot != null) + last.markerPilot.setAlpha(0.5f); + } + if (observer.marker != null) + observer.marker.setAlpha(1.0f); + if (observer.markerPilot != null) + observer.markerPilot.setAlpha(1.0f); + + last = observer; + + CameraPosition position = googleMap.getCameraPosition(); + if (position.zoom < DESIRED_ZOOM - ALLOWED_ZOOM_MARGIN || position.zoom > DESIRED_ZOOM + ALLOWED_ZOOM_MARGIN) + googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(ll, DESIRED_ZOOM)); + else + googleMap.moveCamera(CameraUpdateFactory.newLatLng(ll)); + } + }); + } + + @Override + public boolean onMarkerClick(Marker marker) { + + if (marker != null) { + Object tag = marker.getTag(); + if (tag instanceof AircraftObject) { + model.setActiveAircraft((AircraftObject) tag); + return true; + } + } + return false; + } + + class MapObserver implements Observer<LocationData> { + private Marker marker; + private Marker markerPilot; + private Polyline polyline; + private PolylineOptions polylineOptions; + + private final AircraftObject aircraft; + + MapObserver(AircraftObject active) { + aircraft = active; + aircraft.location.observe(AircraftMapView.this, this); + aircraft.system.observe(AircraftMapView.this, systemObserver); + polylineOptions = new PolylineOptions() + .color(Color.RED) + .clickable(true); + } + + void stop() { + aircraft.location.removeObserver(this); + aircraft.system.removeObserver(systemObserver); + if (marker != null) { + marker.remove(); + marker = null; + } + if (markerPilot != null) { + markerPilot.remove(); + markerPilot = null; + } + if (polyline != null) { + polyline.remove(); + polyline = null; + } + polylineOptions = null; + } + + private final Observer<SystemData> systemObserver = new Observer<SystemData>() { + @Override + public void onChanged(@Nullable SystemData ignore) { + SystemData sys = aircraft.getSystem(); + if (sys == null || googleMap == null) + return; + + // filter out zero data + if (sys.getOperatorLatitude() == 0.0 && sys.getOperatorLongitude() == 0.0) + return; + + LatLng latLng = new LatLng(sys.getOperatorLatitude(), sys.getOperatorLongitude()); + if (markerPilot == null) { + String id = "ID missing"; + if (aircraft.getIdentification() != null) + id = aircraft.getIdentification().getUasIdAsString(); + markerPilot = googleMap.addMarker( + new MarkerOptions() + .alpha(0.5f) + .icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_AZURE)) + .position(latLng) + .title("pilot " + id)); + markerPilot.setTag(new Pair<>(aircraft, this)); + } + markerPilot.setPosition(latLng); + } + }; + + @Override + public void onChanged(@Nullable LocationData ignore) { + boolean zoom = false; + LocationData loc = aircraft.getLocation(); + if (loc == null || googleMap == null || polylineOptions == null) + return; + + // filter out zero data + if (loc.getLatitude() == 0.0 && loc.getLongitude() == 0.0) + return; + + LatLng latLng = new LatLng(loc.getLatitude(), loc.getLongitude()); + if (marker == null) { + String id = "ID missing"; + if (aircraft.getIdentification() != null) + id = aircraft.getIdentification().getUasIdAsString(); + marker = googleMap.addMarker( + new MarkerOptions() + .alpha(0.5f) + .position(latLng) + .title("aircraft " + id)); + marker.setTag(aircraft); + zoom = true; + } + + polylineOptions.add(latLng); + if (polyline != null) { + polyline.remove(); + polyline = null; + } + polyline = googleMap.addPolyline(polylineOptions); + + marker.setPosition(latLng); + if (zoom) { + googleMap.moveCamera(CameraUpdateFactory.newLatLng(latLng)); + } + } + } + + @Override + public View onCreateView(LayoutInflater layoutInflater, ViewGroup viewGroup, Bundle bundle) { + View view = super.onCreateView(layoutInflater, viewGroup, bundle); + getMapAsync(this); + return view; + } + + @Override + public void onActivityCreated(Bundle bundle) { + super.onActivityCreated(bundle); + setupModel(); + } + + @Override + public void onMapReady(GoogleMap googleMap) { + if (getActivity() == null) + return; + + this.googleMap = googleMap; + + if (ActivityCompat.checkSelfPermission(getActivity(), Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && + ActivityCompat.checkSelfPermission(getActivity(), Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { + // TODO: Consider calling ActivityCompat#requestPermissions + // to request the missing permissions, and then overriding + // public void onRequestPermissionsResult(int requestCode, String[] permissions, + // int[] grantResults) + // to handle the case where the user grants the permission. See the documentation + // for ActivityCompat#requestPermissions for more details. + Log.e("XX", "##################### can't make the right permissions"); + return; + } + + googleMap.getUiSettings().setMyLocationButtonEnabled(true); + googleMap.getUiSettings().setMapToolbarEnabled(false); + googleMap.setMyLocationEnabled(true); + googleMap.setMapType(GoogleMap.MAP_TYPE_HYBRID); + googleMap.setOnMarkerClickListener(this); + } +} diff --git a/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/app/AircraftViewModel.java b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/app/AircraftViewModel.java new file mode 100644 index 0000000000000000000000000000000000000000..7869bde52ddb77455bb5e99815b10224733dbfd5 --- /dev/null +++ b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/app/AircraftViewModel.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2019 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + * + */ +package org.dripdronescanner.android.app; + +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; +import androidx.lifecycle.ViewModel; + +import org.dripdronescanner.android.data.AircraftObject; + +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +public class AircraftViewModel extends ViewModel { + private final MutableLiveData<Set<AircraftObject>> aircraft = new MutableLiveData<>(); + private final MutableLiveData<AircraftObject> selected = new MutableLiveData<>(); + + public AircraftViewModel() { + Set<AircraftObject> list = new HashSet<>(); + aircraft.postValue(list); + } + + void setActiveAircraft(AircraftObject object) { + selected.postValue(object); + } + + LiveData<AircraftObject> getActiveAircraft() { + return selected; + } + + void setAllAircraft(Map<Long, AircraftObject> objects) { + aircraft.postValue(new HashSet<>(objects.values())); + } + + LiveData<Set<AircraftObject>> getAllAircraft() { + return aircraft; + } + +} diff --git a/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/app/DebugActivity.java b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/app/DebugActivity.java new file mode 100644 index 0000000000000000000000000000000000000000..c12d40e3ca86feac186605805ade60f7e1c29f60 --- /dev/null +++ b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/app/DebugActivity.java @@ -0,0 +1,392 @@ +/* + * Copyright (C) 2019 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + * + */ +package org.dripdronescanner.android.app; + +import android.Manifest; +import android.annotation.TargetApi; +import android.bluetooth.BluetoothAdapter; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.pm.PackageManager; +import android.location.Location; +import android.net.wifi.WifiManager; +import android.os.Build; +import android.os.Bundle; +import android.os.Environment; +import android.os.Handler; +import android.provider.Settings; +import android.util.Log; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.app.ActivityCompat; +import androidx.fragment.app.FragmentTransaction; +import androidx.lifecycle.Observer; +import androidx.lifecycle.ViewModelProviders; + +import com.google.android.gms.location.LocationCallback; +import com.google.android.gms.location.LocationRequest; +import com.google.android.gms.location.LocationResult; +import com.google.android.gms.location.LocationServices; + +import org.dripdronescanner.android.Constants; +import org.dripdronescanner.android.PermissionUtils; +import org.dripdronescanner.android.R; +import org.dripdronescanner.android.data.AircraftObject; +import org.dripdronescanner.android.log.LogWriter; +import org.dripdronescanner.android.network.BluetoothScanner; +import org.dripdronescanner.android.network.OpenDroneIdDataManager; +import org.dripdronescanner.android.network.WiFiBeaconScanner; +import org.dripdronescanner.android.network.WiFiNaNScanner; + +import java.io.File; +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; +import java.util.Set; + +public class DebugActivity extends AppCompatActivity { + BluetoothScanner btScanner; + WiFiNaNScanner wiFiNaNScanner; + WiFiBeaconScanner wiFiBeaconScanner; + boolean wifiOn = false; + boolean bluetoothOn = false; + + private AircraftViewModel mModel; + OpenDroneIdDataManager dataManager; + + private static final String TAG = DebugActivity.class.getSimpleName(); + + public static final String SHARED_PREF_NAME = "DebugActivity"; + public static final String SHARED_PREF_ENABLE_LOG = "EnableLog"; + private MenuItem mMenuLogItem; + + private File loggerFile; + private LogWriter logger; + + private Handler handler; + private Runnable runnableCode; + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.main_menu, menu); + mMenuLogItem = menu.findItem(R.id.menu_log); + mMenuLogItem.setChecked(getLogEnabled()); + checkBluetoothSupport(menu); + checkNaNSupport(menu); + checkWiFiSupport(menu); + createSettingsItem(menu); + return true; + } + + private void createSettingsItem(Menu menu){ + DebugActivity current = this; + MenuItem settings = menu.findItem(R.id.settings); + settings.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(MenuItem menuItem) { + Intent intent = new Intent(current, SettingsActivity.class); + startActivity(intent); + return true; + } + }); + } + + @TargetApi(Build.VERSION_CODES.O) + private void checkBluetoothSupport(Menu menu) { + Object object = getSystemService(BLUETOOTH_SERVICE); + if (object == null) + return; + BluetoothAdapter bluetoothAdapter = ((android.bluetooth.BluetoothManager) object).getAdapter(); + + if(bluetoothAdapter != null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && bluetoothAdapter.isLeCodedPhySupported()) { + menu.findItem(R.id.coded_phy).setTitle(R.string.coded_phy_supported); + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && bluetoothAdapter.isLeExtendedAdvertisingSupported()) { + menu.findItem(R.id.extended_advertising).setTitle(R.string.ea_supported); + } + } + } + + @TargetApi(Build.VERSION_CODES.O) + private void checkNaNSupport(Menu menu) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_AWARE)) { + menu.findItem(R.id.wifi_nan).setTitle(R.string.nan_supported); + } + } + @TargetApi(Build.VERSION_CODES.M) + private void checkWiFiSupport(Menu menu) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + menu.findItem(R.id.wifi_beacon_scan).setTitle(R.string.wifi_beacon_scan_supported); + } + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.clear: + dataManager.getAircraft().clear(); + mModel.setAllAircraft(dataManager.getAircraft()); + LogWriter.bumpSession(); + return true; + case R.id.menu_log: + boolean enabled = !getLogEnabled(); + setLogEnabled(enabled); + mMenuLogItem.setChecked(enabled); + if (enabled) { + createNewLogfile(); + wiFiNaNScanner.setLogger(logger); + wiFiBeaconScanner.setLogger(logger); + } else { + logger.close(); + btScanner.setLogger(null); + wiFiNaNScanner.setLogger(null); + wiFiBeaconScanner.setLogger(null); + } + return true; + case R.id.log_location: + if (getLogEnabled()) + Toast.makeText(getBaseContext(), "Logging to " + loggerFile, Toast.LENGTH_LONG).show(); + else + Toast.makeText(getBaseContext(), "Logging not activated", Toast.LENGTH_LONG).show(); + return true; + } + return false; + } + + boolean getLogEnabled() { + SharedPreferences pref = getSharedPreferences(SHARED_PREF_NAME, 0); + return pref.getBoolean(SHARED_PREF_ENABLE_LOG, true); + } + + void setLogEnabled(boolean enabled) { + SharedPreferences pref = getSharedPreferences(SHARED_PREF_NAME, 0); + pref.edit().putBoolean(SHARED_PREF_ENABLE_LOG, enabled).apply(); + } + + public File getLoggerFileDir(String name) { + File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS), "OpenDroneID"); + if (!file.mkdirs()) { + file = getExternalFilesDir(null); + } + String pattern = "yyyy-MM-dd_HH-mm-ss.SSS"; + SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern, Locale.US); + return new File(file, "log_" + Build.MODEL + "_" + name + "_" + simpleDateFormat.format(new Date()) + ".csv"); + } + + private void createNewLogfile() { + loggerFile = getLoggerFileDir(btScanner.getBluetoothAdapter().getName()); + + try { + logger = new LogWriter(loggerFile); + } catch (IOException e) { + e.printStackTrace(); + } + btScanner.setLogger(logger); + } + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.activity_debug); + mModel = ViewModelProviders.of(this).get(AircraftViewModel.class); + + dataManager = new OpenDroneIdDataManager(new OpenDroneIdDataManager.Callback() { + @Override + public void onNewAircraft(AircraftObject object) { + mModel.setAllAircraft(dataManager.getAircraft()); + } + }); + + btScanner = new BluetoothScanner(this, dataManager); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && + getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_AWARE)) { + WifiManager wifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE); + if (!wifiManager.isWifiEnabled()) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + Intent panelIntent = new Intent(Settings.Panel.ACTION_WIFI); + startActivityForResult(panelIntent, Constants.REQUEST_ENABLE_WIFI); + } else { + wifiManager.setWifiEnabled(true); + wifiOn = true; + } + } + } + + BluetoothAdapter bluetoothAdapter = btScanner.getBluetoothAdapter(); + if (bluetoothAdapter != null) { + createNewLogfile(); + // Is Bluetooth turned on? + if (!bluetoothAdapter.isEnabled()) { + // Prompt user to turn on Bluetooth (logic continues in onActivityResult()). + Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); + startActivityForResult(enableBtIntent, Constants.REQUEST_ENABLE_BT); + } else { + bluetoothOn = true; + // Check permission + if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED + && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { + Log.d(TAG, "onMapReady: call request permission"); + requestLocationPermission(Constants.FINE_LOCATION_PERMISSION_REQUEST_CODE); + } else { + + initialize(); + } + } + } else { + // Bluetooth is not supported. + showErrorText(R.string.bt_not_supported); + } + + dataManager.mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this); + + dataManager.locationRequest = LocationRequest.create(); + dataManager.locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY); + dataManager.locationRequest.setInterval(10 * 1000); // 10 seconds + dataManager.locationRequest.setFastestInterval(5 * 1000); // 5 seconds + + dataManager.locationCallback = new LocationCallback() { + @Override + public void onLocationResult(LocationResult locationResult) { + if (locationResult == null) { + return; + } + for (Location location : locationResult.getLocations()) { + if (location != null) { + dataManager.receiverLocation = location; + } + } + } + }; + dataManager.activity = this; + dataManager.getReceiverLocation(); + } + + private void initialize() { + mModel.setAllAircraft(dataManager.getAircraft()); + + final Observer<Set<AircraftObject>> listObserver = airCrafts -> { + if (airCrafts == null) + return; + setTitle(String.format(Locale.US, "%d drones", airCrafts.size())); + }; + + mModel.getAllAircraft().observe(this, listObserver); + + btScanner.startScan(); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + wiFiNaNScanner = new WiFiNaNScanner(this, dataManager, logger); + wiFiNaNScanner.startScan(); + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + wiFiBeaconScanner = new WiFiBeaconScanner(this, dataManager, logger); + if (wiFiBeaconScanner != null) { + wiFiBeaconScanner.startScan(); + } + } + + addDeviceList(); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + if (requestCode == Constants.REQUEST_ENABLE_BT) { + if (resultCode == RESULT_OK) { + if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED + && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { + Log.d(TAG, "onMapReady: call request permission"); + requestLocationPermission(Constants.FINE_LOCATION_PERMISSION_REQUEST_CODE); + } else { + initialize(); + } + } else { + if(!wifiOn) { + // User declined to enable Bluetooth, exit the app. + Toast.makeText(this, R.string.bt_not_enabled_leaving, Toast.LENGTH_LONG).show(); + finish(); + } + } + } else if (requestCode == Constants.REQUEST_ENABLE_WIFI) { + WifiManager wifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE); + if (!wifiManager.isWifiEnabled() && !bluetoothOn) { + Toast.makeText(this, R.string.wifi_not_enabled_leaving, Toast.LENGTH_LONG).show(); + finish(); + } + } else { + super.onActivityResult(requestCode, resultCode, data); + } + } + + public void addDeviceList() { + FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); + transaction.replace(R.id.holder, new DeviceList()).commitAllowingStateLoss(); + } + + @Override + protected void onResume() { + Log.d(TAG, "onResume"); + + // Wake up the main Activity thread once per second, in order to update time counters + handler = new Handler(); + runnableCode = () -> { + for (AircraftObject aircraft : dataManager.aircraft.values()) { + aircraft.connection.setValue(aircraft.connection.getValue()); + } + handler.postDelayed(runnableCode, 1000); + }; + handler.post(runnableCode); + super.onResume(); + } + + @Override + protected void onPause() { + Log.d(TAG, "onPause"); + //btScanner.stopScan(); + handler.removeCallbacks(runnableCode); + super.onPause(); + } + + private void showErrorText(int messageId) { + Toast.makeText(this, messageId, Toast.LENGTH_SHORT).show(); + } + + public void requestLocationPermission(int requestCode) { + Log.d(TAG, "requestLocationPermission: request permission"); + + // Location permission has not been granted yet, request it. + PermissionUtils.requestPermission(this, requestCode, + Manifest.permission.ACCESS_FINE_LOCATION, false); + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, + @NonNull int[] grantResults) { + if (requestCode == Constants.FINE_LOCATION_PERMISSION_REQUEST_CODE) { + Log.d(TAG, "onRequestPermissionsResult: back from request FINE_LOCATION"); + if (PermissionUtils.isPermissionGranted(permissions, grantResults, + Manifest.permission.ACCESS_FINE_LOCATION)) { + initialize(); + } else { + showErrorText(R.string.permission_required_toast); + } + + } + } +} diff --git a/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/app/DetailViewModel.java b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/app/DetailViewModel.java new file mode 100644 index 0000000000000000000000000000000000000000..35d0f73cd69bbf1e082159645ba09a44f4b45614 --- /dev/null +++ b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/app/DetailViewModel.java @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2019 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + * + */ +package org.dripdronescanner.android.app; + +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MediatorLiveData; +import androidx.lifecycle.MutableLiveData; +import androidx.lifecycle.Observer; +import androidx.lifecycle.Transformations; +import androidx.lifecycle.ViewModel; + +import org.dripdronescanner.android.data.AircraftObject; +import org.dripdronescanner.android.data.AuthenticationData; +import org.dripdronescanner.android.data.Connection; +import org.dripdronescanner.android.data.Identification; +import org.dripdronescanner.android.data.LocationData; +import org.dripdronescanner.android.data.OperatorIdData; +import org.dripdronescanner.android.data.SelfIdData; +import org.dripdronescanner.android.data.SystemData; + +import java.util.concurrent.atomic.AtomicInteger; + +public class DetailViewModel extends ViewModel { + private final MutableLiveData<AircraftObject> selected = new MutableLiveData<>(); + + private final TimerLiveData timer = new TimerLiveData(500); + + void select(AircraftObject item) { + selected.setValue(item); + } + + public LiveData<AircraftObject> getSelected() { + return selected; + } + + private static class FrequencyEvent { + } + + public void foo() { + final AtomicInteger count = new AtomicInteger(0); + + final MediatorLiveData<FrequencyEvent> frequencyMediator = new MediatorLiveData<>(); + frequencyMediator.addSource(timer, new Observer<Long>() { + long lastTime = 0; + + @Override + public void onChanged(Long time) { + long td = lastTime - time; + if (td > 1000L) { + int c = count.getAndSet(0); + frequencyMediator.getValue(); + + long avg = c / td; + } + } + }); + frequencyMediator.addSource(location, locationData -> count.incrementAndGet()); + } + + final LiveData<Identification> identification = Transformations.switchMap(selected, + input -> input.identification); + + final LiveData<Connection> connection = Transformations.switchMap(selected, + input -> input.connection); + + public final LiveData<LocationData> location = Transformations.switchMap(selected, + input -> input.location); + + final LiveData<AuthenticationData> authentication = Transformations.switchMap(selected, + input -> input.authentication); + + final LiveData<SelfIdData> selfid = Transformations.switchMap(selected, + input -> input.selfid); + + public final LiveData<SystemData> system = Transformations.switchMap(selected, + input -> input.system); + + final LiveData<OperatorIdData> operatorid = Transformations.switchMap(selected, + input -> input.operatorid); +} diff --git a/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/app/DeviceDetailFragment.java b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/app/DeviceDetailFragment.java new file mode 100644 index 0000000000000000000000000000000000000000..637404c20c85b89ce0a67ff91a7d339f0b34acdf --- /dev/null +++ b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/app/DeviceDetailFragment.java @@ -0,0 +1,320 @@ +/* + * Copyright (C) 2019 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + * + */ +package org.dripdronescanner.android.app; + +import static android.content.Context.CLIPBOARD_SERVICE; + +import android.app.AlertDialog; +import android.content.ClipData; +import android.content.ClipboardManager; +import android.os.Bundle; +import android.text.InputType; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.EditText; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.DialogFragment; +import androidx.lifecycle.ViewModelProvider; + +import org.bouncycastle.util.encoders.DecoderException; +import org.bouncycastle.util.encoders.Hex; +import org.dripdronescanner.android.R; + +import java.util.Locale; + +public class DeviceDetailFragment extends DialogFragment { + private TextView receiveTime; + private TextView conMac; + private TextView conRssi; + private TextView conStarted; + private TextView conLastUpdate; + private TextView conMsgDelta; + private TextView distance; + + private TextView infoLastUpdate; + private TextView infoType; + private TextView infoIdType; + private TextView infoUasId; + private TextView infoIDValidated; + private Button copyIDBtn; + + private TextView posLastUpdate; + private TextView status; + private TextView heightType; + private TextView direction; + private TextView horiSpeed; + private TextView vertSpeed; + private TextView lat; + private TextView lon; + private TextView altitudePressure; + private TextView altitudeGeodetic; + private TextView height; + private TextView horizontalAccuracy; + private TextView verticalAccuracy; + private TextView baroAccuracy; + private TextView speedAccuracy; + private TextView timestamp; + private TextView timeAccuracy; + + private TextView authLastUpdate; + private TextView authType; + private TextView authTimestamp; + private TextView authLength; + private TextView authData; + private Button manualAuthBtn; + + private TextView selfIdLastUpdate; + private TextView selfIdType; + private TextView selfIdDescription; + + private TextView systemLastUpdate; + private TextView operatorLocationType; + private TextView classificationType; + private TextView systemLatitude; + private TextView systemLongitude; + private TextView systemAreaCount; + private TextView systemAreaRadius; + private TextView systemAreaCeiling; + private TextView systemAreaFloor; + private TextView category; + private TextView classValue; + + private TextView operatorIdLastUpdate; + private TextView operatorIdType; + private TextView operatorId; + + static DeviceDetailFragment newInstance() { + return new DeviceDetailFragment(); + } + + @Override + public void onActivityCreated(@Nullable Bundle savedInstanceState) { + if (getActivity() == null) + return; + + super.onActivityCreated(savedInstanceState); + DetailViewModel model = new ViewModelProvider(getActivity()).get(DetailViewModel.class); + + model.connection.observe(getViewLifecycleOwner(), connection -> { + if (connection == null) return; + String combo = connection.rssi + " dBm, " + connection.transportType; + conRssi.setText(combo); + conMac.setText(connection.macAddress); + receiveTime.setText(connection.getTimestampAsString()); + conStarted.setText(String.format(Locale.US,"%s ago", DeviceList.elapsed(connection.firstSeen))); + conLastUpdate.setText(String.format(Locale.US,"%s ago", DeviceList.elapsed(connection.lastSeen))); + conMsgDelta.setText(connection.getMsgDeltaAsString()); + }); + + model.identification.observe(getViewLifecycleOwner(), identification -> { + if (identification == null) return; + + receiveTime.setText(identification.getTimestampAsString()); + infoLastUpdate.setText(identification.getADCounterAsString()); + infoType.setText(identification.getUaType().name()); + infoIdType.setText(identification.getIdType().name()); + infoUasId.setText(identification.getUasIdAsString()); + infoIDValidated.setText(identification.getIDValid()); + + }); + + model.location.observe(getViewLifecycleOwner(), locationData -> { + if (locationData == null) return; + + receiveTime.setText(locationData.getTimestampAsString()); + posLastUpdate.setText(locationData.getADCounterAsString()); + status.setText(locationData.getStatus().name()); + direction.setText(locationData.getDirectionAsString()); + horiSpeed.setText(locationData.getSpeedHorizontalAsString()); + vertSpeed.setText(locationData.getSpeedVerticalAsString()); + lat.setText(locationData.getLatitudeAsString()); + lon.setText(locationData.getLongitudeAsString()); + altitudePressure.setText(locationData.getAltitudePressureAsString()); + altitudeGeodetic.setText(locationData.getAltitudeGeodeticAsString()); + heightType.setText(locationData.getHeightType().name()); + height.setText(locationData.getHeightAsString()); + horizontalAccuracy.setText(locationData.getHorizontalAccuracyAsString()); + verticalAccuracy.setText(locationData.getVerticalAccuracyAsString(locationData.getVerticalAccuracy())); + baroAccuracy.setText(locationData.getVerticalAccuracyAsString(locationData.getBaroAccuracy())); + speedAccuracy.setText(locationData.getSpeedAccuracyAsString()); + timestamp.setText(locationData.getLocationTimestampAsString()); + timeAccuracy.setText(locationData.getTimeAccuracyAsString()); + distance.setText(locationData.getDistanceAsString()); + }); + + model.authentication.observe(getViewLifecycleOwner(), authenticationData -> { + if (authenticationData == null) return; + + receiveTime.setText(authenticationData.getTimestampAsString()); + authLastUpdate.setText(authenticationData.getADCounterAsString()); + authType.setText(authenticationData.getAuthType().name()); + authLength.setText(authenticationData.getAuthLengthAsString()); + authTimestamp.setText(authenticationData.getAuthTimestampAsString()); + authData.setText(authenticationData.getAuthenticationDataAsString()); + }); + + model.selfid.observe(getViewLifecycleOwner(), selfIdData -> { + if (selfIdData == null) return; + + receiveTime.setText(selfIdData.getTimestampAsString()); + selfIdLastUpdate.setText(selfIdData.getADCounterAsString()); + selfIdType.setText(String.valueOf(selfIdData.getDescriptionType())); + selfIdDescription.setText(new String(selfIdData.getOperationDescription())); + }); + + model.system.observe(getViewLifecycleOwner(), systemData -> { + if (systemData == null) return; + + receiveTime.setText(systemData.getTimestampAsString()); + systemLastUpdate.setText(systemData.getADCounterAsString()); + operatorLocationType.setText(systemData.getOperatorLocationType().name()); + classificationType.setText(systemData.getclassificationType().name()); + systemLatitude.setText(systemData.getOperatorLatitudeAsString()); + systemLongitude.setText(systemData.getOperatorLongitudeAsString()); + systemAreaCount.setText(String.valueOf(systemData.getAreaCount())); + systemAreaRadius.setText(systemData.getAreaRadiusAsString()); + systemAreaCeiling.setText(systemData.getAreaCeilingAsString()); + systemAreaFloor.setText(systemData.getAreaFloorAsString()); + category.setText(systemData.getCategory().name()); + classValue.setText(systemData.getClassValue().name()); + }); + + model.operatorid.observe(getViewLifecycleOwner(), operatorIdData -> { + if (operatorIdData == null) return; + + receiveTime.setText(operatorIdData.getTimestampAsString()); + operatorIdLastUpdate.setText(operatorIdData.getADCounterAsString()); + operatorIdType.setText(String.valueOf(operatorIdData.getOperatorIdType())); + operatorId.setText(new String(operatorIdData.getOperatorId())); + }); + } + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + + View view = inflater.inflate(R.layout.aircraft_details, container, false); + receiveTime = view.findViewById(R.id.receiveTime); + conMac = view.findViewById(R.id.conMac); + conRssi = view.findViewById(R.id.conRssi); + conStarted = view.findViewById(R.id.conStarted); + conLastUpdate = view.findViewById(R.id.conLastUpdate); + conMsgDelta = view.findViewById(R.id.conMsgDelta); + distance = view.findViewById(R.id.distance); + + infoLastUpdate = view.findViewById(R.id.infoLastUpdate); + infoType = view.findViewById(R.id.infoType); + infoIdType = view.findViewById(R.id.infoIdType); + infoUasId = view.findViewById(R.id.infoUasId); + infoIDValidated = view.findViewById(R.id.infoIDValidated); + copyIDBtn = view.findViewById(R.id.copyIDBtn); + copyIDBtn.setOnClickListener(view1 -> { + ClipboardManager clipboard = (ClipboardManager) view1.getContext().getSystemService(CLIPBOARD_SERVICE); + ClipData clip = ClipData.newPlainText("ID", infoUasId.getText()); + clipboard.setPrimaryClip(clip); + }); + + posLastUpdate = view.findViewById(R.id.posLastUpdate); + status = view.findViewById(R.id.status); + direction = view.findViewById(R.id.direction); + horiSpeed = view.findViewById(R.id.horiSpeed); + vertSpeed = view.findViewById(R.id.vertSpeed); + lat = view.findViewById(R.id.lat); + lon = view.findViewById(R.id.lon); + altitudePressure = view.findViewById(R.id.altitudePressure); + altitudeGeodetic = view.findViewById(R.id.altitudeGeodetic); + heightType = view.findViewById(R.id.heightType); + height = view.findViewById(R.id.height); + horizontalAccuracy = view.findViewById(R.id.horizontalAccuracy); + verticalAccuracy = view.findViewById(R.id.verticalAccuracy); + baroAccuracy = view.findViewById(R.id.baroAccuracy); + speedAccuracy = view.findViewById(R.id.speedAccuracy); + timestamp = view.findViewById(R.id.timestamp); + timeAccuracy = view.findViewById(R.id.timeAccuracy); + + authLastUpdate = view.findViewById(R.id.authLastUpdate); + authType = view.findViewById(R.id.authType); + authLength = view.findViewById(R.id.authLength); + authTimestamp = view.findViewById(R.id.authTimestamp); + authData = view.findViewById(R.id.authData); + manualAuthBtn = view.findViewById(R.id.manAuthBtn); + manualAuthBtn.setOnClickListener(view2 -> { + AlertDialog.Builder builder = new AlertDialog.Builder(view2.getContext()); + builder.setTitle("Enter public key"); + + // Set up the input + final EditText textInput = new EditText(view2.getContext()); + // Specify the type of input expected; this, for example, sets the input as a password, and will mask the text + textInput.setInputType(InputType.TYPE_CLASS_TEXT); + builder.setView(textInput); + + // Set up the buttons + builder.setPositiveButton("OK", (dialog, which) -> { + String pubKey = textInput.getText().toString(); + byte[] decoded; + try { + decoded = Hex.decode(pubKey); + } catch (DecoderException e) { + e.printStackTrace(); + Toast.makeText(view.getContext(), + "Bad hex string, could not decode.", + Toast.LENGTH_LONG).show(); + return; + } + + DetailViewModel model = new ViewModelProvider(getActivity()).get(DetailViewModel.class); + model.getSelected().getValue().setPubKey(decoded); + boolean isValid = model.authentication.getValue().validate( + model.identification.getValue().getUasId(), + model.getSelected().getValue().getPubkey() + ); + if(!isValid){ + model.authentication.getValue().setServerIssues(decoded.length == 0); + Toast.makeText(view.getContext(), + "Authentication failed, Either you entered " + + "the public key incorrectly or the identity is Invalid", + Toast.LENGTH_LONG).show(); + }else{ + model.authentication.getValue().setServerIssues(false); + } + System.out.println("validating id"); + model.identification.getValue().validateId(decoded); + + }); + builder.setNegativeButton("Cancel", (dialog, which) -> dialog.cancel()); + + builder.show(); + }); + + selfIdLastUpdate = view.findViewById(R.id.selfIdLastUpdate); + selfIdType = view.findViewById(R.id.selfIdType); + selfIdDescription = view.findViewById(R.id.selfIdDescription); + + systemLastUpdate = view.findViewById(R.id.systemLastUpdate); + operatorLocationType = view.findViewById(R.id.operatorLocationType); + classificationType = view.findViewById(R.id.classificationType); + systemLatitude = view.findViewById(R.id.systemLatitude); + systemLongitude = view.findViewById(R.id.systemLongitude); + systemAreaCount = view.findViewById(R.id.systemAreaCount); + systemAreaRadius = view.findViewById(R.id.systemAreaRadius); + systemAreaCeiling = view.findViewById(R.id.systemAreaCeiling); + systemAreaFloor = view.findViewById(R.id.systemAreaFloor); + category = view.findViewById(R.id.category); + classValue = view.findViewById(R.id.classValue); + + operatorIdLastUpdate = view.findViewById(R.id.operatorIdLastUpdate); + operatorIdType = view.findViewById(R.id.operatorIdType); + operatorId = view.findViewById(R.id.operatorId); + return view; + } +} diff --git a/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/app/DeviceList.java b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/app/DeviceList.java new file mode 100644 index 0000000000000000000000000000000000000000..ab71f28de0c6db1c45dba00feeece795e9b4baac --- /dev/null +++ b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/app/DeviceList.java @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2019 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + * + */ +package org.dripdronescanner.android.app; + +import android.graphics.Color; +import android.graphics.PorterDuff; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.StateListDrawable; +import android.os.Bundle; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.lifecycle.Observer; +import androidx.lifecycle.ViewModelProviders; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import com.mikepenz.fastadapter.FastAdapter; +import com.mikepenz.fastadapter.adapters.ModelAdapter; +import com.mikepenz.fastadapter.commons.utils.FastAdapterUIUtils; +import com.mikepenz.fastadapter.items.AbstractItem; +import com.mikepenz.fastadapter.select.SelectExtension; + +import org.dripdronescanner.android.R; +import org.dripdronescanner.android.data.AircraftObject; +import org.dripdronescanner.android.data.Connection; +import org.dripdronescanner.android.data.Identification; +import org.dripdronescanner.android.data.LocationData; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +public class DeviceList extends Fragment { + private static final String TAG = "CustomAdapter"; + + private AircraftViewModel mModel; + private ModelAdapter<AircraftObject, ListItem> mItemAdapter; + private FastAdapter<ListItem> mAdapter; + + public static DeviceList newInstance() { + return new DeviceList(); + } + + private void subscribeToModel(AircraftViewModel model) { + mModel = model; + final Observer<Set<AircraftObject>> listObserver = aircraftList -> { + if (aircraftList == null) + return; + Log.d(TAG, "DeviceList onChanged: " + aircraftList); + mItemAdapter.setNewList(new ArrayList<>(aircraftList)); + }; + + model.getActiveAircraft().observe(getViewLifecycleOwner(), object -> { + SelectExtension<ListItem> selectExtension = mAdapter.getSelectExtension(); + if (object == null) { + selectExtension.deselect(); + } else { + selectExtension.selectByIdentifier(object.getMacAddress(), false, false); + } + }); + mModel.getAllAircraft().observe(getViewLifecycleOwner(), listObserver); + } + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + @Override + public void onActivityCreated(@Nullable Bundle savedInstanceState) { + if (getActivity() == null) + return; + super.onActivityCreated(savedInstanceState); + AircraftViewModel model = ViewModelProviders.of(getActivity()).get(AircraftViewModel.class); + subscribeToModel(model); + } + + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + ViewGroup viewGroup = (ViewGroup) inflater.inflate(R.layout.aircraft_list, null); + // Set CustomAdapter as the adapter for RecyclerView. + // Create the ItemAdapter holding your Items + + mItemAdapter = new ModelAdapter<>(ListItem::new); + + // Create the managing FastAdapter, by passing in the itemAdapter + mAdapter = FastAdapter.with(mItemAdapter); + mAdapter.setHasStableIds(true); + mAdapter.withSelectable(true); + + mAdapter.withSelectionListener((item, selected) -> { + Log.d(TAG, "onSelectionChanged: "+item + " selected="+selected); + if (selected && item != null) { + if (mModel.getActiveAircraft().getValue() != item.object) { + // only set if different + mModel.setActiveAircraft(item.object); + } + } + }); + RecyclerView mRecyclerView = viewGroup.findViewById(R.id.device_list); + mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext())); + mRecyclerView.setAdapter(mAdapter); + mRecyclerView.scrollToPosition(0); + + return viewGroup; + } + + static String elapsed(long start) { + long millis = System.currentTimeMillis() - start; + return String.format(Locale.US, "%02d:%02d ", + TimeUnit.MILLISECONDS.toMinutes(millis), + TimeUnit.MILLISECONDS.toSeconds(millis) - + TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(millis)) + ); + } + + private void showDetails(AircraftObject aircraft) { + if (getActivity() == null || getParentFragmentManager() == null) + return; + DetailViewModel model = ViewModelProviders.of(getActivity()).get(DetailViewModel.class); + model.select(aircraft); + DeviceDetailFragment newFragment = DeviceDetailFragment.newInstance(); + newFragment.show(getParentFragmentManager(), "dialog"); + } + + /** + * Provide a reference to the type of views that you are using (custom ViewHolder) + */ + public class AircraftViewHolder extends FastAdapter.ViewHolder<ListItem> { + private final TextView textView; + private final TextView textView2; + private final TextView lastSeen; + private AircraftObject aircraft; + private View view; + private ImageView iconImageView; + private Drawable droneIcon; + + AircraftViewHolder(View v) { + super(v); + this.view = v; + textView = v.findViewById(R.id.aircraftName); + textView2 = v.findViewById(R.id.aircraftFun); + + Button button = v.findViewById(R.id.modButton); + button.setText(R.string.info); + lastSeen = v.findViewById(R.id.last_seen); + button.setOnClickListener(v1 -> showDetails(aircraft)); + + droneIcon = getResources().getDrawable(R.mipmap.ic_plane_icon); + iconImageView = v.findViewById(R.id.drone_icon); + } + + @Override + public void bindView(@NonNull ListItem aircraftItem, @NonNull List<Object> payloads) { + if (getContext() == null) + return; + + this.aircraft = aircraftItem.object; + + StateListDrawable selectableBackground = + FastAdapterUIUtils.getSelectableBackground(getContext(), Color.LTGRAY, true); + view.setBackground(selectableBackground); + Identification id = aircraft.getIdentification(); + if (id != null) + textView.setText(String.format("Aircraft %s", id.getUasIdAsString())); + + observer = identification -> { + Log.w(TAG, "on changed: " + identification); + if (identification == null) return; + textView.setText(String.format("%s", identification.getUasIdAsString())); + + droneIcon.setColorFilter( 0xff00ff00, PorterDuff.Mode.MULTIPLY ); + iconImageView.setImageDrawable(droneIcon); + }; + + aircraft.connection.observe(DeviceList.this, connectionObserver); + aircraft.location.observe(DeviceList.this, locationObserver); + aircraft.identification.observe(DeviceList.this, observer); + } + + @Override + public void unbindView(@NonNull ListItem aircraftItem) { + aircraft.identification.removeObserver(observer); + aircraft.connection.removeObserver(connectionObserver); + aircraft.location.removeObserver(locationObserver); + } + final Observer<Connection> connectionObserver = new Observer<Connection>() { + @Override + public void onChanged(@Nullable Connection connection) { + if (connection != null) + lastSeen.setText(String.format(Locale.US, "%s dBm", connection.rssi)); + } + }; + final Observer<LocationData> locationObserver = new Observer<LocationData>() { + @Override + public void onChanged(@Nullable LocationData locationData) { + if (locationData != null) + textView2.setText(String.format(Locale.US, "%s over %s, %s, %s away", + locationData.getHeightLessPreciseAsString(), + locationData.getHeightType().toString(), + locationData.getSpeedHorizontalLessPreciseAsString(), + locationData.getDistanceAsString())); + } + }; + + Observer<Identification> observer; + } + + public class ListItem extends AbstractItem<ListItem, AircraftViewHolder> { + + private final AircraftObject object; + + ListItem(AircraftObject object) { + this.object = object; + } + + @NonNull + @Override + public AircraftViewHolder getViewHolder(@NonNull View v) { + return new AircraftViewHolder(v); + } + + @Override + public long getIdentifier() { + return object.getMacAddress(); + } + + @Override + public int getType() { + return 0; + } + + @Override + public int getLayoutRes() { + return R.layout.listitem_aircraft; + } + + @Override @NonNull + public String toString() { + return "ListItem{" + + "object=" + object + + '}'; + } + } + +} diff --git a/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/app/SettingsActivity.java b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/app/SettingsActivity.java new file mode 100644 index 0000000000000000000000000000000000000000..5b0d0f713a87311e79fb83f538d21e7c1a59b786 --- /dev/null +++ b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/app/SettingsActivity.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2019 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + * + */ +package org.dripdronescanner.android.app; + +import android.content.Context; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.view.View; +import android.widget.Button; +import android.widget.TextView; + +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; + +import org.dripdronescanner.android.R; + +public class SettingsActivity extends AppCompatActivity { + private TextView tv; + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.activity_settings); + Button save = this.findViewById(R.id.saveBtn); + this.tv = this.findViewById(R.id.editBaseUrl); + save.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + + SharedPreferences sharedPreferences = getSharedPreferences("ONLY_PREF", Context.MODE_PRIVATE); + SharedPreferences.Editor editor = sharedPreferences.edit(); + + editor.putString("url",tv.getText().toString()); + editor.commit(); + } + }); + } +} diff --git a/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/app/TimerLiveData.java b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/app/TimerLiveData.java new file mode 100644 index 0000000000000000000000000000000000000000..fc8c0d9ffb93abb9cc5a90dfe24051bf0104d4a1 --- /dev/null +++ b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/app/TimerLiveData.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2019 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + * + */ +package org.dripdronescanner.android.app; + +import android.os.Handler; +import android.os.Looper; + +import androidx.lifecycle.LiveData; + +/** + * Live data that updates at a fixed rate, value is set to last timestamp + */ +public class TimerLiveData extends LiveData<Long> { + private static final int TIMER_EVENT = 1; + + private final int interval; + private final Handler.Callback callback = message -> { + tick(); + return true; + }; + + TimerLiveData(int intervalMillis) { + this.interval = intervalMillis; + } + + private final Handler timerHandler = new Handler(Looper.getMainLooper(), callback); + + private void tick() { + postValue(System.currentTimeMillis()); + timerHandler.sendEmptyMessageDelayed(TIMER_EVENT, interval); + } + + @Override + protected void onActive() { + super.onActive(); + tick(); + } + + @Override + protected void onInactive() { + timerHandler.removeMessages(TIMER_EVENT); + super.onInactive(); + } +} \ No newline at end of file diff --git a/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/data/AircraftObject.java b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/data/AircraftObject.java new file mode 100644 index 0000000000000000000000000000000000000000..4d7fdac42c02df5a4c4fbd2efdab162ebeaf5a93 --- /dev/null +++ b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/data/AircraftObject.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2019 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + * + */ +package org.dripdronescanner.android.data; + +import android.os.Build; + +import androidx.annotation.NonNull; +import androidx.annotation.RequiresApi; +import androidx.lifecycle.MutableLiveData; + +import org.dripdronescanner.android.Constants; + +public class AircraftObject { + final public MutableLiveData<Connection> connection = new MutableLiveData<>(); + final public MutableLiveData<Identification> identification = new MutableLiveData<>(); + final public MutableLiveData<LocationData> location = new MutableLiveData<>(); + final public MutableLiveData<AuthenticationData> authentication = new MutableLiveData<>(); + final public MutableLiveData<SelfIdData> selfid = new MutableLiveData<>(); + final public MutableLiveData<SystemData> system = new MutableLiveData<>(); + final public MutableLiveData<OperatorIdData> operatorid = new MutableLiveData<>(); + + private final long macAddress; + private byte[] pubkey; + public AircraftObject(long macAddress) { + this.macAddress = macAddress; + this.pubkey = new byte[0]; + } + public long getMacAddress() { return macAddress; } + + public Connection getConnection() { return connection.getValue(); } + public Identification getIdentification() { + return identification.getValue(); + } + public LocationData getLocation() { return location.getValue(); } + public AuthenticationData getAuthentication() { return authentication.getValue(); } + public SelfIdData getSelfID() { return selfid.getValue(); } + public SystemData getSystem() { return system.getValue(); } + public OperatorIdData getOperatorID() { return operatorid.getValue(); } + + // Non-zero authentication data pages do not contain the following fields. Save them for displaying + private int authPageCountSave; + private int authLengthSave; + private long authTimestampSave; + + // Multiple authentication messages are possible, each transmitting a part of the + // authentication signature. Collect the data into authDataCombined. + private byte[] authDataCombined = new byte[Constants.MAX_AUTH_DATA]; + + @RequiresApi(api = Build.VERSION_CODES.O) + public AuthenticationData combineAuthentication(AuthenticationData newData) { + AuthenticationData currData = authentication.getValue(); + if (currData == null) + currData = new AuthenticationData(newData.getAircraft()); + + currData.setADCounter(newData.getADCounter()); + currData.setTimestamp(newData.getTimestamp()); + + int offset = 0; + int amount = Constants.MAX_AUTH_PAGE_ZERO_SIZE; + if (newData.getAuthDataPage() == 0) { + authPageCountSave = newData.getAuthPageCount(); + authLengthSave = newData.getAuthLength(); + authTimestampSave = newData.getAuthTimestamp(); + } else { + offset = Constants.MAX_AUTH_PAGE_ZERO_SIZE + (newData.getAuthDataPage() - 1) * Constants.MAX_AUTH_PAGE_NON_ZERO_SIZE; + amount = Constants.MAX_AUTH_PAGE_NON_ZERO_SIZE; + } + for (int i = offset; i < offset + amount; i++) + authDataCombined[i] = newData.getAuthData()[i]; + + currData.setAuthType(newData.getAuthType()); + currData.setAuthPageCount(authPageCountSave); + currData.setAuthLength(authLengthSave); + currData.setAuthTimestamp(authTimestampSave); + currData.setAuthData(authDataCombined); + //Should always be authenticated but current validate function only works on drip + if ((authPageCountSave - 1) == newData.getAuthDataPage()){ + // returns boolean but also sets values on currData therefore + // result can for this instance be ignored + boolean isValid = currData.validate(identification.getValue().getUasId(), this.pubkey); + if(!isValid){ + currData.setServerIssues(this.pubkey.length == 0); + }else{ + currData.setServerIssues(false); + } + } + return currData; + } + + public byte[] getPubkey() { + if(this.pubkey == null) return new byte[0]; + return this.pubkey; + } + + @Override @NonNull + public String toString() { + return "AircraftObject{" + + "macAddress=" + macAddress + + ", identification=" + identification + + '}'; + } + + public void setPubKey(byte[] pubKey) { + if(pubKey == null){ + this.pubkey = new byte[0]; + }else { + this.pubkey = pubKey; + } + } +} diff --git a/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/data/AuthenticationData.java b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/data/AuthenticationData.java new file mode 100644 index 0000000000000000000000000000000000000000..8d4fe8371b80555325d097bba718ef323d0fc34d --- /dev/null +++ b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/data/AuthenticationData.java @@ -0,0 +1,228 @@ +/* + * Copyright (C) 2019 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + * + */ +package org.dripdronescanner.android.data; + +import static org.dripdronescanner.android.Constants.ED25519_SIG_LENGTH; +import static org.dripdronescanner.android.Constants.MAX_DRIP_ID_BYTE_SIZE; + +import android.os.Build; + +import androidx.annotation.RequiresApi; + +import org.bouncycastle.math.ec.rfc8032.Ed25519; +import org.dripdronescanner.android.Constants; + +import java.sql.Timestamp; +import java.time.Instant; +import java.util.Arrays; +import java.util.Locale; + +public class AuthenticationData extends MessageData { + public static int TIMESTAMP_OFFSET = 1546300800; + + private AuthTypeEnum authType; + private AircraftObject aircraft; + private int authDataPage; + private int authPageCount; + private int authLength; + private long authTimestamp; + + + /** + * reference to id type used to determine + * how to validate auth message. + */ + private int authIDType; + private byte[] authData; + private boolean isValidated; + private boolean serverIssues; + + public AuthenticationData(AircraftObject ac) { + super(); + aircraft = ac; + authType = AuthTypeEnum.None; + authDataPage = 0; + authPageCount = 0; + authLength = 0; + authTimestamp = 0; + authData = new byte[0]; + isValidated = false; + serverIssues = false; + } + + public enum AuthTypeEnum { + None(0), + UAS_ID_Signature(1), + Operator_ID_Signature(2), + Message_Set_Signature(3), + Network_Remote_ID(4), + Private_Use_0xA(0xA), + Private_Use_0xB(0xB), + Private_Use_0xC(0xC), + Private_Use_0xD(0xD), + Private_Use_0xE(0xE), + Private_Use_0xF(0xF); + + AuthTypeEnum(int id) { this.id = id; } + public final int id; + } + + public AircraftObject getAircraft() { + return aircraft; + } + + public AuthTypeEnum getAuthType() { return authType; } + void setAuthType(AuthTypeEnum authType) { this.authType = authType; } + public void setAuthType(int authType) { + switch(authType) { + case 1: this.authType = AuthTypeEnum.UAS_ID_Signature; break; + case 2: this.authType = AuthTypeEnum.Operator_ID_Signature; break; + case 3: this.authType = AuthTypeEnum.Message_Set_Signature; break; + case 4: this.authType = AuthTypeEnum.Network_Remote_ID; break; + case 0xA: this.authType = AuthTypeEnum.Private_Use_0xA; break; + case 0xB: this.authType = AuthTypeEnum.Private_Use_0xB; break; + case 0xC: this.authType = AuthTypeEnum.Private_Use_0xC; break; + case 0xD: this.authType = AuthTypeEnum.Private_Use_0xD; break; + case 0xE: this.authType = AuthTypeEnum.Private_Use_0xE; break; + case 0xF: this.authType = AuthTypeEnum.Private_Use_0xF; break; + default: this.authType = AuthTypeEnum.None; break; + } + } + + int getAuthDataPage() { return authDataPage; } + public void setAuthDataPage(int authDataPage) { + if (authDataPage < 0) + authDataPage = 0; + if (authDataPage > (getMaxAuthPages()-1)) + authDataPage = getMaxAuthPages()-1; + this.authDataPage = authDataPage; + } + + private int getMaxAuthPages(){ + if(this.authIDType == Identification.IdTypeEnum.DRIP_ID.getValue()) + return Constants.MAX_DRIP_AUTH_DATA_PAGES; + return Constants.MAX_AUTH_DATA_PAGES; + + } + + int getAuthPageCount() { return authPageCount; } + + public String getAuthPageCountAsString() { + return String.format(Locale.US,"%d pages", authPageCount); + } + public void setAuthPageCount(int authPageCount) { + if (authPageCount < 0) + authPageCount = 0; + if (authPageCount > getMaxAuthPages()) + authPageCount = getMaxAuthPages(); + this.authPageCount = authPageCount; + } + + int getAuthLength() { return authLength; } + public String getAuthLengthAsString() { + return String.format(Locale.US,"%d bytes", authLength); + } + public void setAuthLength(int authLength) { + if (authLength < 0) + authLength = 0; + if (authLength > getMaxAuthData()) + authLength = getMaxAuthData(); + this.authLength = authLength; + } + + private int getMaxAuthData(){ + if(this.authIDType == Identification.IdTypeEnum.DRIP_ID.getValue()) + return Constants.MAX_DRIP_AUTH_DATA; + return Constants.MAX_AUTH_DATA; + } + + long getAuthTimestamp() { return authTimestamp; } + public String getAuthTimestampAsString() { + if (authTimestamp == 0) + return "Unknown"; + Timestamp time = new Timestamp((1546300800L + authTimestamp) * 1000); + return time.toString(); + } + public void setAuthTimestamp(long authTimestamp) { this.authTimestamp = authTimestamp; } + + + byte[] getAuthData() { return authData; } + public void setAuthData(byte[] authData) { this.authData = authData; } + public String getAuthenticationDataAsString() { + + StringBuilder sb = new StringBuilder(); + if(aircraft.identification.getValue().getIdType() == Identification.IdTypeEnum.DRIP_ID) { + if (this.isValidated){ + sb.append("Valid"); + }else { + if(this.serverIssues){ + sb.append("Unknown due to server issues or failed manual entry"); + }else { + sb.append("Invalid"); + } + } + }else { + for (int i = 0; i < authLength; i++) { + sb.append(String.format("%02X ", authData[i])); + } + } + return sb.toString(); + } + + public void setServerIssues(boolean issues){ + this.serverIssues = issues; + } + + public boolean isServerIssues(){ + return this.serverIssues; + } + + /** + * Validates the authentication message. Currently this method + * can only validate DRIP authentication messages. + * @param id The id for the drone + * @param publicKey The public key for the drone + * @return true if the authentication is valid, false otherwise. + */ + @RequiresApi(api = Build.VERSION_CODES.O) + public boolean validate(final byte[] id, final byte[] publicKey) { + if(publicKey.length != Ed25519.PUBLIC_KEY_SIZE) return false; + int hhitOffset = ED25519_SIG_LENGTH; + int timeStampOffset = ED25519_SIG_LENGTH+MAX_DRIP_ID_BYTE_SIZE; + + byte[] hhit = Arrays.copyOfRange(authData, hhitOffset, timeStampOffset); + if(!Arrays.equals(hhit, id)) + return false; + + int expiration = astmDecodeInt(Arrays.copyOfRange(authData, timeStampOffset, timeStampOffset+4)); + if(expiration <= getASTMTime()) + return false; + + byte[] sig = Arrays.copyOfRange(authData, 0, 64); + byte[] msg = Arrays.copyOfRange(authData, ED25519_SIG_LENGTH, this.authLength); + boolean signatureValid = Ed25519.verify( + sig, 0, publicKey, 0, msg, 0, this.authLength-ED25519_SIG_LENGTH); + this.isValidated = signatureValid; + return signatureValid; + } + + + + public int astmDecodeInt(byte[] val) { + /* Assuming byte order is LE */ + int newVal = 0; + for (int i = 0; i < val.length; i++) { + newVal |= (val[i] & 0xFF) << (i*8); // & 0xFF to get unsigned byte + } + return newVal; + } + + @RequiresApi(api = Build.VERSION_CODES.O) + public static long getASTMTime() { + return Instant.now().getEpochSecond() - TIMESTAMP_OFFSET; + } +} diff --git a/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/data/Connection.java b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/data/Connection.java new file mode 100644 index 0000000000000000000000000000000000000000..c3c49047b6741c9e46165952e38c0d75974d3438 --- /dev/null +++ b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/data/Connection.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2019 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + * + */ +package org.dripdronescanner.android.data; + +import java.util.Locale; + +public class Connection extends MessageData { + public int rssi; + public String transportType; + public String macAddress; + public long lastSeen; + public long firstSeen; + public long msgDelta; + + public String getMsgDeltaAsString() { + if (msgDelta / 1000 == 0) + return String.format(Locale.US,"%3d ms", msgDelta); + else { + double seconds = msgDelta; + seconds /= 1000; + return String.format(Locale.US,"%.1f s", seconds); + } + } + + public Connection() { + super(); + } +} diff --git a/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/data/Identification.java b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/data/Identification.java new file mode 100644 index 0000000000000000000000000000000000000000..bd07182797e8612e791f8c32072b8b5a514d5b32 --- /dev/null +++ b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/data/Identification.java @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2019 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + * + */ +package org.dripdronescanner.android.data; + +import static org.dripdronescanner.android.Constants.ED25519_CURVE_ID; +import static org.dripdronescanner.android.Constants.HIPv2_CONTEXT_ID; + +import android.util.Log; + +import org.bouncycastle.crypto.digests.CSHAKEDigest; +import org.bouncycastle.util.encoders.Hex; +import org.dripdronescanner.android.Constants; + +import java.util.Arrays; + +public class Identification extends MessageData { + + private UaTypeEnum uaType; + private IdTypeEnum idType; + private byte ogaId; + private byte[] uasId; + private byte[] hid; + private AircraftObject aircraft; + private boolean valid; + private boolean isValidated; + + + public Identification(AircraftObject aircraftObject) { + super(); + uaType = UaTypeEnum.None; + idType = IdTypeEnum.None; + uasId = new byte[0]; + aircraft = aircraftObject; + valid = false; + isValidated = false; + + } + + + + public enum UaTypeEnum { + None, + Aeroplane, + Helicopter_or_Multirotor, + Gyroplane, + Hybrid_Lift, // VTOL. Fixed wing aircraft that can take off vertically + Ornithopter, + Glider, + Kite, + Free_balloon, + Captive_balloon, + Airship, + Free_fall_parachute, // Unpowered + Rocket, + Tethered_powered_aircraft, + Ground_obstacle, + Other, + } + + public enum IdTypeEnum { + None(0),//0 + Serial_Number(1),//1 + CAA_Registration_ID(2),//2 + UTM_Assigned_ID(3),//3 + DRIP_ID(4);//4 + + int value; + + IdTypeEnum(int val){ + this.value = val; + } + + public int getValue(){ + return this.value; + } + + + public static boolean contains(int idType){ + if(0<= idType && idType <= 4){ + return true; + } + return false; + } + } + + public UaTypeEnum getUaType() { return uaType; } + public void setUaType(int uaType) { + switch(uaType) { + case 1: this.uaType = UaTypeEnum.Aeroplane; break; + case 2: this.uaType = UaTypeEnum.Helicopter_or_Multirotor; break; + case 3: this.uaType = UaTypeEnum.Gyroplane; break; + case 4: this.uaType = UaTypeEnum.Hybrid_Lift; break; + case 5: this.uaType = UaTypeEnum.Ornithopter; break; + case 6: this.uaType = UaTypeEnum.Glider; break; + case 7: this.uaType = UaTypeEnum.Kite; break; + case 8: this.uaType = UaTypeEnum.Free_balloon; break; + case 9: this.uaType = UaTypeEnum.Captive_balloon; break; + case 10: this.uaType = UaTypeEnum.Airship; break; + case 11: this.uaType = UaTypeEnum.Free_fall_parachute; break; + case 12: this.uaType = UaTypeEnum.Rocket; break; + case 13: this.uaType = UaTypeEnum.Tethered_powered_aircraft; break; + case 14: this.uaType = UaTypeEnum.Ground_obstacle; break; + case 15: this.uaType = UaTypeEnum.Other; break; + default: this.uaType = UaTypeEnum.None; break; + } + } + + public IdTypeEnum getIdType() { return idType; } + public void setIdType(int idType) { + switch(idType) { + case 1: this.idType = IdTypeEnum.Serial_Number; break; + case 2: this.idType = IdTypeEnum.CAA_Registration_ID; break; + case 3: this.idType = IdTypeEnum.UTM_Assigned_ID; break; + case 4: this.idType = IdTypeEnum.DRIP_ID;break; + default: this.idType = IdTypeEnum.None; break; + } + } + + public byte[] getUasId() { return uasId; } + public String getUasIdAsString() { + if(this.idType == IdTypeEnum.DRIP_ID){ + return Hex.toHexString(uasId).replaceFirst("0x", ""); + } + return new String(uasId); + } + + public void setUasId(byte[] uasId) { + if (uasId.length <= Constants.MAX_ID_BYTE_SIZE) { + this.uasId = uasId; + } + hid = Arrays.copyOfRange(uasId, 4, 8); // hid from byte 4 + ogaId = (byte) (uasId[3] & 0x0F); // oga_id in bits 4-7 in byte 3 + } + + public String getIDValid() { + if(this.isValidated && this.valid) return "Valid"; + if(this.isValidated) return "Invalid"; + return "Unknown due to server issues or failed manual entry"; + } + + public boolean isValid() { + return valid; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + + if (o == null || getClass() != o.getClass()) + return false; + + Identification that = (Identification) o; + return uaType == that.uaType && + idType == that.idType && + Arrays.equals(uasId, that.uasId); + } + + public boolean validateId(byte[] pub_key) { + if(pub_key == null){ + pub_key = new byte[0]; + } + if(ogaId == 0x5) { + byte[] message = new byte[HIPv2_CONTEXT_ID.length + ED25519_CURVE_ID.length + aircraft.getPubkey().length]; + System.arraycopy(HIPv2_CONTEXT_ID, 0, message, 0, HIPv2_CONTEXT_ID.length); + System.arraycopy(ED25519_CURVE_ID, 0, message, HIPv2_CONTEXT_ID.length, ED25519_CURVE_ID.length); + System.arraycopy(pub_key, 0, message, HIPv2_CONTEXT_ID.length + ED25519_CURVE_ID.length, aircraft.getPubkey().length); + + CSHAKEDigest dgst = new CSHAKEDigest(128, null, HIPv2_CONTEXT_ID); + byte[] output = new byte[8]; + dgst.update(message, 0, message.length); + dgst.doOutput(output, 0, 8); + + boolean idValidated = Arrays.equals(output, Arrays.copyOfRange(uasId, 8, 16)); + //setIdValidationStatus(idValidated ? "Valid" : "Invalid"); + this.isValidated = pub_key.length != 0; + this.valid = idValidated; + return idValidated; + } else { + this.isValidated = false; + this.valid = false; + Log.w("DRIP_ID", "Oga id not recognized during verification of id."); + return false; + } + } +} diff --git a/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/data/LocationData.java b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/data/LocationData.java new file mode 100644 index 0000000000000000000000000000000000000000..304da0eff60427b96d541221dcabc732c820ac34 --- /dev/null +++ b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/data/LocationData.java @@ -0,0 +1,334 @@ +/* + * Copyright (C) 2019 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + * + */ +package org.dripdronescanner.android.data; + +import java.util.Locale; + +public class LocationData extends MessageData { + + private StatusEnum status; + private heightTypeEnum heightType; + private double direction; + private double speedHorizontal; + private double speedVertical; + private double latitude; + private double longitude; + private double altitudePressure; + private double altitudeGeodetic; + private double height; + private HorizontalAccuracyEnum horizontalAccuracy; + private VerticalAccuracyEnum verticalAccuracy; + private VerticalAccuracyEnum baroAccuracy; + private SpeedAccuracyEnum speedAccuracy; + private double locationTimestamp; + private double timeAccuracy; + private float distance; + + public LocationData() { + super(); + status = StatusEnum.Undeclared; + heightType = heightTypeEnum.Takeoff; + direction = 361; // 361 is the Invalid value in the specification + speedHorizontal = 255; // 255 is the Invalid value in the specification + speedVertical = 63; // 63 is the Invalid value in the specification + latitude = 0; + longitude = 0; + altitudePressure = -1000; // -1000 is the Invalid value in the specification + altitudeGeodetic = -1000; // -1000 is the Invalid value in the specification + height = -1000; // -1000 is the Invalid value in the specification + horizontalAccuracy = HorizontalAccuracyEnum.Unknown; + verticalAccuracy = VerticalAccuracyEnum.Unknown; + baroAccuracy = VerticalAccuracyEnum.Unknown; + speedAccuracy = SpeedAccuracyEnum.Unknown; + locationTimestamp = 0; + timeAccuracy = 0; + } + + public enum StatusEnum { + Undeclared, + Ground, + Airborne, + Emergency, + } + public StatusEnum getStatus() { return status; } + public void setStatus(int status) { + switch(status) { + case 1: this.status = StatusEnum.Ground; break; + case 2: this.status = StatusEnum.Airborne; break; + case 3: this.status = StatusEnum.Emergency; break; + default: this.status = StatusEnum.Undeclared; break; + } + } + + public enum heightTypeEnum { + Takeoff, + Ground, + } + public heightTypeEnum getHeightType() { return heightType; } + public void setHeightType(int heightType) { + if (heightType == 1) + this.heightType = heightTypeEnum.Ground; + else + this.heightType = heightTypeEnum.Takeoff; + } + + public double getDirection() { return direction; } + public String getDirectionAsString() { + if (direction != 361) + return String.format(Locale.US,"%3.0f deg", direction); + else + return ("Unknown"); + } + public void setDirection(double direction) { + if (direction < 0 || direction > 360) + direction = 361; // 361 is defined in the specification as the Invalid value + this.direction = direction; + } + + public double getSpeedHorizontal() { return speedHorizontal; } + public String getSpeedHorizontalAsString() { + if (speedHorizontal != 255) + return String.format(Locale.US,"%3.2f m/s", speedHorizontal); + else + return ("Unknown"); + } + public String getSpeedHorizontalLessPreciseAsString() { + if (speedHorizontal != 255) + return String.format(Locale.US,"%3.0fm/s", speedHorizontal); + else + return ("Unknown"); + } + public void setSpeedHorizontal(double speedHorizontal) { + if (speedHorizontal < 0 || speedHorizontal > 254.25) + speedHorizontal = 255; // 255 is defined in the specification as the Invalid value + this.speedHorizontal = speedHorizontal; + } + + public double getSpeedVertical() { return speedVertical; } + public String getSpeedVerticalAsString() { + if (speedVertical != 63) + return String.format(Locale.US,"%3.2f m/s", speedVertical); + else + return ("Unknown"); + } + public void setSpeedVertical(double speedVertical) { + if (speedVertical < -62 || speedVertical > 62) + speedVertical = 63; // 63 is defined in the specification as the Invalid value + this.speedVertical = speedVertical; + } + + public double getLatitude() { return latitude; } + public String getLatitudeAsString() { + if (latitude == 0 && longitude == 0) + return "Unknown"; + return String.format(Locale.US,"%3.7f", latitude); + } + public void setLatitude(double latitude) { + if (latitude < -90 || latitude > 90) { + latitude = 0; + this.longitude = 0; // both equal to zero is defined in the specification as the Invalid value + } + this.latitude = latitude; + } + + public double getLongitude() { return longitude; } + public String getLongitudeAsString() { + if (latitude == 0 && longitude == 0) + return "Unknown"; + return String.format(Locale.US,"%3.7f", longitude); + } + public void setLongitude(double longitude) { + if (longitude < -180 || longitude > 180) { + this.latitude = 0; + longitude = 0; // both equal to zero is defined in the specification as the Invalid value + } + this.longitude = longitude; + } + + private String getAltitudeAsString(double altitude) { + if (altitude == -1000) + return "Unknown"; + return String.format(Locale.US,"%3.1f m", altitude); + } + public double getAltitudePressure() { return altitudePressure; } + public String getAltitudePressureAsString() { return getAltitudeAsString(altitudePressure); } + public void setAltitudePressure(double altitudePressure) { + if (altitudePressure < -1000 || altitudePressure > 31767) + altitudePressure = -1000; // -1000 is defined in the specification as the Invalid value + this.altitudePressure = altitudePressure; + } + public double getAltitudeGeodetic() { return altitudeGeodetic; } + public String getAltitudeGeodeticAsString() { return getAltitudeAsString(altitudeGeodetic); } + public void setAltitudeGeodetic(double altitudeGeodetic) { + if (altitudeGeodetic < -1000 || altitudeGeodetic > 31767) + altitudeGeodetic = -1000; // -1000 is defined in the specification as the Invalid value + this.altitudeGeodetic = altitudeGeodetic; + } + public double getHeight() { return height; } + public String getHeightAsString() { return getAltitudeAsString(height); } + public String getHeightLessPreciseAsString() { + if (height == -1000) + return "Unknown"; + return String.format(Locale.US,"%3.0fm", height); + } + public void setHeight(double height) { + if (height < -1000 || height > 31767) + height = -1000; // -1000 is defined in the specification as the Invalid value + this.height = height; + } + + public enum HorizontalAccuracyEnum { + Unknown, + kilometers_18_52, + kilometers_7_408, + kilometers_3_704, + kilometers_1_852, + meters_926, + meters_555_6, + meters_185_2, + meters_92_6, + meters_30, + meters_10, + meters_3, + meters_1, + } + public HorizontalAccuracyEnum getHorizontalAccuracy() { return horizontalAccuracy; } + public String getHorizontalAccuracyAsString() { + switch(horizontalAccuracy) { + case kilometers_18_52: return "< 18.52 km"; + case kilometers_7_408: return "< 7.408 km"; + case kilometers_3_704: return "< 3.704 km"; + case kilometers_1_852: return "< 1.852 km"; + case meters_926: return "< 926 m"; + case meters_555_6: return "< 555.6 m"; + case meters_185_2: return "< 185.2 m"; + case meters_92_6: return "< 92.6 m"; + case meters_30: return "< 30 m"; + case meters_10: return "< 10 m"; + case meters_3: return "< 3 m"; + case meters_1: return "< 1 m"; + default: return "Unknown"; + } + } + public void setHorizontalAccuracy(int horizontalAccuracy) { + switch(horizontalAccuracy) { + case 1: this.horizontalAccuracy = HorizontalAccuracyEnum.kilometers_18_52; break; + case 2: this.horizontalAccuracy = HorizontalAccuracyEnum.kilometers_7_408; break; + case 3: this.horizontalAccuracy = HorizontalAccuracyEnum.kilometers_3_704; break; + case 4: this.horizontalAccuracy = HorizontalAccuracyEnum.kilometers_1_852; break; + case 5: this.horizontalAccuracy = HorizontalAccuracyEnum.meters_926; break; + case 6: this.horizontalAccuracy = HorizontalAccuracyEnum.meters_555_6; break; + case 7: this.horizontalAccuracy = HorizontalAccuracyEnum.meters_185_2; break; + case 8: this.horizontalAccuracy = HorizontalAccuracyEnum.meters_92_6; break; + case 9: this.horizontalAccuracy = HorizontalAccuracyEnum.meters_30; break; + case 10: this.horizontalAccuracy = HorizontalAccuracyEnum.meters_10; break; + case 11: this.horizontalAccuracy = HorizontalAccuracyEnum.meters_3; break; + case 12: this.horizontalAccuracy = HorizontalAccuracyEnum.meters_1; break; + default: this.horizontalAccuracy = HorizontalAccuracyEnum.Unknown; break; + } + } + + public enum VerticalAccuracyEnum { + Unknown, + meters_150, + meters_45, + meters_25, + meters_10, + meters_3, + meters_1, + } + public VerticalAccuracyEnum getVerticalAccuracy() { return verticalAccuracy; } + public String getVerticalAccuracyAsString(VerticalAccuracyEnum accuracy) { + switch(accuracy) { + case meters_150: return "< 150 m"; + case meters_45: return "< 45 m"; + case meters_25: return "< 25 m"; + case meters_10: return "< 10 m"; + case meters_3: return "< 3 m"; + case meters_1: return "< 1 m"; + default: return "Unknown"; + } + } + private VerticalAccuracyEnum intToVerticalAccuracy(int verticalAccuracy) { + switch(verticalAccuracy) { + case 1: return VerticalAccuracyEnum.meters_150; + case 2: return VerticalAccuracyEnum.meters_45; + case 3: return VerticalAccuracyEnum.meters_25; + case 4: return VerticalAccuracyEnum.meters_10; + case 5: return VerticalAccuracyEnum.meters_3; + case 6: return VerticalAccuracyEnum.meters_1; + default: return VerticalAccuracyEnum.Unknown; + } + } + public void setVerticalAccuracy(int verticalAccuracy) { + this.verticalAccuracy = intToVerticalAccuracy(verticalAccuracy); + } + public VerticalAccuracyEnum getBaroAccuracy() { return baroAccuracy; } + public void setBaroAccuracy(int verticalAccuracy) { + this.baroAccuracy = intToVerticalAccuracy(verticalAccuracy); + } + + public enum SpeedAccuracyEnum { + Unknown, + meter_per_second_10, + meter_per_second_3, + meter_per_second_1, + meter_per_second_0_3, + } + public SpeedAccuracyEnum getSpeedAccuracy() { return speedAccuracy; } + public String getSpeedAccuracyAsString() { + switch(speedAccuracy) { + case meter_per_second_10: return "< 10 m/s"; + case meter_per_second_3: return "< 3 m/s"; + case meter_per_second_1: return "< 1 m/s"; + case meter_per_second_0_3: return "< 0.3 m/s"; + default: return "Unknown"; + } + } + public void setSpeedAccuracy(int speedAccuracy) { + switch(speedAccuracy) { + case 1: this.speedAccuracy = SpeedAccuracyEnum.meter_per_second_10; break; + case 2: this.speedAccuracy = SpeedAccuracyEnum.meter_per_second_3; break; + case 3: this.speedAccuracy = SpeedAccuracyEnum.meter_per_second_1; break; + case 4: this.speedAccuracy = SpeedAccuracyEnum.meter_per_second_0_3; break; + default: this.speedAccuracy = SpeedAccuracyEnum.Unknown; break; + } + } + + public double getLocationTimestamp() { return locationTimestamp; } + private double getTimeStampMinutes() { return ((int) (locationTimestamp / 10)) / 60; } + private double getTimeStampSeconds() { return (locationTimestamp/10) % 60; } + public String getLocationTimestampAsString() { + return String.format(Locale.US,"%02.0f:%02.0f", getTimeStampMinutes(), getTimeStampSeconds()); + } + public void setLocationTimestamp(double locationTimestamp) { + if (locationTimestamp < 0) + locationTimestamp = 0; + if (locationTimestamp > 36000) + locationTimestamp = 36000; // Max one hour is allowed. Unit is 0.1s + this.locationTimestamp = locationTimestamp; + } + + public double getTimeAccuracy() { return timeAccuracy; } + public String getTimeAccuracyAsString() { + if (timeAccuracy == 0) + return "Unknown"; + else + return String.format(Locale.US,"<= %1.1f s", timeAccuracy); + } + public void setTimeAccuracy(double timeAccuracy) { + if (timeAccuracy < 0) + timeAccuracy = 0; + if (timeAccuracy > 1.5) + timeAccuracy = 1.5; // 1.5s is the maximum value in the specification + this.timeAccuracy = timeAccuracy; + } + + public String getDistanceAsString() { return String.format(Locale.US,"~%.0f m", distance); } + public float getDistance() { return distance; } + public void setDistance(float distance) { this.distance = distance; } +} diff --git a/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/data/MessageData.java b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/data/MessageData.java new file mode 100644 index 0000000000000000000000000000000000000000..50a43225a1873f24820d3556ab66fca19b8e74e7 --- /dev/null +++ b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/data/MessageData.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2019 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + * + */ +package org.dripdronescanner.android.data; + +import android.os.SystemClock; + +import java.sql.Timestamp; +import java.util.Locale; + +public class MessageData { + private int adCounter; + private long timestamp; + + MessageData() { + adCounter = 0; + timestamp = 0; + } + + public void setADCounter(int adCounter) { + this.adCounter = adCounter; + } + + int getADCounter() { + return this.adCounter; + } + + public String getADCounterAsString() { + return String.format(Locale.US ,"%3d", this.adCounter); + } + + public void setTimestamp(long timestamp) { + this.timestamp = timestamp; + } + + long getTimestamp() { + return timestamp; + } + + public String getTimestampAsString() { + long msSinceEvent = (SystemClock.elapsedRealtimeNanos() - getTimestamp()) / 1000000L; + long actualTime = System.currentTimeMillis() - msSinceEvent; + Timestamp time = new Timestamp(actualTime); + return time.toString(); + } +} diff --git a/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/data/OperatorIdData.java b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/data/OperatorIdData.java new file mode 100644 index 0000000000000000000000000000000000000000..2fb15f93d2e2863319826d443fff57235e593b60 --- /dev/null +++ b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/data/OperatorIdData.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2019 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + * + */ +package org.dripdronescanner.android.data; + +import org.dripdronescanner.android.Constants; + +public class OperatorIdData extends MessageData { + + private int operatorIdType; + private byte[] operatorId; + + public OperatorIdData() { + super(); + operatorIdType = 0; + operatorId = new byte[0]; + } + + public void setOperatorIdType(int operatorIdType) { + if (operatorIdType < 0) + operatorIdType = 0; + if (operatorIdType > 255) + operatorIdType = 255; + this.operatorIdType = operatorIdType; + } + public int getOperatorIdType() { return operatorIdType; } + + public void setOperatorId(byte[] operatorId) { + if (operatorId.length <= Constants.MAX_ID_BYTE_SIZE) + this.operatorId = operatorId; + } + public byte[] getOperatorId() { return operatorId; } +} diff --git a/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/data/SelfIdData.java b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/data/SelfIdData.java new file mode 100644 index 0000000000000000000000000000000000000000..f9d0e5051334c7b2da16653d4c208aa6c6c716ad --- /dev/null +++ b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/data/SelfIdData.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2019 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + * + */ +package org.dripdronescanner.android.data; + +import org.dripdronescanner.android.Constants; + +public class SelfIdData extends MessageData { + + private int descriptionType; + private byte[] operationDescription; + + public SelfIdData() { + super(); + descriptionType = 0; + operationDescription = new byte[0]; + } + + public void setDescriptionType(int descriptionType) { + if (descriptionType < 0) + descriptionType = 0; + if (descriptionType > 255) + descriptionType = 255; + this.descriptionType = descriptionType; + } + public int getDescriptionType() { return descriptionType; } + + public void setOperationDescription(byte[] operationDescription) { + if (operationDescription.length <= Constants.MAX_STRING_BYTE_SIZE) + this.operationDescription = operationDescription; + } + public byte[] getOperationDescription() { return operationDescription; } +} diff --git a/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/data/SystemData.java b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/data/SystemData.java new file mode 100644 index 0000000000000000000000000000000000000000..228000d1e9709035105d44bbecf25993cbcbf12b --- /dev/null +++ b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/data/SystemData.java @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2019 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + * + */ +package org.dripdronescanner.android.data; + +import java.util.Locale; + +public class SystemData extends MessageData { + + private operatorLocationTypeEnum operatorLocationType; + private classificationTypeEnum classificationType; + private double operatorLatitude; + private double operatorLongitude; + private int areaCount; + private int areaRadius; + private double areaCeiling; + private double areaFloor; + private categoryEnum category; + private classValueEnum classValue; + + public SystemData() { + super(); + operatorLocationType = operatorLocationTypeEnum.Invalid; + classificationType = classificationTypeEnum.Undeclared; + operatorLatitude = 0; + operatorLongitude = 0; + areaCount = 0; + areaRadius = 0; + areaCeiling = -1000; // -1000 is the Invalid value in the specification + areaFloor = -1000; // -1000 is the Invalid value in the specification + category = categoryEnum.Undeclared; + classValue = classValueEnum.Undeclared; + } + + public enum operatorLocationTypeEnum { + TakeOff, + LiveGNSS, + FixedLocation, + Invalid, + } + + public operatorLocationTypeEnum getOperatorLocationType() { return operatorLocationType; } + public void setOperatorLocationType(int operatorLocationType) { + switch(operatorLocationType) { + case 0: this.operatorLocationType = operatorLocationTypeEnum.TakeOff; break; + case 1: this.operatorLocationType = operatorLocationTypeEnum.LiveGNSS; break; + case 2: this.operatorLocationType = operatorLocationTypeEnum.FixedLocation; break; + default: this.operatorLocationType = operatorLocationTypeEnum.Invalid; break; + } + } + + public enum classificationTypeEnum { + Undeclared, + EU, // European Union + } + + public classificationTypeEnum getclassificationType() { return classificationType; } + public void setClassificationType(int classificationType) { + if (classificationType == 1) { + this.classificationType = classificationTypeEnum.EU; + } else { + this.classificationType = classificationTypeEnum.Undeclared; + } + } + + public void setOperatorLatitude(double operatorLatitude) { + if (operatorLatitude < -90 || operatorLatitude > 90) { + operatorLatitude = 0; + this.operatorLongitude = 0; // both equal to zero is defined in the specification as the Invalid value + } + this.operatorLatitude = operatorLatitude; + } + public double getOperatorLatitude() { return operatorLatitude; } + public String getOperatorLatitudeAsString() { + if (operatorLatitude == 0 && operatorLongitude == 0) + return "Unknown"; + return String.format(Locale.US,"%3.7f", operatorLatitude); + } + + public void setOperatorLongitude(double operatorLongitude) { + if (operatorLongitude < -180 || operatorLongitude > 180) { + this.operatorLatitude = 0; + operatorLongitude = 0; // both equal to zero is defined in the specification as the Invalid value + } + this.operatorLongitude = operatorLongitude; + } + public double getOperatorLongitude() { return operatorLongitude; } + public String getOperatorLongitudeAsString() { + if (operatorLatitude == 0 && operatorLongitude == 0) + return "Unknown"; + return String.format(Locale.US,"%3.7f", operatorLongitude); + } + + public void setAreaCount(int areaCount) { this.areaCount = areaCount; } + public int getAreaCount() { return areaCount; } + + public void setAreaRadius(int areaRadius) { this.areaRadius = areaRadius; } + public int getAreaRadius() { return areaRadius; } + public String getAreaRadiusAsString() { + return String.format(Locale.US,"%d m", areaRadius); + } + + private String getAltitudeAsString(double altitude) { + if (altitude == -1000) + return "Unknown"; + return String.format(Locale.US,"%3.1f m", altitude); + } + + public void setAreaCeiling(double areaCeiling) { this.areaCeiling = areaCeiling; } + public double getAreaCeiling() { return areaCeiling; } + public String getAreaCeilingAsString() { return getAltitudeAsString(areaCeiling); } + + public void setAreaFloor(double areaFloor) { this.areaFloor = areaFloor; } + public double getAreaFloor() { return areaFloor; } + public String getAreaFloorAsString() { return getAltitudeAsString(areaFloor); } + + public enum categoryEnum { + Undeclared, + EU_Open, + EU_Specific, + EU_Certified, + } + + public categoryEnum getCategory() { return category; } + public void setCategory(int category) { + if (classificationType == classificationTypeEnum.EU) { + switch(category) { + case 1: this.category = categoryEnum.EU_Open; break; + case 2: this.category = categoryEnum.EU_Specific; break; + case 3: this.category = categoryEnum.EU_Certified; break; + default: this.category = categoryEnum.Undeclared; break; + } + } else { + this.category = categoryEnum.Undeclared; + } + } + + public enum classValueEnum { + Undeclared, + EU_Class_0, + EU_Class_1, + EU_Class_2, + EU_Class_3, + EU_Class_4, + EU_Class_5, + EU_Class_6, + } + + public classValueEnum getClassValue() { return classValue; } + public void setClassValue(int classValue) { + if (classificationType == classificationTypeEnum.EU) { + switch(classValue) { + case 1: this.classValue = classValueEnum.EU_Class_0; break; + case 2: this.classValue = classValueEnum.EU_Class_1; break; + case 3: this.classValue = classValueEnum.EU_Class_2; break; + case 4: this.classValue = classValueEnum.EU_Class_3; break; + case 5: this.classValue = classValueEnum.EU_Class_4; break; + case 6: this.classValue = classValueEnum.EU_Class_5; break; + case 7: this.classValue = classValueEnum.EU_Class_6; break; + default: this.classValue = classValueEnum.Undeclared; break; + } + } else { + this.classValue = classValueEnum.Undeclared; + } + } +} + diff --git a/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/data/Util.java b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/data/Util.java new file mode 100644 index 0000000000000000000000000000000000000000..8dbd653f28f195c073b74306e16eb2f60997887b --- /dev/null +++ b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/data/Util.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2019 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + * + */ +package org.dripdronescanner.android.data; + +import androidx.annotation.Nullable; +import androidx.lifecycle.Observer; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +public class Util { + + public static void main(String[] args) { + Set<String> str = new HashSet<>(); + str.add("a"); + str.add("b"); + str.add("c"); + + Set<String> str2 = new HashSet<>(); + str2.add("c"); + str2.add("d"); + str2.add("e"); + + DiffObserver<String> stringDiffObserver = new DiffObserver<String>() { + @Override + public void onChanged(@Nullable Set<String> newSet) { + System.out.println("> new: "+newSet); + super.onChanged(newSet); + } + + @Override + public void onAdded(Collection<String> added) { + System.out.println("added: "+added); + } + + @Override + public void onRemoved(Collection<String> removed) { + System.out.println("removed: "+removed); + } + }; + + stringDiffObserver.onChanged(str); + stringDiffObserver.onChanged(str2); + + } + + public static class SetDifference<T> { + final Set<T> added; + final Set<T> removed; + + SetDifference(Set<? extends T> newSet, Set<? extends T> oldSet) { + added = difference(newSet, oldSet); + removed = difference(oldSet, newSet); + } + } + + /** OTHER - SET */ + private static <E> Set<E> difference(Set<? extends E> set, Set<? extends E> other) { + HashSet<E> diff = new HashSet<>(); + for (E e : set) { + if (!other.contains(e)) { + diff.add(e); + } + } + return diff; + } + + + /** + * set - other, what is not in other + */ + + public static class DiffObserver<T> implements Observer<Set<T>> { + Set<T> last = Collections.emptySet(); + + @Override + public void onChanged(@Nullable Set<T> newSet) { + SetDifference<T> difference = new SetDifference<>(newSet, last); + + if (!difference.added.isEmpty()) { + onAdded(difference.added); + } + if (!difference.removed.isEmpty()) { + onRemoved(difference.removed); + } + last = newSet; + } + + public void onAdded(Collection<T> added) { + + } + + public void onRemoved(Collection<T> removed) { + + } + } +} diff --git a/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/log/LogEntry.java b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/log/LogEntry.java new file mode 100644 index 0000000000000000000000000000000000000000..bd39bee7bd783e659bdfa97ed821eace2a3e7a07 --- /dev/null +++ b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/log/LogEntry.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2019 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + * + */ +package org.dripdronescanner.android.log; + +import androidx.annotation.NonNull; + +public class LogEntry { + int session; + long timestamp; + String transportType; + String macAddress; + int callbackType; + int rssi; + byte[] data; + StringBuilder csvLog; + + final static String[] HEADER = new String[]{ + "session", + "timestamp (nanos)", + "transportType", + "mac address", + "callbackType", + "rssi", + "payload" + }; + + static final String DELIM = ","; + + @NonNull + public String toString() { + return toString(true); + } + + String toString(boolean withData) { + String s = session + DELIM + + timestamp + DELIM + + transportType + DELIM + + macAddress + DELIM + + callbackType + DELIM + + rssi; + if (withData) { + // The first byte in the data array contains the length of the subsequent data + int length = ((int) data[0] & 0xFF) + 1; + s += DELIM + toHexString(data, length); + } + s += DELIM + csvLog; + return s; + } + + static LogEntry fromString(String line) { + String[] fields = line.split("\\s*[,]\\s*"); + if (fields.length < 6) { + return null; + } + + try { + LogEntry entry = new LogEntry(); + entry.session = Integer.parseInt(fields[0]); + entry.timestamp = Long.parseLong(fields[1]); + entry.transportType = fields[2]; + entry.macAddress = fields[3]; + entry.callbackType = Integer.parseInt(fields[4]); + entry.rssi = Integer.parseInt(fields[5]); + entry.data = parseHexString(fields[6]); + return entry; + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + public static String toHexString(byte[] bytes, int len) { + StringBuilder sb = new StringBuilder((len) * 3); + int i = 0; + for (byte b : bytes) { + i++; + if (i > len) + break; + int val = b & 0xFF; + sb.append(String.format("%02X ", val)); + } + return sb.toString(); + } + + private static byte[] parseHexString(String hexString) { + String[] byteStrings = hexString.split("\\s+"); + byte[] bytes = new byte[byteStrings.length]; + for (int i = 0; i < byteStrings.length; i++) { + bytes[i] = (byte) Integer.parseInt(byteStrings[i], 16); + } + return bytes; + } + +} diff --git a/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/log/LogMessageEntry.java b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/log/LogMessageEntry.java new file mode 100644 index 0000000000000000000000000000000000000000..08a8eb6ed2589cce5f1b5bbdd24fa1b91d34221c --- /dev/null +++ b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/log/LogMessageEntry.java @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2019 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + * + */ +package org.dripdronescanner.android.log; + +import org.dripdronescanner.android.Constants; +import org.dripdronescanner.android.network.OpenDroneIdParser; + +import java.util.ArrayList; +import java.util.Collections; + +public class LogMessageEntry { + + private static final String DELIM = Constants.DELIM; + private static final String DELIM_BASIC_ID = DELIM + DELIM + DELIM; + private static final String DELIM_LOCATION = DELIM + DELIM + DELIM + DELIM + DELIM + DELIM + + DELIM + DELIM + DELIM + DELIM + DELIM + DELIM + + DELIM + DELIM + DELIM + DELIM + DELIM + DELIM + + DELIM; + private static final String DELIM_AUTHENTICATION = DELIM + DELIM + DELIM + DELIM + DELIM + + DELIM; + private static final String DELIM_SELF_ID = DELIM + DELIM; + private static final String DELIM_SYSTEM = DELIM + DELIM + DELIM + DELIM + DELIM + DELIM + + DELIM + DELIM + DELIM + DELIM; + private static final String DELIM_OPERATOR = DELIM + DELIM; + + private final ArrayList<OpenDroneIdParser.Message> messages; + + public LogMessageEntry() { this.messages = new ArrayList<>(); } + + public void add(OpenDroneIdParser.Message<?> message) { messages.add(message); } + + @SuppressWarnings("unchecked") + public StringBuilder getMessageLogEntry() { + if (messages.size() == 0) + return null; + + Collections.sort(messages); + + StringBuilder entry = new StringBuilder(); + int i = 0; + + if (messages.get(i).header.type == OpenDroneIdParser.Type.BASIC_ID) { + OpenDroneIdParser.Message<OpenDroneIdParser.BasicId> message = + (OpenDroneIdParser.Message<OpenDroneIdParser.BasicId>) messages.get(i); + entry.append(message.payload.toCsvString()); + i++; + } else { + entry.append(DELIM_BASIC_ID); + } + + if (i < messages.size() && messages.get(i).header.type == OpenDroneIdParser.Type.LOCATION) { + OpenDroneIdParser.Message<OpenDroneIdParser.Location> message = + (OpenDroneIdParser.Message<OpenDroneIdParser.Location>) messages.get(i); + entry.append(message.payload.toCsvString()); + i++; + } else { + entry.append(DELIM_LOCATION); + } + + for (int j = 0; j < Constants.MAX_AUTH_DATA_PAGES; j++) + { + if (i < messages.size() && messages.get(i).header.type == OpenDroneIdParser.Type.AUTH) { + OpenDroneIdParser.Message<OpenDroneIdParser.Authentication> message = + (OpenDroneIdParser.Message<OpenDroneIdParser.Authentication>) messages.get(i); + if (message.payload.getAuthDataPage() == j) { + entry.append(message.payload.toCsvString()); + i++; + } else { + entry.append(DELIM_AUTHENTICATION); + } + } else { + entry.append(DELIM_AUTHENTICATION); + } + } + + if (i < messages.size() && messages.get(i).header.type == OpenDroneIdParser.Type.SELFID) { + OpenDroneIdParser.Message<OpenDroneIdParser.SelfID> message = + (OpenDroneIdParser.Message<OpenDroneIdParser.SelfID>) messages.get(i); + entry.append(message.payload.toCsvString()); + i++; + } else { + entry.append(DELIM_SELF_ID); + } + + if (i < messages.size() && messages.get(i).header.type == OpenDroneIdParser.Type.SYSTEM) { + OpenDroneIdParser.Message<OpenDroneIdParser.SystemMsg> message = + (OpenDroneIdParser.Message<OpenDroneIdParser.SystemMsg>) messages.get(i); + entry.append(message.payload.toCsvString()); + i++; + } else { + entry.append(DELIM_SYSTEM); + } + + if (i < messages.size() && messages.get(i).header.type == OpenDroneIdParser.Type.OPERATOR_ID) { + OpenDroneIdParser.Message<OpenDroneIdParser.OperatorID> message = + (OpenDroneIdParser.Message<OpenDroneIdParser.OperatorID>) messages.get(i); + entry.append(message.payload.toCsvString()); + } else { + entry.append(DELIM_OPERATOR); + } + + return entry; + } +} diff --git a/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/log/LogWriter.java b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/log/LogWriter.java new file mode 100644 index 0000000000000000000000000000000000000000..afb83b9cd9d50e3fde4933c47c4dbd13ed0c7b96 --- /dev/null +++ b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/log/LogWriter.java @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2019 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + * + */ +package org.dripdronescanner.android.log; + +import android.bluetooth.le.ScanResult; +import android.text.TextUtils; +import android.util.Log; + +import org.dripdronescanner.android.Constants; +import org.dripdronescanner.android.network.OpenDroneIdParser; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.LinkedBlockingQueue; + +public class LogWriter { + private static final String TAG = "LogWriter"; + private final BufferedWriter writer; + private static int session = 0; + public static void bumpSession() { session++; } + private final BlockingQueue<String> logQueue = new LinkedBlockingQueue<>(); + private boolean loggingActive = false; + + public LogWriter(File file) throws IOException { + writer = new BufferedWriter( + new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8)); + ExecutorService exec = Executors.newSingleThreadExecutor(); + + Log.i(TAG, "starting logging to " + file); + exec.submit(() -> { + try { + loggingActive = true; + long last = System.currentTimeMillis(); + + // write header + writer.write(TextUtils.join(",", LogEntry.HEADER)); + writer.write("," + OpenDroneIdParser.BasicId.csvHeader()); + writer.write(OpenDroneIdParser.Location.csvHeader()); + for (int i = 0; i < Constants.MAX_AUTH_DATA_PAGES; i++) + writer.write(OpenDroneIdParser.Authentication.csvHeader()); + writer.write(OpenDroneIdParser.SelfID.csvHeader()); + writer.write(OpenDroneIdParser.SystemMsg.csvHeader()); + writer.write(OpenDroneIdParser.OperatorID.csvHeader()); + writer.newLine(); + while (loggingActive) { + String log; + try { + log = logQueue.take(); + } catch (InterruptedException e) { + break; + } + writer.write(log); + writer.newLine(); + long time = System.currentTimeMillis(); + if (time - last > 1000) { + writer.flush(); + last = time; + } + } + } catch (IOException e) { + Log.e(TAG, "error writing log", e); + } finally { + try { + writer.flush(); + writer.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + }); + } + + public void logBluetooth(int callbackType, ScanResult result, + String transportType, StringBuilder csvLog) { + LogEntry entry = new LogEntry(); + entry.session = session; + entry.timestamp = result.getTimestampNanos(); + entry.transportType = transportType; + entry.macAddress = result.getDevice().getAddress(); + entry.callbackType = callbackType; + entry.rssi = result.getRssi(); + if (result.getScanRecord() != null) + entry.data = result.getScanRecord().getBytes(); + entry.csvLog = csvLog; + logQueue.add(entry.toString()); + } + + public void logNaN(Long timeNano, int peerHash, byte[] serviceSpecificInfo, + String transportType, StringBuilder csvLog) { + LogEntry entry = new LogEntry(); + entry.session = session; + entry.timestamp = timeNano; + entry.transportType = transportType; + entry.macAddress = Integer.toString(peerHash); + entry.callbackType = 0; + entry.rssi = 0; + if (serviceSpecificInfo != null) + entry.data = serviceSpecificInfo; + entry.csvLog = csvLog; + logQueue.add(entry.toString()); + } + + public void logBeacon(Long timeNano, android.net.wifi.ScanResult scanResult, byte[] data, + String transportType, StringBuilder csvLog) { + LogEntry entry = new LogEntry(); + entry.session = session; + entry.timestamp = timeNano; + entry.transportType = transportType; + entry.macAddress = scanResult.BSSID; + entry.callbackType = 0; + entry.rssi = scanResult.level; + if (data != null) + entry.data = data; + entry.csvLog = csvLog; + logQueue.add(entry.toString()); + } + + public void logText(String text,String[] data){ + StringBuilder logString = new StringBuilder("---------------------------------------------------\n" + text); + if(data != null){ + logString.append("\n\nExtra data:\n"); + for (String dataText:data) { + logString.append(dataText); + logString.append("\n"); + } + logQueue.add(logString.toString()); + } + } + + public void close() { + loggingActive = false; + try { + writer.flush(); + writer.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } +} + diff --git a/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/network/BluetoothScanner.java b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/network/BluetoothScanner.java new file mode 100644 index 0000000000000000000000000000000000000000..a5541917a4889bd79d39eb39ad4b9859621cd7e7 --- /dev/null +++ b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/network/BluetoothScanner.java @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2019 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + * + */ +package org.dripdronescanner.android.network; + +import android.annotation.TargetApi; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.le.BluetoothLeScanner; +import android.bluetooth.le.ScanCallback; +import android.bluetooth.le.ScanFilter; +import android.bluetooth.le.ScanRecord; +import android.bluetooth.le.ScanResult; +import android.bluetooth.le.ScanSettings; +import android.content.Context; +import android.os.Build; +import android.os.ParcelUuid; +import android.util.Log; + +import org.dripdronescanner.android.log.LogEntry; +import org.dripdronescanner.android.log.LogMessageEntry; +import org.dripdronescanner.android.log.LogWriter; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.UUID; + +public class BluetoothScanner { + private static final String TAG = "BluetoothManager"; + + private final OpenDroneIdDataManager dataManager; + private LogWriter logger; + private BluetoothAdapter bluetoothAdapter; + private BluetoothLeScanner bluetoothLeScanner; + private Context con; + + public BluetoothScanner(Context context, OpenDroneIdDataManager dataManager) { + this.dataManager = dataManager; + this.con = context; + Object object = context.getSystemService(Context.BLUETOOTH_SERVICE); + if (object == null) + return; + bluetoothAdapter = ((android.bluetooth.BluetoothManager) object).getAdapter(); + } + + public void setLogger(LogWriter logger) { this.logger = logger; } + + private static String dumpBytes(byte[] bytes) { + return LogEntry.toHexString(bytes, bytes.length); + } + + public BluetoothAdapter getBluetoothAdapter() { + return bluetoothAdapter; + } + + private final ScanCallback scanCallback = new ScanCallback() { + + @Override + public void onScanResult(int callbackType, ScanResult result) { + ScanRecord scanRecord = result.getScanRecord(); + if (scanRecord == null) + return; + byte[] bytes = scanRecord.getBytes(); + String addr = result.getDevice().getAddress().substring(0, 8); + int advertiseFlags = scanRecord.getAdvertiseFlags(); + int rssi = result.getRssi(); + String string = String.format(Locale.US, "scan: addr=%s flags=0x%02X rssi=% d, len=%d", + addr, advertiseFlags, rssi, bytes != null ? bytes.length : -1); + + String transportType = "BT4"; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && bluetoothAdapter.isLeCodedPhySupported()) { + if (result.getPrimaryPhy() == BluetoothDevice.PHY_LE_CODED) + transportType = "BT5"; + } + + LogMessageEntry logMessageEntry = new LogMessageEntry(); + dataManager.receiveDataBluetooth(bytes, result, logMessageEntry, transportType); + + StringBuilder csvLog = logMessageEntry.getMessageLogEntry(); + if (logger != null) + logger.logBluetooth(callbackType, result, transportType, csvLog); + + Log.w(TAG, "onScanResult: " + string); + if (bytes != null) { + Log.w(TAG, "-- bytes: " + dumpBytes(bytes)); + } + } + + @Override + public void onBatchScanResults(List<ScanResult> results) { + } + + @Override + public void onScanFailed(int errorCode) { + } + }; + + /* OpenDroneID Bluetooth beacons identify themselves by setting the GAP AD Type to + * "Service Data - 16-bit UUID" and the value to 0xFFFA for ASTM International, ASTM Remote ID. + * https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile/ + * https://www.bluetooth.com/specifications/assigned-numbers/16-bit-uuids-for-sdos/ + * Vol 3, Part B, Section 2.5.1 of the Bluetooth 5.1 Core Specification + * The AD Application Code is set to 0x0D = Open Drone ID. + */ + private static final UUID SERVICE_UUID = UUID.fromString("0000fffa-0000-1000-8000-00805f9b34fb"); + private static final ParcelUuid SERVICE_pUUID = new ParcelUuid(SERVICE_UUID); + private static final byte[] OPEN_DRONE_ID_AD_CODE = new byte[]{(byte) 0x0D}; + + @TargetApi(Build.VERSION_CODES.O) + public void startScan() { + if (bluetoothAdapter == null) + return; + + Log.d(TAG, ">>>> startScan"); + bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner(); + + ScanFilter.Builder builder = new ScanFilter.Builder(); + builder.setServiceData(SERVICE_pUUID, OPEN_DRONE_ID_AD_CODE); + List<ScanFilter> scanFilters = new ArrayList<>(); + scanFilters.add(builder.build()); + + ScanSettings scanSettings = new ScanSettings.Builder() + .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY) + .build(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && + bluetoothAdapter.isLeCodedPhySupported() && + bluetoothAdapter.isLeExtendedAdvertisingSupported()) { + // Enable scanning also for devices advertising on an LE Coded PHY S2 or S8 + scanSettings = new ScanSettings.Builder() + .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY) + .setLegacy(false) + .setPhy(ScanSettings.PHY_LE_ALL_SUPPORTED) + .build(); + } + + bluetoothLeScanner.startScan(scanFilters, scanSettings, scanCallback); + } + + public void stopScan() { + if (bluetoothLeScanner != null && scanCallback != null) { + bluetoothLeScanner.stopScan(scanCallback); + } + } +} diff --git a/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/network/CertificateServerAPI.java b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/network/CertificateServerAPI.java new file mode 100644 index 0000000000000000000000000000000000000000..cf59bad2f0a89716a1d9eaa86c5ffef11e1b6cff --- /dev/null +++ b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/network/CertificateServerAPI.java @@ -0,0 +1,171 @@ +package org.dripdronescanner.android.network; + +import android.app.Activity; +import android.content.Context; +import android.content.SharedPreferences; +import android.util.JsonReader; +import android.util.Log; + +import org.dripdronescanner.android.app.DebugActivity; +import org.dripdronescanner.android.log.LogWriter; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Callable; + +import javax.net.ssl.HttpsURLConnection; + +public class CertificateServerAPI implements Callable { + static final Map<String,String> idToKeys = new HashMap<>(); + public CertificateServerAPI() { + } + + + /** + * Fetches the public key for a given drip id. If key has already been fetched then + * it will simply be returned directly. Note that this is dependant on the lifecycle for this + * class. When the class is terminated any memory of keys will be forgotten. + * @param activity The activity that was running when this is called. + * @param basicId The ID object that is used to give the drip id. + * @param logger Potential logger object, can be null. + * @return A JSONObject in format {id1:{"drip_id":id1,"pub_key":pub_key1},id2:{...}...} + * or null. + */ + public static JSONObject getKey(Activity activity,OpenDroneIdParser.BasicId basicId,LogWriter logger) { + if(idToKeys.containsKey(basicId.toDRIP())){ + try { + return new JSONObject().put(basicId.toDRIP(), idToKeys.get(basicId.toDRIP())); + } catch (JSONException e) { + System.out.println("Unable to parse json data while reading locally: " +e); + if(logger != null) { + logger.logText("Unable to parse json data while reading locally: \" +e", null); + } + return null; + } + } + if(logger == null){ + try { + logger = new LogWriter(((DebugActivity) activity).getLoggerFileDir("certificateServerCall")); + }catch(IOException e){ + System.out.println("Unable to create a logger"); + Log.i("CertificateServerCall","Unable to create a logger, might cause missed logs"); + } + } + SharedPreferences sp = activity.getSharedPreferences("ONLY_PREF",Context.MODE_PRIVATE); + String base_url = sp.getString("url","https://192.168.0.1:8080"); + if (base_url.endsWith("/")) { + base_url = base_url.substring(0,base_url.length()-2); + } + String server_url = base_url +"/get/key"; + JSONObject result = null; + try { + URL url = new URL(server_url); + HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection(); + try { + urlConnection.setRequestMethod("POST"); + urlConnection.setRequestProperty("Content-Type","application/json;charset=UTF-8"); + urlConnection.setRequestProperty("Accept","application/json"); + urlConnection.setDoOutput(true); + urlConnection.setHostnameVerifier((s, sslSession) -> { + //Allows for local lan connections only. + return s.startsWith("192.168") || + s.startsWith("localhost") || + s.startsWith("127.0.0.1") || + s.startsWith("zebraland.ida.liu.se"); + }); + urlConnection.setChunkedStreamingMode(0); + urlConnection.connect(); + OutputStream out = new BufferedOutputStream(urlConnection.getOutputStream()); + + String data = "{\"drip_ids\": [\"" + basicId.toDRIP() + "\"]}"; + System.out.println("Sending data to certificate server:" + data); + out.write(data.getBytes(StandardCharsets.UTF_8)); + out.flush(); + out.close(); + InputStream in = new BufferedInputStream(urlConnection.getInputStream()); + JsonReader reader = new JsonReader(new InputStreamReader(in, "UTF-8")); + result = readJson(reader); + + } catch (IOException e) { + e.printStackTrace(); + System.out.println("Something went wrong in writing to or receiving from the server:" + url.toString()); + if(logger != null) { + String [] data = new String[2]; + data[0] = e.toString(); + data[1] = url.toString(); + logger.logText("Something went wrong in writing to or receiving from the server",data); + } + + + } catch (JSONException e) { + System.out.println("Unable to parse json data: " + e); + if(logger != null) { + logger.logText("Unable to parse json data: " + e, null); + } + + } finally { + urlConnection.disconnect(); + } + + } catch (IOException e) { + System.out.println("Unable to create connection, no internet or server is not responding:" + e); + if(logger != null) { + logger.logText("Unable to create connection, no internet or server is not responding: " +e, null); + } + + return null; + } + return result; + } + + private static JSONObject readJson(JsonReader reader) throws IOException, JSONException { + reader.beginArray(); + List<JSONObject> objects = new ArrayList<>(); + while(reader.hasNext()){ + objects.add(readObject(reader)); + } + reader.endArray(); + JSONObject result = new JSONObject(); + for(int i = 0; i < objects.size(); i++){ + result.put(objects.get(i).getString("drip_id"), objects.get(i).getString("pub_key")); + } + return result; + } + + private static JSONObject readObject(JsonReader reader) throws IOException, JSONException { + String id = null; + String pub_key = null; + reader.beginObject(); + while (reader.hasNext()) { + String name = reader.nextName(); + if (name.equals("drip_id")) { + id = reader.nextString(); + } else if (name.equals("pub_key")) { + pub_key = reader.nextString(); + } else { + reader.skipValue(); + } + } + reader.endObject(); + idToKeys.put(id,pub_key); + return new JSONObject().put("drip_id",id).put("pub_key",pub_key); + + } + + @Override + public Object call() throws Exception { + return null; + } +} diff --git a/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/network/OpenDroneIdDataManager.java b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/network/OpenDroneIdDataManager.java new file mode 100644 index 0000000000000000000000000000000000000000..2f9188883382471d713635a8b3e516ee86b35ecf --- /dev/null +++ b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/network/OpenDroneIdDataManager.java @@ -0,0 +1,307 @@ +/* + * Copyright (C) 2019 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + * + */ +package org.dripdronescanner.android.network; + +import android.Manifest; +import android.app.Activity; +import android.bluetooth.le.ScanResult; +import android.content.pm.PackageManager; + +import androidx.core.app.ActivityCompat; + +import com.google.android.gms.location.FusedLocationProviderClient; +import com.google.android.gms.location.LocationCallback; +import com.google.android.gms.location.LocationRequest; + +import org.dripdronescanner.android.Constants; +import org.dripdronescanner.android.data.AircraftObject; +import org.dripdronescanner.android.data.AuthenticationData; +import org.dripdronescanner.android.data.Connection; +import org.dripdronescanner.android.data.Identification; +import org.dripdronescanner.android.data.LocationData; +import org.dripdronescanner.android.data.OperatorIdData; +import org.dripdronescanner.android.data.SelfIdData; +import org.dripdronescanner.android.data.SystemData; +import org.dripdronescanner.android.log.LogMessageEntry; + +import java.util.Arrays; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class OpenDroneIdDataManager { + public final ConcurrentHashMap<Long, AircraftObject> aircraft = new ConcurrentHashMap<>(); + + public Activity activity; + public android.location.Location receiverLocation; + public LocationRequest locationRequest; + public LocationCallback locationCallback; + public FusedLocationProviderClient mFusedLocationClient; + + private final Callback callback; + + public static class Callback { + public void onNewAircraft(AircraftObject object) {} + } + + public OpenDroneIdDataManager(Callback callback) { + this.callback = callback; + } + + public Map<Long, AircraftObject> getAircraft() { + return aircraft; + } + + void receiveDataBluetooth(byte[] data, ScanResult result, LogMessageEntry logMessageEntry, String transportType) { + String macAddress = result.getDevice().getAddress(); + String macAddressCleaned = macAddress.replace(":", ""); + long macAddressLong = Long.parseLong(macAddressCleaned,16); + + OpenDroneIdParser.Message<?> message = OpenDroneIdParser.parseAdvertisingData(data, 6, result.getTimestampNanos(), logMessageEntry, receiverLocation,activity); + if (message == null) + return; + receiveData(result.getTimestampNanos(), macAddress, macAddressLong, result.getRssi(), + message, logMessageEntry, transportType); + + getReceiverLocation(); // Ensure the receiver location gets updated at some point in the future + } + + void receiveDataNaN(byte[] data, int peerHash, long timeNano, LogMessageEntry logMessageEntry, String transportType) { + OpenDroneIdParser.Message<?> message = OpenDroneIdParser.parseAdvertisingData(data, 1, timeNano, logMessageEntry, receiverLocation,activity); + if (message == null) + return; + receiveData(timeNano, "NaN ID: " + peerHash, peerHash, 0, message, + logMessageEntry, transportType); + + getReceiverLocation(); // Ensure the receiver location gets updated at some point in the future + } + + void receiveDataWiFiBeacon(byte[] data, String mac, long macLong, int rssi, long timeNano, LogMessageEntry logMessageEntry, String transportType) { + OpenDroneIdParser.Message<?> message = OpenDroneIdParser.parseAdvertisingData(data, 1, timeNano, logMessageEntry, receiverLocation,activity); + if (message == null) + return; + receiveData(timeNano, mac, macLong, rssi, message, logMessageEntry, transportType); + + getReceiverLocation(); // Ensure the receiver location gets updated at some point in the future + } + + @SuppressWarnings("unchecked") + void receiveData(long timeNano, String macAddress, long macAddressLong, int rssi, + OpenDroneIdParser.Message<?> message, LogMessageEntry logMessageEntry, String transportType) { + + // Handle connection + boolean newAircraft = false; + AircraftObject ac = aircraft.get(macAddressLong); + if (ac == null) { + ac = createNewAircraft(macAddress, macAddressLong); + newAircraft = true; + } + long currentTime = System.currentTimeMillis(); + ac.getConnection().msgDelta = currentTime - ac.getConnection().lastSeen; + ac.getConnection().lastSeen = currentTime; + ac.getConnection().rssi = rssi; + ac.getConnection().transportType = transportType; + ac.getConnection().setTimestamp(timeNano); + ac.connection.setValue(ac.connection.getValue()); + + if (newAircraft) { + aircraft.put(macAddressLong, ac); + callback.onNewAircraft(ac); + } + + if (message.header.type == OpenDroneIdParser.Type.MESSAGE_PACK) + handleMessagePack(ac, (OpenDroneIdParser.Message<OpenDroneIdParser.MessagePack>) message, timeNano, logMessageEntry, message.adCounter); + else + handleMessages(ac, message); + } + + @SuppressWarnings("unchecked") + private void handleMessages(AircraftObject ac, OpenDroneIdParser.Message<?> message) { + switch (message.header.type) { + case BASIC_ID: + handleBasicId(ac, (OpenDroneIdParser.Message<OpenDroneIdParser.BasicId>) message); + break; + case LOCATION: + handleLocation(ac, (OpenDroneIdParser.Message<OpenDroneIdParser.Location>) message); + break; + case AUTH: + handleAuthentication(ac, (OpenDroneIdParser.Message<OpenDroneIdParser.Authentication>) message); + break; + case SELFID: + handleSelfID(ac, (OpenDroneIdParser.Message<OpenDroneIdParser.SelfID>) message); + break; + case SYSTEM: + handleSystem(ac, (OpenDroneIdParser.Message<OpenDroneIdParser.SystemMsg>) message); + break; + case OPERATOR_ID: + handleOperatorID(ac, (OpenDroneIdParser.Message<OpenDroneIdParser.OperatorID>) message); + break; + } + } + + private AircraftObject createNewAircraft(String macAddress, long macAddressLong) { + AircraftObject ac = new AircraftObject(macAddressLong); + Connection connection = new Connection(); + connection.firstSeen = System.currentTimeMillis(); + connection.macAddress = macAddress; + ac.connection.setValue(connection); + + ac.identification.setValue(new Identification(ac)); + ac.location.setValue(new LocationData()); + ac.authentication.setValue(new AuthenticationData(ac)); + ac.selfid.setValue(new SelfIdData()); + ac.system.setValue(new SystemData()); + ac.operatorid.setValue(new OperatorIdData()); + return ac; + } + + private void handleBasicId(AircraftObject ac, OpenDroneIdParser.Message<OpenDroneIdParser.BasicId> message) { + OpenDroneIdParser.BasicId raw = message.payload; + Identification data = new Identification(ac); + data.setADCounter(message.adCounter); + data.setTimestamp(message.timestamp); + + data.setUaType(raw.uaType); + data.setIdType(raw.idType); + data.setUasId(raw.uasId); + + // Check if there already is a public key, + // otherwise we risk overwriting the manual public key entry + if(ac.getPubkey().length <= 0 && raw.pubKey != null) { + // If no public key, validate with raw + ac.setPubKey(raw.pubKey); + data.validateId(raw.pubKey); + } else if(ac.getPubkey().length > 0) { + // else validate with existing public key + data.validateId(ac.getPubkey()); + } + + ac.identification.postValue(data); + } + + private void handleLocation(AircraftObject ac, OpenDroneIdParser.Message<OpenDroneIdParser.Location> message) { + OpenDroneIdParser.Location raw = message.payload; + LocationData data = new LocationData(); + data.setADCounter(message.adCounter); + data.setTimestamp(message.timestamp); + + data.setStatus(raw.status); + data.setHeightType(raw.heightType); + data.setDirection(raw.getDirection()); + data.setSpeedHorizontal(raw.getSpeedHori()); + data.setSpeedVertical(raw.getSpeedVert()); + data.setLatitude(raw.getLatitude()); + data.setLongitude(raw.getLongitude()); + data.setAltitudePressure(raw.getAltitudePressure()); + data.setAltitudeGeodetic(raw.getAltitudeGeodetic()); + data.setHeight(raw.getHeight()); + data.setHorizontalAccuracy(raw.horizontalAccuracy); + data.setVerticalAccuracy(raw.verticalAccuracy); + data.setBaroAccuracy(raw.baroAccuracy); + data.setSpeedAccuracy(raw.speedAccuracy); + data.setLocationTimestamp(raw.timestamp); + data.setTimeAccuracy(raw.getTimeAccuracy()); + data.setDistance(raw.distance); + ac.location.postValue(data); + } + + private void handleAuthentication(AircraftObject ac, OpenDroneIdParser.Message<OpenDroneIdParser.Authentication> message) { + OpenDroneIdParser.Authentication raw = message.payload; + AuthenticationData data = new AuthenticationData(ac); + data.setADCounter(message.adCounter); + data.setTimestamp(message.timestamp); + + data.setAuthType(raw.authType); + data.setAuthDataPage(raw.authDataPage); + if (raw.authDataPage == 0) { + data.setAuthPageCount(raw.authPageCount); + data.setAuthLength(raw.authLength); + data.setAuthTimestamp(raw.authTimestamp); + } + data.setAuthData(raw.authData); + ac.authentication.postValue(ac.combineAuthentication(data)); + } + + private void handleSelfID(AircraftObject ac, OpenDroneIdParser.Message<OpenDroneIdParser.SelfID> message) { + OpenDroneIdParser.SelfID raw = message.payload; + SelfIdData data = new SelfIdData(); + data.setADCounter(message.adCounter); + data.setTimestamp(message.timestamp); + + data.setDescriptionType(raw.descriptionType); + data.setOperationDescription(raw.operationDescription); + ac.selfid.postValue(data); + } + + private void handleSystem(AircraftObject ac, OpenDroneIdParser.Message<OpenDroneIdParser.SystemMsg> message) { + OpenDroneIdParser.SystemMsg raw = message.payload; + SystemData data = new SystemData(); + data.setADCounter(message.adCounter); + data.setTimestamp(message.timestamp); + + data.setOperatorLocationType(raw.operatorLocationType); + data.setClassificationType(raw.classificationType); + data.setOperatorLatitude(raw.getLatitude()); + data.setOperatorLongitude(raw.getLongitude()); + data.setAreaCount(raw.areaCount); + data.setAreaRadius(raw.getAreaRadius()); + data.setAreaCeiling(raw.getAreaCeiling()); + data.setAreaFloor(raw.getAreaFloor()); + data.setCategory(raw.category); + data.setClassValue(raw.classValue); + ac.system.postValue(data); + } + + private void handleOperatorID(AircraftObject ac, OpenDroneIdParser.Message<OpenDroneIdParser.OperatorID> message) { + OpenDroneIdParser.OperatorID raw = message.payload; + OperatorIdData data = new OperatorIdData(); + data.setADCounter(message.adCounter); + data.setTimestamp(message.timestamp); + + data.setOperatorIdType(raw.operatorIdType); + data.setOperatorId(raw.operatorId); + ac.operatorid.postValue(data); + } + + private void handleMessagePack(AircraftObject ac, OpenDroneIdParser.Message<OpenDroneIdParser.MessagePack> message, long timestamp, LogMessageEntry logMessageEntry, int adCounter) { + OpenDroneIdParser.MessagePack raw = message.payload; + if (raw == null) + return; + + if (raw.messageSize != Constants.MAX_MESSAGE_SIZE || + raw.messagesInPack <= 0 || + raw.messagesInPack > Constants.MAX_MESSAGES_IN_PACK) + return; + + for (int i = 0; i < raw.messagesInPack; i++) { + int offset = i*raw.messageSize; + byte[] data = Arrays.copyOfRange(raw.messages, offset, offset + raw.messageSize); + OpenDroneIdParser.Message<?> subMessage = OpenDroneIdParser.parseMessage(data, 0, timestamp, logMessageEntry, receiverLocation, adCounter,activity); + if (subMessage == null) + return; + + handleMessages(ac, subMessage); + } + } + + public void getReceiverLocation() { + if (activity == null) + return; + + if (ActivityCompat.checkSelfPermission(activity, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED && + ActivityCompat.checkSelfPermission(activity, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) { + mFusedLocationClient.getLastLocation().addOnSuccessListener(activity, location -> { + if (location != null) { + receiverLocation = location; + } else { + mFusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, null); + } + }); + } + } + +} + diff --git a/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/network/OpenDroneIdParser.java b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/network/OpenDroneIdParser.java new file mode 100644 index 0000000000000000000000000000000000000000..536991416f0af9024886cfc1e375405e79e9146b --- /dev/null +++ b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/network/OpenDroneIdParser.java @@ -0,0 +1,697 @@ +/* + * Copyright (C) 2019 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + * + */ +package org.dripdronescanner.android.network; + +import android.app.Activity; +import android.util.Log; +import android.widget.Toast; + +import androidx.annotation.NonNull; + +import org.bouncycastle.util.encoders.Hex; +import org.dripdronescanner.android.Constants; +import org.dripdronescanner.android.data.Identification; +import org.dripdronescanner.android.log.LogMessageEntry; +import org.json.JSONException; +import org.json.JSONObject; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Locale; +import java.util.concurrent.CompletableFuture; + + +public class OpenDroneIdParser { + private static final String TAG = "OpenDroneIdParser"; + private static final String DELIM = Constants.DELIM; + + public enum Type { + BASIC_ID(0), + LOCATION(1), + AUTH(2), + SELFID(3), + SYSTEM(4), + OPERATOR_ID(5), + MESSAGE_PACK(0xF); + + Type(int id) { this.id = id; } + public final int id; + + public static Type fromId(int id) { + if (id == BASIC_ID.id) { + return BASIC_ID; + } else if (id == LOCATION.id) { + return LOCATION; + } else if (id == AUTH.id) { + return AUTH; + } else if (id == SELFID.id) { + return SELFID; + } else if (id == SYSTEM.id) { + return SYSTEM; + } else if (id == OPERATOR_ID.id) { + return OPERATOR_ID; + } else if (id == MESSAGE_PACK.id) { + return MESSAGE_PACK; + } else { + return null; + } + } + } + + public static class Header { + public Type type; + public int version; + + @Override @NonNull + public String toString() { + return "Header{" + + "type=" + type + + ", version=" + version + + '}'; + } + } + + public interface Payload { + String toCsvString(); + } + + private static final double LAT_LONG_MULTIPLIER = 1e-7; + private static final double SPEED_VERTICAL_MULTIPLIER = 0.5; + + public static class BasicId implements Payload { + int idType; + int uaType; + byte[] uasId; + byte[] pubKey; + + public BasicId(int idType, int uaType,byte[] idmsg) { + this.idType = idType; + this.uaType = uaType; + this.uasId = idmsg; + } + + public BasicId(int idlength) { + this.uasId = new byte[idlength]; + } + + public static String csvHeader() { + return "idType" + DELIM + + "uaType" + DELIM + + "uasId" + DELIM; + } + + public String toDRIP(){ + return Hex.toHexString(this.uasId).replaceFirst("0x", ""); + } + + public void setPubKey(byte[] pub_key) { + if (pub_key == null){ + this.pubKey = new byte[0]; + }else { + this.pubKey = pub_key; + } + } + + @Override + public String toCsvString() { + return idType + DELIM + + uaType + DELIM + + new String(uasId) + DELIM; + } + + @Override @NonNull + public String toString() { + return "BasicId{" + + "idType=" + idType + + ", uaType=" + uaType + + ", uasId='" + Arrays.toString(uasId) + '\'' + + '}'; + } + } + + public static class Location implements Payload { + int status; + int heightType; + int EWDirection; + int speedMult; + int Direction; + int speedHori; + int speedVert; + int droneLat; + int droneLon; + int altitudePressure; + int altitudeGeodetic; + int height; + int horizontalAccuracy; + int verticalAccuracy; + int baroAccuracy; + int speedAccuracy; + int timestamp; + int timeAccuracy; + float distance; + + static double calcSpeed(int value, int mult) { + if (mult == 0) + return value * 0.25; + else + return (value * 0.75) + (255 * 0.25); + } + + static double calcDirection(int value, int EW) { + if (EW == 0) + return value; + else + return value + 180; + } + + double getDirection() { return calcDirection(Direction, EWDirection); } + double getSpeedHori() { return calcSpeed(speedHori, speedMult); } + + double getSpeedVert() { return SPEED_VERTICAL_MULTIPLIER * speedVert; } + + double getLatitude() { + return LAT_LONG_MULTIPLIER * droneLat; + } + double getLongitude() { + return LAT_LONG_MULTIPLIER * droneLon; + } + + static double calcAltitude(int value) { return (double) value / 2 - 1000; } + double getAltitudePressure() { return calcAltitude(altitudePressure); } + double getAltitudeGeodetic() { return calcAltitude(altitudeGeodetic); } + double getHeight() { return calcAltitude(height); } + + double getTimeAccuracy() { return timeAccuracy * 0.1; } + + public static String csvHeader() { + return "status" + DELIM + + "heightType" + DELIM + + "EWDirection" + DELIM + + "speedMult" + DELIM + + "direction" + DELIM + + "speedHori" + DELIM + + "speedVert" + DELIM + + "droneLat" + DELIM + + "droneLon" + DELIM + + "altitudePressure" + DELIM + + "altitudeGeodetic" + DELIM + + "height" + DELIM + + "horizontalAccuracy" + DELIM + + "verticalAccuracy" + DELIM + + "baroAccuracy" + DELIM + + "speedAccuracy" + DELIM + + "timestamp" + DELIM + + "timeAccuracy" + DELIM + + "distance" + DELIM; + } + + @Override + public String toCsvString() { + return status + DELIM + + heightType + DELIM + + EWDirection + DELIM + + speedMult + DELIM + + Direction + DELIM + + speedHori + DELIM + + speedVert + DELIM + + droneLat + DELIM + + droneLon + DELIM + + altitudePressure + DELIM + + altitudeGeodetic + DELIM + + height + DELIM + + horizontalAccuracy + DELIM + + verticalAccuracy + DELIM + + baroAccuracy + DELIM + + speedAccuracy + DELIM + + timestamp + DELIM + + timeAccuracy + DELIM + + distance + DELIM; + } + + @Override @NonNull + public String toString() { + return "Location{" + + "status=" + status + + ", heightType=" + heightType + + ", EWDirection=" + EWDirection + + ", speedMult=" + speedMult + + ", direction=" + Direction + + ", speedHori=" + speedHori + + ", speedVert=" + speedVert + + ", droneLat=" + droneLat + + ", droneLon=" + droneLon + + ", altitudePressure=" + altitudePressure + + ", altitudeGeodetic=" + altitudeGeodetic + + ", height=" + height + + ", horizontalAccuracy=" + horizontalAccuracy + + ", verticalAccuracy=" + verticalAccuracy + + ", baroAccuracy=" + baroAccuracy + + ", speedAccuracy=" + speedAccuracy + + ", timestamp=" + timestamp + + ", timeAccuracy=" + timeAccuracy + + ", distance=" + distance + + '}'; + } + } + + public static class Authentication implements Payload { + int authType; + int authDataPage; + int authPageCount; + int authLength; + int authTimestamp; + byte[] authData = new byte[Constants.MAX_AUTH_DATA]; + + public int getAuthDataPage() { return authDataPage; } + + public static String csvHeader() { + return "authType" + DELIM + + "authDataPage" + DELIM + + "authPageCount" + DELIM + + "authLength" + DELIM + + "authTimestamp" + DELIM + + "authData" + DELIM; + } + + private String authDataToString() { + StringBuilder sb = new StringBuilder(); + for (byte authDatum : authData) { + sb.append(String.format("%02X ", authDatum)); + } + return sb.toString(); + } + + @Override + public String toCsvString() { + return authType + DELIM + + authDataPage + DELIM + + authPageCount + DELIM + + authLength + DELIM + + authTimestamp + DELIM + + authDataToString() + DELIM; + } + + @Override @NonNull + public String toString() { + return "Authentication{" + + "authType=" + authType + + ", authDataPage=" + authDataPage + + ", authPageCount=" + authPageCount + + ", authLength=" + authLength + + ", authTimestamp=" + authTimestamp + + ", authData='" + Arrays.toString(authData) + '\'' + + '}'; + } + } + + public static class SelfID implements Payload { + int descriptionType; + byte[] operationDescription = new byte[Constants.MAX_STRING_BYTE_SIZE]; + + public static String csvHeader() { + return "descriptionType" + DELIM + + "operationDescription" + DELIM; + } + + @Override + public String toCsvString() { + return descriptionType + DELIM + + new String(operationDescription) + DELIM; + } + + @Override @NonNull + public String toString() { + return "SelfID{" + + "descriptionType=" + descriptionType + + ", operationDescription='" + Arrays.toString(operationDescription) + '\'' + + '}'; + } + } + + public static class SystemMsg implements Payload { + int operatorLocationType; + int classificationType; + int operatorLatitude; + int operatorLongitude; + int areaCount; + int areaRadius; + int areaCeiling; + int areaFloor; + int category; + int classValue; + + double getLatitude() { + return LAT_LONG_MULTIPLIER * operatorLatitude; + } + double getLongitude() { + return LAT_LONG_MULTIPLIER * operatorLongitude; + } + + int getAreaRadius() { return areaRadius * 10; } + static double calcAltitude(int value) { return (double) value / 2 - 1000; } + double getAreaCeiling() { return calcAltitude(areaCeiling); } + double getAreaFloor() { return calcAltitude(areaFloor); } + + public static String csvHeader() { + return "operatorLocationType" + DELIM + + "classificationType" + DELIM + + "operatorLatitude" + DELIM + + "operatorLongitude" + DELIM + + "areaCount" + DELIM + + "areaRadius" + DELIM + + "areaCeiling" + DELIM + + "areaFloor" + DELIM + + "category" + DELIM + + "classValue" + DELIM; + } + + @Override + public String toCsvString() { + return operatorLocationType + DELIM + + classificationType + DELIM + + operatorLatitude + DELIM + + operatorLongitude + DELIM + + areaCount + DELIM + + areaRadius + DELIM + + areaCeiling + DELIM + + areaFloor + DELIM + + category + DELIM + + classValue + DELIM; + } + + @Override @NonNull + public String toString() { + return "PilotLocation{" + + "operatorLocationType=" + operatorLocationType + + ", classificationType=" + classificationType + + ", operatorLatitude=" + operatorLatitude + + ", operatorLongitude=" + operatorLongitude + + ", areaCount=" + areaCount + + ", areaRadius=" + areaRadius + + ", areaCeiling=" + areaCeiling + + ", areaFloor=" + areaFloor + + ", category=" + category + + ", class=" + classValue + + '}'; + } + } + + public static class OperatorID implements Payload { + int operatorIdType; + byte[] operatorId = new byte[Constants.MAX_ID_BYTE_SIZE]; + + public static String csvHeader() { + return "operatorIdType" + DELIM + + "operatorId" + DELIM; + } + + @Override + public String toCsvString() { + return operatorIdType + DELIM + + new String(operatorId) + DELIM; + } + @Override @NonNull + public String toString() { + return "OperatorID{" + + "operatorIdType=" + operatorIdType + + ", operatorId='" + Arrays.toString(operatorId) + '\'' + + '}'; + } + } + + public static class MessagePack implements Payload { + int messageSize; + int messagesInPack; + byte[] messages = new byte[Constants.MAX_MESSAGE_PACK_SIZE]; + + @Override @NonNull + public String toString() { + return "MessagePack{" + + "messageSize=" + messageSize + + ", messagesInPack=" + messagesInPack + + ", messages='" + Arrays.toString(messages) + '\'' + + '}'; + } + + @Override public String toCsvString() { return null; } + } + + public static class Message<T extends Payload> implements Comparable<Message<T>> { + final int adCounter; + final long timestamp; + public final Header header; + public final T payload; + + Message(Header header, T payload, long timestamp, int adCounter) { + this.adCounter = adCounter; + this.header = header; + this.payload = payload; + this.timestamp = timestamp; + } + + @Override + public int compareTo(@NonNull Message o) + { + if (this.header.type == Type.AUTH && o.header.type == Type.AUTH ) { + Authentication authThis = (OpenDroneIdParser.Authentication) this.payload; + Authentication authO = (OpenDroneIdParser.Authentication) o.payload; + return (authThis.authDataPage - authO.authDataPage); + } else { + return this.header.type.compareTo(o.header.type); + } + } + } + + static Message<Payload> parseAdvertisingData(byte[] payload, int offset, long timestamp, + LogMessageEntry logMessageEntry, + android.location.Location receiverLocation,Activity activity) { + if (offset <= 0 || payload.length < offset + Constants.MAX_MESSAGE_SIZE) + return null; + + int adCounter = payload[offset - 1] & 0xFF; + return parseMessage(payload, offset, timestamp, logMessageEntry, receiverLocation, adCounter,activity); + } + + static Message<Payload> parseMessage(byte[] payload, int offset, long timestamp, + LogMessageEntry logMessageEntry, + android.location.Location receiverLocation, int adCounter, Activity activity) { + if (payload.length < offset + Constants.MAX_MESSAGE_SIZE) + return null; + + ByteBuffer byteBuffer = ByteBuffer.wrap(payload, offset, Constants.MAX_MESSAGE_SIZE); + byteBuffer.order(ByteOrder.LITTLE_ENDIAN); + + Header header = new Header(); + int b = byteBuffer.get() & 0xFF; + int type = (b & 0xF0) >> 4; + header.type = Type.fromId(type); + if (header.type == null) { + Log.e(TAG, "Header type unknown"); + return null; + } + header.version = b & 0x0F; + + Payload payloadObj = null; + + switch (header.type) { + case BASIC_ID: + payloadObj = parseBasicId(byteBuffer,activity); + break; + case LOCATION: + payloadObj = parseLocation(byteBuffer, receiverLocation); + break; + case AUTH: + payloadObj = parseAuthentication(byteBuffer); + break; + case SELFID: + payloadObj = parseSelfID(byteBuffer); + break; + case SYSTEM: + payloadObj = parseSystem(byteBuffer); + break; + case OPERATOR_ID: + payloadObj = parseOperatorID(byteBuffer); + break; + case MESSAGE_PACK: + payloadObj = parseMessagePack(payload, offset); + break; + default: + Log.w(TAG, "Received unhandled message type: id=" + type); + + } + Message<Payload> message = new Message<>(header, payloadObj, timestamp, adCounter); + if (header.type != Type.MESSAGE_PACK) + logMessageEntry.add(message); + return message; + } + + + private static BasicId parseBasicId(ByteBuffer byteBuffer, Activity activity) { + BasicId basicId; + + int type = byteBuffer.get(); + int idType = (type & 0xF0) >> 4; + int uaType = type & 0x0F; + if (idType == Identification.IdTypeEnum.DRIP_ID.getValue()) { + basicId = new BasicId(Constants.MAX_DRIP_ID_BYTE_SIZE); + basicId.idType = idType; + basicId.uaType = uaType; + byte[] idArr = byteBuffer.array(); + basicId.uasId = Arrays.copyOfRange(idArr, 8, 8+Constants.MAX_DRIP_ID_BYTE_SIZE); + //fetch url from id message + CompletableFuture<JSONObject> uaDataFuture = CompletableFuture + .supplyAsync(() -> CertificateServerAPI.getKey(activity,basicId,null)); + uaDataFuture.handle((keys,error) -> { + if(keys == null || error != null){ + System.out.println("No key retrieved due to: " + error); + + Toast.makeText(activity.getApplicationContext(), + "Unable to retrieve key for id: " + basicId.toDRIP(), + Toast.LENGTH_LONG).show(); + }else{ + try { + String pub_key = ((String)((JSONObject)keys).get(basicId.toDRIP())).toLowerCase(Locale.ROOT); + basicId.setPubKey(Hex.decode(pub_key.getBytes(StandardCharsets.UTF_8))); + } catch (JSONException e) { + System.out.println("Unable to parse json data after getting it from api: " + e); + Toast.makeText(activity.getApplicationContext(), + "Unable to parse data correctly", + Toast.LENGTH_LONG).show(); + } + } + return basicId; + }); + }else { + basicId = new BasicId(Constants.MAX_ID_BYTE_SIZE); + basicId.idType = idType; + basicId.uaType = uaType; + byteBuffer.get(basicId.uasId, 0, Constants.MAX_ID_BYTE_SIZE); + } + return basicId; + } + + private static Location parseLocation(ByteBuffer byteBuffer, + android.location.Location receiverLocation) { + Location location = new Location(); + + int b = byteBuffer.get(); + + location.status = (b & 0xF0) >> 4; + location.heightType = (b & 0x04) >> 2; + location.EWDirection = (b & 0x02) >> 1; + location.speedMult = b & 0x01; + + location.Direction = byteBuffer.get() & 0xFF; + location.speedHori = byteBuffer.get() & 0xFF; + location.speedVert = byteBuffer.get(); + + location.droneLat = byteBuffer.getInt(); + location.droneLon = byteBuffer.getInt(); + + location.altitudePressure = byteBuffer.getShort(); + location.altitudeGeodetic = byteBuffer.getShort(); + location.height = byteBuffer.getShort(); + + int horiVertAccuracy = byteBuffer.get(); + location.horizontalAccuracy = horiVertAccuracy & 0x0F; + location.verticalAccuracy = (horiVertAccuracy & 0xF0) >> 4; + int speedBaroAccuracy = byteBuffer.get(); + location.baroAccuracy = (speedBaroAccuracy & 0xF0) >> 4; + location.speedAccuracy = speedBaroAccuracy & 0x0F; + location.timestamp = byteBuffer.getShort() & 0xFFFF; + location.timeAccuracy = byteBuffer.get() & 0x0F; + + // Use an older retrieved receiver location to calculate the distance to the drone + if (location.droneLat != 0 && location.droneLon != 0) { + android.location.Location droneLoc = new android.location.Location(""); + droneLoc.setLatitude(location.getLatitude()); + droneLoc.setLongitude(location.getLongitude()); + if (receiverLocation != null) + location.distance = receiverLocation.distanceTo(droneLoc); + } + + return location; + } + + private static Authentication parseAuthentication(ByteBuffer byteBuffer) { + Authentication authentication = new Authentication(); + + int type = byteBuffer.get(); + authentication.authType = (type & 0xF0) >> 4; + authentication.authDataPage = type & 0x0F; + + int offset = 0; + int amount = Constants.MAX_AUTH_PAGE_ZERO_SIZE; + if (authentication.authDataPage == 0) { + authentication.authPageCount = byteBuffer.get(); + authentication.authLength = byteBuffer.get(); + authentication.authTimestamp = byteBuffer.getInt(); + } else { + offset = Constants.MAX_AUTH_PAGE_ZERO_SIZE + (authentication.authDataPage - 1) * Constants.MAX_AUTH_PAGE_NON_ZERO_SIZE; + amount = Constants.MAX_AUTH_PAGE_NON_ZERO_SIZE; + } + if (authentication.authDataPage >= 0 && authentication.authDataPage < Constants.MAX_AUTH_DATA_PAGES) + for (int i = offset; i < offset + amount; i++) + authentication.authData[i] = byteBuffer.get(); + return authentication; + } + + private static SelfID parseSelfID(ByteBuffer byteBuffer) { + SelfID selfID = new SelfID(); + selfID.descriptionType = byteBuffer.get(); + byteBuffer.get(selfID.operationDescription, 0, Constants.MAX_STRING_BYTE_SIZE); + return selfID; + } + + private static SystemMsg parseSystem(ByteBuffer byteBuffer) { + SystemMsg s = new SystemMsg(); + + int b = byteBuffer.get(); + s.operatorLocationType = b & 0x03; + s.classificationType = (b & 0x1C) >> 2; + s.operatorLatitude = byteBuffer.getInt(); + s.operatorLongitude = byteBuffer.getInt(); + s.areaCount = byteBuffer.getShort() & 0xFFFF; + s.areaRadius = byteBuffer.get() & 0xFF; + s.areaCeiling = byteBuffer.getShort(); + s.areaFloor = byteBuffer.getShort(); + b = byteBuffer.get(); + s.category = (b & 0xF0) >> 4; + s.classValue = b & 0x0F; + return s; + } + + private static OperatorID parseOperatorID(ByteBuffer byteBuffer) { + OperatorID operatorID = new OperatorID(); + operatorID.operatorIdType = byteBuffer.get(); + byteBuffer.get(operatorID.operatorId, 0, Constants.MAX_ID_BYTE_SIZE); + return operatorID; + } + + + private static MessagePack parseMessagePack(byte[] payload, int offset) { + ByteBuffer byteBuffer = ByteBuffer.wrap(payload, offset + 1, 2); + byteBuffer.order(ByteOrder.LITTLE_ENDIAN); + + MessagePack messagePack = new MessagePack(); + messagePack.messageSize = byteBuffer.get(); + messagePack.messagesInPack = byteBuffer.get(); + + if (messagePack.messageSize != Constants.MAX_MESSAGE_SIZE || + messagePack.messagesInPack <= 0 || + messagePack.messagesInPack > Constants.MAX_MESSAGES_IN_PACK || + payload.length < offset + 1 + 2 + messagePack.messageSize*messagePack.messagesInPack) + return null; + + // Now that we know how much data is in the message, re-wrap and extract the data + byteBuffer = ByteBuffer.wrap(payload, offset + 1 + 2, messagePack.messageSize*messagePack.messagesInPack); + byteBuffer.get(messagePack.messages, 0, messagePack.messageSize * messagePack.messagesInPack); + return messagePack; + } +} diff --git a/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/network/WiFiBeaconScanner.java b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/network/WiFiBeaconScanner.java new file mode 100644 index 0000000000000000000000000000000000000000..e02461a0aa860027d3bb27f7c7cc1109b51904fb --- /dev/null +++ b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/network/WiFiBeaconScanner.java @@ -0,0 +1,245 @@ +/* + * Copyright (C) 2021 Skydio Inc + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * + */ + +package org.dripdronescanner.android.network; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.PackageManager; +import android.net.wifi.ScanResult; +import android.net.wifi.WifiManager; +import android.os.Build; +import android.os.CountDownTimer; +import android.os.SystemClock; +import android.util.Log; +import android.widget.Toast; + +import androidx.annotation.RequiresApi; + +import org.dripdronescanner.android.log.LogMessageEntry; +import org.dripdronescanner.android.log.LogWriter; + +import java.nio.ByteBuffer; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.List; +import java.util.Locale; + +public class WiFiBeaconScanner { + private static final int CIDLen = 3; + private static final int DriStartByteOffset = 4; + private static final int ScanTimerInterval = 2; + private static final int[] DRI_CID = { 0xFA, 0x0B, 0xBC }; + private static final int VendorTypeLen = 1; + private static final int VendorTypeValue = 0x0D; + private boolean WiFiScanEnabled = true; + private final OpenDroneIdDataManager dataManager; + private LogWriter logger; + private WifiManager wifiManager; + Context context; + int scanSuccess; + int scanFailed; + String startTime; + CountDownTimer countDownTimer; + boolean beaconScanDebugEnable; + + private static final String TAG = WiFiBeaconScanner.class.getSimpleName(); + + public void setLogger(LogWriter logger) { this.logger = logger; } + + public WiFiBeaconScanner(Context context, OpenDroneIdDataManager dataManager, LogWriter logger) { + this.dataManager = dataManager; + this.logger = logger; + + this.startTime = getCurrTimeStr(); + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M || + !context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) { + Toast.makeText(context, "WiFi Scanning is not supported", Toast.LENGTH_LONG).show(); + WiFiScanEnabled = false; + return; + } + + this.context = context; + beaconScanDebugEnable = false; + + wifiManager = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE); + if (!wifiManager.isWifiEnabled()) { + Toast.makeText(context, "Turning WiFi ON...", Toast.LENGTH_LONG).show(); + wifiManager.setWifiEnabled(true); + } + IntentFilter filter = new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION); + + BroadcastReceiver myReceiver = new BroadcastReceiver() { + @RequiresApi(api = Build.VERSION_CODES.M) + @Override + public void onReceive(Context context, Intent intent) { + handleScanResults(intent); + } + }; + + context.registerReceiver(myReceiver, filter); + + startCountDownTimer(); + // Kick off WiFi Scan + startScan(); + + } + + void processRemoteIdVendorIE(ScanResult scanResult, ByteBuffer buf) { + if (buf.remaining() < 30) + return; + byte[] dri_CID = new byte[CIDLen]; + byte[] arr = new byte[buf.remaining()]; + buf.get(dri_CID, 0, CIDLen); + byte[] vendorType = new byte[VendorTypeLen]; + buf.get(vendorType); + if ((dri_CID[0] & 0xFF) == DRI_CID[0] && (dri_CID[1] & 0xFF) == DRI_CID[1] && + (dri_CID[2] & 0xFF) == DRI_CID[2] && vendorType[0] == VendorTypeValue) { + buf.position(DriStartByteOffset); + buf.get(arr, 0, buf.remaining()); + LogMessageEntry logMessageEntry = new LogMessageEntry(); + long timeNano = SystemClock.elapsedRealtimeNanos(); + String transportType = "Beacon"; + dataManager.receiveDataWiFiBeacon(arr, scanResult.BSSID, scanResult.BSSID.hashCode(), + scanResult.level, timeNano, logMessageEntry, transportType); + + StringBuilder csvLog = logMessageEntry.getMessageLogEntry(); + if (logger != null) + logger.logBeacon(timeNano, scanResult, arr, transportType, csvLog); + } + } + + @RequiresApi(api = Build.VERSION_CODES.M) + void handleScanResults(Intent intent) { + if (wifiManager == null) { + Toast.makeText(context, "WiFi beacon scanner attach failed.", Toast.LENGTH_LONG).show(); + return; + + } + boolean freshScanResult = intent.getBooleanExtra(WifiManager.EXTRA_RESULTS_UPDATED, true); + String action = intent.getAction(); + if (freshScanResult && WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(action)) { + List<ScanResult> wifiList = wifiManager.getScanResults(); + for (ScanResult scanResult : wifiList) { + try { + handleResult(scanResult); + } catch (NoSuchFieldException | IllegalAccessException e) { + e.printStackTrace(); + } + } + startScan(); + } + } + + void handleResult(ScanResult scanResult) throws NoSuchFieldException, IllegalAccessException { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) { + // On earlier Android APIs, the information element field is hidden. + // Use reflection to access it. + Object value = ScanResult.class.getField("informationElements").get(scanResult); + ScanResult.InformationElement[] elements = (ScanResult.InformationElement[]) value; + if (elements == null) + return; + for (ScanResult.InformationElement element : elements) { + if (element == null) + continue; + Object valueId = element.getClass().getField("id").get(element); + if (valueId == null) + continue; + int id = (int) valueId; + if (id == 221) { + Object valueBytes = element.getClass().getField("bytes").get(element); + if (valueBytes == null) + continue; + ByteBuffer buf = ByteBuffer.wrap(((byte[]) valueBytes)).asReadOnlyBuffer(); + processRemoteIdVendorIE(scanResult, buf); + } + } + } else { + for (ScanResult.InformationElement element : scanResult.getInformationElements()) { + if (element != null && element.getId() == 221) { + ByteBuffer buf = element.getBytes(); + processRemoteIdVendorIE(scanResult, buf); + } + } + } + } + + public void startScan() { + if (!WiFiScanEnabled) { + return; + } + boolean ret = wifiManager.startScan(); + if (ret) { + scanSuccess++; + } else { + scanFailed++; + } + Log.d(TAG, "start_scan:" + ret); + printScanStats(ret); + } + + public void stopScan() { + if (!WiFiScanEnabled) { + return; + } + if (countDownTimer != null) { + countDownTimer.cancel(); + } + Toast.makeText(context, "Stopping WiFi scanning.", Toast.LENGTH_LONG).show(); + } + + // There are 2 ways to control WiFi scan: + // Continuous scan: Calls startSCan() from scan completion callback + // Periodic scan: countdown timer triggers startScan after expiry of the timer. + // If phone is debug mode and scan throttling is off, scan is triggered from onReceive() callback. + // But if scan throttling is turned on on the phone (default setting on the phone), then scan throttling kick in. + // In case of throttling, startScan() fails. We need timer thread to periodically kick off scanning. + public void startCountDownTimer() { + countDownTimer = new CountDownTimer(Long.MAX_VALUE, ScanTimerInterval * 1000) { + // This is called after every ScanTimerInterval sec. + public void onTick(long millisUntilFinished) { + startScan(); + } + + public void onFinish() { + } + }.start(); + } + + private String getCurrTimeStr() { + SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()); + return sdf.format(new Date()); + } + + private void printScanStats(boolean ret) { + StringBuilder sb = new StringBuilder(); + sb.append("Started: " + startTime + " "); + sb.append("success: " + scanSuccess + ", " + "failed: " + scanFailed); + sb.append(" curr-time: " + getCurrTimeStr() + ", " + " curr-status: " + ret); + + Log.d(TAG, sb.toString()); + + if (beaconScanDebugEnable) { + Toast.makeText(context, sb, Toast.LENGTH_LONG).show(); + } + } + + public void SetBeaconScanDebug(boolean enable) { + beaconScanDebugEnable = enable; + } +} diff --git a/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/network/WiFiNaNScanner.java b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/network/WiFiNaNScanner.java new file mode 100644 index 0000000000000000000000000000000000000000..dc40bfefa6f64eb4336ac66b797ef822723def5e --- /dev/null +++ b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/network/WiFiNaNScanner.java @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2020 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + * + */ +package org.dripdronescanner.android.network; + +import android.annotation.TargetApi; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.PackageManager; +import android.net.wifi.aware.AttachCallback; +import android.net.wifi.aware.DiscoverySessionCallback; +import android.net.wifi.aware.IdentityChangedListener; +import android.net.wifi.aware.PeerHandle; +import android.net.wifi.aware.SubscribeConfig; +import android.net.wifi.aware.SubscribeDiscoverySession; +import android.net.wifi.aware.WifiAwareManager; +import android.net.wifi.aware.WifiAwareSession; +import android.os.Build; +import android.os.Handler; +import android.os.SystemClock; +import android.util.Log; +import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.annotation.RequiresApi; + +import org.dripdronescanner.android.log.LogMessageEntry; +import org.dripdronescanner.android.log.LogWriter; + +import java.util.Arrays; +import java.util.List; + +public class WiFiNaNScanner { + + private final OpenDroneIdDataManager dataManager; + private LogWriter logger; + private boolean wifiAwareSupported = false; + private WifiAwareManager wifiAwareManager; + private WifiAwareSession wifiAwareSession; + private Handler handler; + Context context; + private static final String TAG = WiFiNaNScanner.class.getSimpleName(); + + public void setLogger(LogWriter logger) { this.logger = logger; } + + @RequiresApi(api = Build.VERSION_CODES.O) + public WiFiNaNScanner(Context context, OpenDroneIdDataManager dataManager, LogWriter logger) { + this.dataManager = dataManager; + this.logger = logger; + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O || + !context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_AWARE)) { + //Toast.makeText(context, "WiFi Aware is not supported", Toast.LENGTH_LONG).show(); + return; + } + wifiAwareSupported = true; + this.context = context; + handler = new Handler(); + + wifiAwareManager = (WifiAwareManager) context.getSystemService(Context.WIFI_AWARE_SERVICE); + if (wifiAwareManager != null && !wifiAwareManager.isAvailable()) { + Toast.makeText(context, "WiFi Aware is currently not available. Code to properly handle this must be added.", Toast.LENGTH_LONG).show(); + } + if (wifiAwareManager != null && wifiAwareManager.isAvailable()) { + Toast.makeText(context, "WiFi Aware is currently available.", Toast.LENGTH_LONG).show(); + } + + IntentFilter filter = new IntentFilter(WifiAwareManager.ACTION_WIFI_AWARE_STATE_CHANGED); + BroadcastReceiver myReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (wifiAwareManager.isAvailable()) { + Toast.makeText(context, "WiFi Aware became available.", Toast.LENGTH_LONG).show(); + startScan(); + } else { + Toast.makeText(context, "WiFi Aware was lost. Code to properly handle this must be added.", Toast.LENGTH_LONG).show(); + } + } + }; + context.registerReceiver(myReceiver, filter); + } + + @TargetApi(Build.VERSION_CODES.O) + private final AttachCallback attachCallback = new AttachCallback() { + @Override + public void onAttached(WifiAwareSession session) { + if (!wifiAwareSupported) + return; + + wifiAwareSession = session; + SubscribeConfig config = new SubscribeConfig.Builder() + .setServiceName("drone-drip-id") + .build(); + + wifiAwareSession.subscribe(config, new DiscoverySessionCallback() { + @Override + public void onSubscribeStarted(@NonNull SubscribeDiscoverySession session) { + } + + @Override + public void onServiceDiscovered(PeerHandle peerHandle, byte[] serviceSpecificInfo, List<byte[]> matchFilter) { + Log.i(TAG, "onServiceDiscovered: " + serviceSpecificInfo.length +": " + Arrays.toString(serviceSpecificInfo)); + String transportType = "NAN"; + LogMessageEntry logMessageEntry = new LogMessageEntry(); + long timeNano = SystemClock.elapsedRealtimeNanos(); + dataManager.receiveDataNaN(serviceSpecificInfo, peerHandle.hashCode(), timeNano, logMessageEntry, transportType); + + StringBuilder csvLog = logMessageEntry.getMessageLogEntry(); + if (logger != null) + logger.logNaN(timeNano, peerHandle.hashCode(), serviceSpecificInfo, transportType, csvLog); + } + }, null); + } + + @Override + public void onAttachFailed() { + Toast.makeText(context, "wifiAware onAttachFailed. Code to properly handle this must be added.", Toast.LENGTH_LONG).show(); + } + }; + + @TargetApi(Build.VERSION_CODES.O) + private final IdentityChangedListener identityChangedListener = new IdentityChangedListener() { + @Override + public void onIdentityChanged(byte[] mac) { + /*Byte[] macAddress = new Byte[mac.length]; + int i = 0; + for (byte b: mac) + macAddress[i++] = b; + Toast.makeText(context, "identityChangedListener. MAC: " + Arrays.toString(macAddress), Toast.LENGTH_LONG).show();*/ + } + }; + + @TargetApi(Build.VERSION_CODES.O) + public void startScan() { + if (!wifiAwareSupported) + return; + //Toast.makeText(context, "WiFi NaN attaching", Toast.LENGTH_LONG).show(); + if (wifiAwareManager.isAvailable()) + wifiAwareManager.attach(attachCallback, identityChangedListener, handler); + } + + @TargetApi(Build.VERSION_CODES.O) + public void stopScan() { + if (!wifiAwareSupported) + return; + Toast.makeText(context, "WiFi NaN stopping scanning. Code to properly handle this must be added.", Toast.LENGTH_LONG).show(); + if (wifiAwareManager.isAvailable() && wifiAwareSession != null) + wifiAwareSession.close(); + } +} diff --git a/drip-android-observer-master/Android/app/src/main/res/drawable-hdpi/ic_airplan.png b/drip-android-observer-master/Android/app/src/main/res/drawable-hdpi/ic_airplan.png new file mode 100644 index 0000000000000000000000000000000000000000..2a498c91d42941c33596962f1a29a2b2df9a0199 Binary files /dev/null and b/drip-android-observer-master/Android/app/src/main/res/drawable-hdpi/ic_airplan.png differ diff --git a/drip-android-observer-master/Android/app/src/main/res/drawable-hdpi/ic_person_pin_circle.png b/drip-android-observer-master/Android/app/src/main/res/drawable-hdpi/ic_person_pin_circle.png new file mode 100644 index 0000000000000000000000000000000000000000..5bf5626ae96e7aefb327cc20c8c7f249f0246b0d Binary files /dev/null and b/drip-android-observer-master/Android/app/src/main/res/drawable-hdpi/ic_person_pin_circle.png differ diff --git a/drip-android-observer-master/Android/app/src/main/res/drawable-hdpi/ic_pilot.png b/drip-android-observer-master/Android/app/src/main/res/drawable-hdpi/ic_pilot.png new file mode 100644 index 0000000000000000000000000000000000000000..0306a537fb10c62dc9c1f1759f126e0d54a696db Binary files /dev/null and b/drip-android-observer-master/Android/app/src/main/res/drawable-hdpi/ic_pilot.png differ diff --git a/drip-android-observer-master/Android/app/src/main/res/drawable-mdpi/ic_airplan.png b/drip-android-observer-master/Android/app/src/main/res/drawable-mdpi/ic_airplan.png new file mode 100644 index 0000000000000000000000000000000000000000..84be2e4a8f1c7285b65f98a65a67f5a988cd0e1c Binary files /dev/null and b/drip-android-observer-master/Android/app/src/main/res/drawable-mdpi/ic_airplan.png differ diff --git a/drip-android-observer-master/Android/app/src/main/res/drawable-mdpi/ic_person_pin_circle.png b/drip-android-observer-master/Android/app/src/main/res/drawable-mdpi/ic_person_pin_circle.png new file mode 100644 index 0000000000000000000000000000000000000000..ffc24f5b04e861d722182773baa356aa5459c483 Binary files /dev/null and b/drip-android-observer-master/Android/app/src/main/res/drawable-mdpi/ic_person_pin_circle.png differ diff --git a/drip-android-observer-master/Android/app/src/main/res/drawable-mdpi/ic_pilot.png b/drip-android-observer-master/Android/app/src/main/res/drawable-mdpi/ic_pilot.png new file mode 100644 index 0000000000000000000000000000000000000000..2d96bedc4fdba655f370e5bb1e0f0b48358e19f5 Binary files /dev/null and b/drip-android-observer-master/Android/app/src/main/res/drawable-mdpi/ic_pilot.png differ diff --git a/drip-android-observer-master/Android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/drip-android-observer-master/Android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000000000000000000000000000000000000..5a9ebbad202d80c70287b1e257210982edfc7a94 --- /dev/null +++ b/drip-android-observer-master/Android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,34 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:aapt="http://schemas.android.com/aapt" + android:width="108dp" + android:height="108dp" + android:viewportHeight="108" + android:viewportWidth="108"> + <path + android:fillType="evenOdd" + android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z" + android:strokeColor="#00000000" + android:strokeWidth="1"> + <aapt:attr name="android:fillColor"> + <gradient + android:endX="78.5885" + android:endY="90.9159" + android:startX="48.7653" + android:startY="61.0927" + android:type="linear"> + <item + android:color="#44000000" + android:offset="0.0" /> + <item + android:color="#00000000" + android:offset="1.0" /> + </gradient> + </aapt:attr> + </path> + <path + android:fillColor="#FFFFFF" + android:fillType="nonZero" + android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z" + android:strokeColor="#00000000" + android:strokeWidth="1" /> +</vector> diff --git a/drip-android-observer-master/Android/app/src/main/res/drawable-xhdpi/ic_airplan.png b/drip-android-observer-master/Android/app/src/main/res/drawable-xhdpi/ic_airplan.png new file mode 100644 index 0000000000000000000000000000000000000000..4e9a2c01092f01e96d51e71370c5335decb48d6e Binary files /dev/null and b/drip-android-observer-master/Android/app/src/main/res/drawable-xhdpi/ic_airplan.png differ diff --git a/drip-android-observer-master/Android/app/src/main/res/drawable-xhdpi/ic_person_pin_circle.png b/drip-android-observer-master/Android/app/src/main/res/drawable-xhdpi/ic_person_pin_circle.png new file mode 100644 index 0000000000000000000000000000000000000000..3e4ba853ed409fb672f29eee2d1f312086a64975 Binary files /dev/null and b/drip-android-observer-master/Android/app/src/main/res/drawable-xhdpi/ic_person_pin_circle.png differ diff --git a/drip-android-observer-master/Android/app/src/main/res/drawable-xhdpi/ic_pilot.png b/drip-android-observer-master/Android/app/src/main/res/drawable-xhdpi/ic_pilot.png new file mode 100644 index 0000000000000000000000000000000000000000..c161a1c31d63713cd5b145618406845b69413caa Binary files /dev/null and b/drip-android-observer-master/Android/app/src/main/res/drawable-xhdpi/ic_pilot.png differ diff --git a/drip-android-observer-master/Android/app/src/main/res/drawable-xxhdpi/ic_airplan.png b/drip-android-observer-master/Android/app/src/main/res/drawable-xxhdpi/ic_airplan.png new file mode 100644 index 0000000000000000000000000000000000000000..d013a34dda59abd49f580558b340e00985050ca2 Binary files /dev/null and b/drip-android-observer-master/Android/app/src/main/res/drawable-xxhdpi/ic_airplan.png differ diff --git a/drip-android-observer-master/Android/app/src/main/res/drawable-xxhdpi/ic_person_pin_circle.png b/drip-android-observer-master/Android/app/src/main/res/drawable-xxhdpi/ic_person_pin_circle.png new file mode 100644 index 0000000000000000000000000000000000000000..11f0f93158d3d1266d1f3e3db5a074befc3cef8f Binary files /dev/null and b/drip-android-observer-master/Android/app/src/main/res/drawable-xxhdpi/ic_person_pin_circle.png differ diff --git a/drip-android-observer-master/Android/app/src/main/res/drawable-xxhdpi/ic_pilot.png b/drip-android-observer-master/Android/app/src/main/res/drawable-xxhdpi/ic_pilot.png new file mode 100644 index 0000000000000000000000000000000000000000..e88b60f932952014979c05c12d098a0e2fe70d70 Binary files /dev/null and b/drip-android-observer-master/Android/app/src/main/res/drawable-xxhdpi/ic_pilot.png differ diff --git a/drip-android-observer-master/Android/app/src/main/res/drawable-xxxhdpi/ic_airplan.png b/drip-android-observer-master/Android/app/src/main/res/drawable-xxxhdpi/ic_airplan.png new file mode 100644 index 0000000000000000000000000000000000000000..1856f5b36057d3b8f4251ae724ea5777beb93eb2 Binary files /dev/null and b/drip-android-observer-master/Android/app/src/main/res/drawable-xxxhdpi/ic_airplan.png differ diff --git a/drip-android-observer-master/Android/app/src/main/res/drawable-xxxhdpi/ic_person_pin_circle.png b/drip-android-observer-master/Android/app/src/main/res/drawable-xxxhdpi/ic_person_pin_circle.png new file mode 100644 index 0000000000000000000000000000000000000000..c9cb2faed562ffd7159979de823149d5b563ee4a Binary files /dev/null and b/drip-android-observer-master/Android/app/src/main/res/drawable-xxxhdpi/ic_person_pin_circle.png differ diff --git a/drip-android-observer-master/Android/app/src/main/res/drawable-xxxhdpi/ic_pilot.png b/drip-android-observer-master/Android/app/src/main/res/drawable-xxxhdpi/ic_pilot.png new file mode 100644 index 0000000000000000000000000000000000000000..95a23425d61612e8397ab298a96d51c164c873e3 Binary files /dev/null and b/drip-android-observer-master/Android/app/src/main/res/drawable-xxxhdpi/ic_pilot.png differ diff --git a/drip-android-observer-master/Android/app/src/main/res/drawable/ic_autorenew_white_24dp.xml b/drip-android-observer-master/Android/app/src/main/res/drawable/ic_autorenew_white_24dp.xml new file mode 100644 index 0000000000000000000000000000000000000000..7c054745ea022faaac48b80fa338b821e3265478 --- /dev/null +++ b/drip-android-observer-master/Android/app/src/main/res/drawable/ic_autorenew_white_24dp.xml @@ -0,0 +1,5 @@ +<vector android:height="24dp" android:tint="#FFFFFF" + android:viewportHeight="24.0" android:viewportWidth="24.0" + android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> + <path android:fillColor="#FF000000" android:pathData="M12,6v3l4,-4 -4,-4v3c-4.42,0 -8,3.58 -8,8 0,1.57 0.46,3.03 1.24,4.26L6.7,14.8c-0.45,-0.83 -0.7,-1.79 -0.7,-2.8 0,-3.31 2.69,-6 6,-6zM18.76,7.74L17.3,9.2c0.44,0.84 0.7,1.79 0.7,2.8 0,3.31 -2.69,6 -6,6v-3l-4,4 4,4v-3c4.42,0 8,-3.58 8,-8 0,-1.57 -0.46,-3.03 -1.24,-4.26z"/> +</vector> diff --git a/drip-android-observer-master/Android/app/src/main/res/drawable/ic_launcher_background.xml b/drip-android-observer-master/Android/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000000000000000000000000000000000000..2408e30d1751ecd910eace02fa947f67c0bbd1dd --- /dev/null +++ b/drip-android-observer-master/Android/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,74 @@ +<?xml version="1.0" encoding="utf-8"?> +<vector + android:height="108dp" + android:width="108dp" + android:viewportHeight="108" + android:viewportWidth="108" + xmlns:android="http://schemas.android.com/apk/res/android"> + <path android:fillColor="#008577" + android:pathData="M0,0h108v108h-108z"/> + <path android:fillColor="#00000000" android:pathData="M9,0L9,108" + android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> + <path android:fillColor="#00000000" android:pathData="M19,0L19,108" + android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> + <path android:fillColor="#00000000" android:pathData="M29,0L29,108" + android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> + <path android:fillColor="#00000000" android:pathData="M39,0L39,108" + android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> + <path android:fillColor="#00000000" android:pathData="M49,0L49,108" + android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> + <path android:fillColor="#00000000" android:pathData="M59,0L59,108" + android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> + <path android:fillColor="#00000000" android:pathData="M69,0L69,108" + android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> + <path android:fillColor="#00000000" android:pathData="M79,0L79,108" + android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> + <path android:fillColor="#00000000" android:pathData="M89,0L89,108" + android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> + <path android:fillColor="#00000000" android:pathData="M99,0L99,108" + android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> + <path android:fillColor="#00000000" android:pathData="M0,9L108,9" + android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> + <path android:fillColor="#00000000" android:pathData="M0,19L108,19" + android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> + <path android:fillColor="#00000000" android:pathData="M0,29L108,29" + android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> + <path android:fillColor="#00000000" android:pathData="M0,39L108,39" + android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> + <path android:fillColor="#00000000" android:pathData="M0,49L108,49" + android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> + <path android:fillColor="#00000000" android:pathData="M0,59L108,59" + android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> + <path android:fillColor="#00000000" android:pathData="M0,69L108,69" + android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> + <path android:fillColor="#00000000" android:pathData="M0,79L108,79" + android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> + <path android:fillColor="#00000000" android:pathData="M0,89L108,89" + android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> + <path android:fillColor="#00000000" android:pathData="M0,99L108,99" + android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> + <path android:fillColor="#00000000" android:pathData="M19,29L89,29" + android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> + <path android:fillColor="#00000000" android:pathData="M19,39L89,39" + android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> + <path android:fillColor="#00000000" android:pathData="M19,49L89,49" + android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> + <path android:fillColor="#00000000" android:pathData="M19,59L89,59" + android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> + <path android:fillColor="#00000000" android:pathData="M19,69L89,69" + android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> + <path android:fillColor="#00000000" android:pathData="M19,79L89,79" + android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> + <path android:fillColor="#00000000" android:pathData="M29,19L29,89" + android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> + <path android:fillColor="#00000000" android:pathData="M39,19L39,89" + android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> + <path android:fillColor="#00000000" android:pathData="M49,19L49,89" + android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> + <path android:fillColor="#00000000" android:pathData="M59,19L59,89" + android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> + <path android:fillColor="#00000000" android:pathData="M69,19L69,89" + android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> + <path android:fillColor="#00000000" android:pathData="M79,19L79,89" + android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> +</vector> diff --git a/drip-android-observer-master/Android/app/src/main/res/drawable/ic_launcher_foreground.xml b/drip-android-observer-master/Android/app/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000000000000000000000000000000000000..f251488048a903a0e96d039717549b66cf827a71 --- /dev/null +++ b/drip-android-observer-master/Android/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,109 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="108dp" + android:height="108dp" + android:viewportWidth="309.375" + android:viewportHeight="309.375"> + <group android:translateX="56.848957" + android:translateY="1.890625"> + <path + android:pathData="M59.665,138.151A26.919,26.934 0,0 1,31.678 125.093,26.919 26.934,0 0,1 34.37,94.313 26.919,26.934 0,0 1,64.198 86.317,26.919 26.934,0 0,1 81.91,111.626" + android:strokeAlpha="1" + android:strokeLineJoin="miter" + android:strokeWidth="12" + android:fillColor="#00000000" + android:strokeColor="#e5e5e5" + android:fillType="nonZero" + android:fillAlpha="1" + android:strokeLineCap="butt"/> + <path + android:pathData="m82.142,194.43a26.919,26.934 90.003,0 1,-17.722 25.296,26.919 26.934,90.003 0,1 -29.845,-7.992 26.919,26.934 90.003,0 1,-2.693 -30.763,26.919 26.934,90.003 0,1 28.003,-13.051" + android:strokeAlpha="1" + android:strokeLineJoin="miter" + android:strokeWidth="12" + android:fillColor="#00000000" + android:strokeColor="#e5e5e5" + android:fillType="nonZero" + android:fillAlpha="1" + android:strokeLineCap="butt"/> + <path + android:pathData="m151.642,171.439a26.934,26.919 90.001,0 1,11.836 32.538,26.934 26.919,90.001 0,1 -29.97,17.313 26.934,26.919 90.001,0 1,-22.245 -26.525" + android:strokeAlpha="1" + android:strokeLineJoin="miter" + android:strokeWidth="12" + android:fillColor="#00000000" + android:strokeColor="#e5e5e5" + android:fillType="nonZero" + android:fillAlpha="1" + android:strokeLineCap="butt"/> + <path + android:pathData="m111.132,112.105a26.919,26.934 90.001,0 1,22.257 -26.51,26.919 26.934,90.001 0,1 29.987,17.303 26.919,26.934 90.001,0 1,-11.843 32.52" + android:strokeAlpha="1" + android:strokeLineJoin="miter" + android:strokeWidth="12" + android:fillColor="#00000000" + android:strokeColor="#e5e5e5" + android:fillType="nonZero" + android:fillAlpha="1" + android:strokeLineCap="butt"/> + <path + android:pathData="m110.855,124.565a32.366,29.242 0,0 1,28.068 14.589,32.366 29.242,0 0,1 0.032,29.256 32.366,29.242 0,0 1,-28.036 14.639l-0.006,-29.242z" + android:strokeAlpha="1" + android:strokeLineJoin="miter" + android:strokeWidth="12" + android:fillColor="#00000000" + android:strokeColor="#ffffff" + android:fillType="nonZero" + android:fillAlpha="1" + android:strokeLineCap="butt"/> + <path + android:pathData="M77.373,122.41h3.517v62.876h-3.517z" + android:strokeAlpha="1" + android:strokeLineJoin="miter" + android:strokeWidth="8.18205643" + android:fillColor="#00000000" + android:strokeColor="#ffffff" + android:fillType="nonZero" + android:fillAlpha="1" + android:strokeLineCap="butt"/> + <path + android:pathData="M67.205,125.519l0,-2.916l22.264,-0l0,2.916z" + android:strokeAlpha="1" + android:strokeLineJoin="miter" + android:strokeWidth="8.62253952" + android:fillColor="#00000000" + android:strokeColor="#ffffff" + android:fillType="nonZero" + android:fillAlpha="1" + android:strokeLineCap="butt"/> + <path + android:pathData="M67.886,185.228l0,-2.916l22.264,-0l0,2.916z" + android:strokeAlpha="1" + android:strokeLineJoin="miter" + android:strokeWidth="8.62266254" + android:fillColor="#00000000" + android:strokeColor="#ffffff" + android:fillType="nonZero" + android:fillAlpha="1" + android:strokeLineCap="butt"/> + <path + android:pathData="M102.127,127.342l0,-6.003l4.455,-0l0,6.003z" + android:strokeAlpha="1" + android:strokeLineJoin="miter" + android:strokeWidth="5.53488302" + android:fillColor="#00000000" + android:strokeColor="#ffffff" + android:fillType="nonZero" + android:fillAlpha="1" + android:strokeLineCap="butt"/> + <path + android:pathData="M101.905,186.212l0,-6.003l4.455,-0l0,6.003z" + android:strokeAlpha="1" + android:strokeLineJoin="miter" + android:strokeWidth="5.53488302" + android:fillColor="#00000000" + android:strokeColor="#ffffff" + android:fillType="nonZero" + android:fillAlpha="1" + android:strokeLineCap="butt"/> + </group> +</vector> diff --git a/drip-android-observer-master/Android/app/src/main/res/layout/activity_debug.xml b/drip-android-observer-master/Android/app/src/main/res/layout/activity_debug.xml new file mode 100644 index 0000000000000000000000000000000000000000..6045bcd595812f90b7f427f5d01973995f0af0aa --- /dev/null +++ b/drip-android-observer-master/Android/app/src/main/res/layout/activity_debug.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 Intel Corporation + + SPDX-License-Identifier: Apache-2.0 + +!--> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <fragment + android:id="@+id/mapView" + class="org.dripdronescanner.android.app.AircraftMapView" + + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1"/> + + <FrameLayout + android:id="@+id/holder" + android:layout_width="match_parent" + android:layout_height="175dp" + android:layout_gravity="bottom" + android:background="#eee" + android:minHeight="150dp" + android:elevation="2dp" + > + + </FrameLayout> +</LinearLayout> \ No newline at end of file diff --git a/drip-android-observer-master/Android/app/src/main/res/layout/activity_main.xml b/drip-android-observer-master/Android/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000000000000000000000000000000000000..4e0822c3789deb978e37722b46c5cc3abcd802b2 --- /dev/null +++ b/drip-android-observer-master/Android/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 Intel Corporation + + SPDX-License-Identifier: Apache-2.0 + +!--> +<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + tools:context=".MainActivity"> + + <com.google.android.gms.maps.MapView + android:id="@+id/map_view" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="0dp" + app:layout_constraintLeft_toLeftOf="parent" + app:layout_constraintRight_toRightOf="parent" + app:layout_constraintTop_toTopOf="parent"></com.google.android.gms.maps.MapView> + +</androidx.constraintlayout.widget.ConstraintLayout> diff --git a/drip-android-observer-master/Android/app/src/main/res/layout/activity_settings.xml b/drip-android-observer-master/Android/app/src/main/res/layout/activity_settings.xml new file mode 100644 index 0000000000000000000000000000000000000000..47eee5831b3e0afc5e9615ac5b2626b48b0e6874 --- /dev/null +++ b/drip-android-observer-master/Android/app/src/main/res/layout/activity_settings.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <TextView + android:id="@+id/textView" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="base url" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toStartOf="@+id/editBaseUrl" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + <EditText + android:id="@+id/editBaseUrl" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginEnd="60dp" + android:ems="10" + android:inputType="textUri" + android:minHeight="48dp" + android:text="https://192.168.0.1:8080" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintVertical_bias="0.499" /> + + <Button + android:id="@+id/saveBtn" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="66dp" + android:text="Save" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/editBaseUrl" /> +</androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file diff --git a/drip-android-observer-master/Android/app/src/main/res/layout/aircraft_details.xml b/drip-android-observer-master/Android/app/src/main/res/layout/aircraft_details.xml new file mode 100644 index 0000000000000000000000000000000000000000..7c8beca13913c8aeae22d87a8f36aa7dacad6a31 --- /dev/null +++ b/drip-android-observer-master/Android/app/src/main/res/layout/aircraft_details.xml @@ -0,0 +1,815 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 Intel Corporation + + SPDX-License-Identifier: Apache-2.0 + +!--> +<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <androidx.cardview.widget.CardView + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_margin="4dp" + app:cardElevation="1dp" + app:contentPadding="8dp"> + + <TextView + style="@style/Details.Heading" + android:layout_width="99dp" + android:layout_height="wrap_content" + android:text="Connection" /> + + <TextView + android:id="@+id/receiveTime" + android:layout_width="170dp" + android:layout_height="wrap_content" + android:layout_gravity="end" + android:text="–" /> + + <TableLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_marginLeft="10dp" + android:layout_marginTop="15dp" + android:stretchColumns="1,3"> + + <TableRow + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <TextView + style="@style/Details.Label" + android:text="RSSI" /> + + <TextView + android:id="@+id/conRssi" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="–" /> + + <TextView + style="@style/Details.Label" + android:text="MAC" /> + + <TextView + android:id="@+id/conMac" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="–" /> + </TableRow> + + <TableRow + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <TextView + style="@style/Details.Label" + android:text="Started" /> + + <TextView + android:id="@+id/conStarted" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="–" /> + + <TextView + style="@style/Details.Label" + android:text="Last seen" /> + + <TextView + android:id="@+id/conLastUpdate" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="–" /> + </TableRow> + + <TableRow + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <TextView + style="@style/Details.Label" + android:text="Msg ∆" /> + + <TextView + android:id="@+id/conMsgDelta" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="–" /> + + <TextView + style="@style/Details.Label" + android:text="Distance" /> + + <TextView + android:id="@+id/distance" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="–" /> + </TableRow> + </TableLayout> + </androidx.cardview.widget.CardView> + + <androidx.cardview.widget.CardView + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_margin="4dp" + app:cardElevation="1dp" + app:contentPadding="8dp"> + + <TextView + style="@style/Details.Heading" + android:layout_width="99dp" + android:layout_height="wrap_content" + android:text="Basic ID" /> + + <TextView + android:id="@+id/infoLastUpdate" + android:layout_width="78dp" + android:layout_height="wrap_content" + android:layout_gravity="end" + android:text="–" + android:textAlignment="viewEnd" /> + + <TableLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_marginTop="15dp" + android:stretchColumns="1,3" + android:layout_marginLeft="10dp"> + + <TableRow + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <TextView + style="@style/Details.Label" + android:text="Type" /> + + <TextView + android:id="@+id/infoType" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="–" /> + + <TextView + style="@style/Details.Label" + android:text="ID Type" /> + + <TextView + android:id="@+id/infoIdType" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="–" /> + </TableRow> + + <TableRow + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <TextView + style="@style/Details.Label" + android:text="UAS ID" /> + + <TextView + android:id="@+id/infoUasId" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_span="3" + android:text="–" /> + </TableRow> + + <TableRow + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <TextView + style="@style/Details.Label" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Validated: " /> + + <TextView + android:id="@+id/infoIDValidated" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="-" + android:layout_span="3" /> + + </TableRow> + + </TableLayout> + </androidx.cardview.widget.CardView> + + <Button + android:id="@+id/copyIDBtn" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="Copy ID to clipboard" /> + + <androidx.cardview.widget.CardView + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_margin="4dp" + app:cardElevation="1dp" + app:contentPadding="8dp"> + + <TextView + style="@style/Details.Heading" + android:text="Location" /> + + <TextView + android:id="@+id/posLastUpdate" + android:layout_width="78dp" + android:layout_height="wrap_content" + android:layout_gravity="end" + android:text="–" + android:textAlignment="viewEnd" /> + + <TableLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_marginLeft="10dp" + android:layout_marginTop="15dp" + android:stretchColumns="1,3"> + + <TableRow + android:layout_width="match_parent" + android:layout_height="wrap_content"> + + <TextView + style="@style/Details.Label" + android:text="Latitude" /> + + <TextView + android:id="@+id/lat" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="–" /> + + <TextView + style="@style/Details.Label" + android:text="Longitude" /> + + <TextView + android:id="@+id/lon" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="–" /> + </TableRow> + + <TableRow + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <TextView + style="@style/Details.Label" + android:text="Altitude Press." /> + + <TextView + android:id="@+id/altitudePressure" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="–" /> + + <TextView + style="@style/Details.Label" + android:text="Alt. Geod." /> + + <TextView + android:id="@+id/altitudeGeodetic" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="–" /> + </TableRow> + + <TableRow + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <TextView + style="@style/Details.Label" + android:text="Direction" /> + + <TextView + android:id="@+id/direction" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="–" /> + + <TextView + style="@style/Details.Label" + android:text="Status" /> + + <TextView + android:id="@+id/status" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="–" /> + </TableRow> + + <TableRow + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <TextView + style="@style/Details.Label" + android:text="Hori. Speed" /> + + <TextView + android:id="@+id/horiSpeed" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="–" /> + + <TextView + style="@style/Details.Label" + android:text="Vert. Speed" /> + + <TextView + android:id="@+id/vertSpeed" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="–" /> + </TableRow> + + <TableRow + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <TextView + style="@style/Details.Label" + android:text="Height" /> + + <TextView + android:id="@+id/height" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="–" /> + + <TextView + style="@style/Details.Label" + android:text="Height Over" /> + + <TextView + android:id="@+id/heightType" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="–" /> + </TableRow> + + <TableRow + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <TextView + style="@style/Details.Label" + android:text="Hori. Accuracy" /> + + <TextView + android:id="@+id/horizontalAccuracy" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="–" /> + + <TextView + style="@style/Details.Label" + android:text="Vert. Acc." /> + + <TextView + android:id="@+id/verticalAccuracy" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="–" /> + </TableRow> + + <TableRow + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <TextView + style="@style/Details.Label" + android:text="Baro Acc." /> + + <TextView + android:id="@+id/baroAccuracy" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="–" /> + + <TextView + style="@style/Details.Label" + android:text="Speed Acc." /> + + <TextView + android:id="@+id/speedAccuracy" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="–" /> + </TableRow> + + <TableRow + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <TextView + style="@style/Details.Label" + android:text="Time Acc." /> + + <TextView + android:id="@+id/timeAccuracy" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="–" /> + + <TextView + style="@style/Details.Label" + android:text="Timestamp" /> + + <TextView + android:id="@+id/timestamp" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="–" /> + </TableRow> + + </TableLayout> + </androidx.cardview.widget.CardView> + + <androidx.cardview.widget.CardView + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_margin="4dp" + app:cardElevation="1dp" + app:contentPadding="8dp"> + + <TextView + style="@style/Details.Heading" + android:layout_width="99dp" + android:layout_height="wrap_content" + android:text="Self ID" /> + + <TextView + android:id="@+id/selfIdLastUpdate" + android:layout_width="78dp" + android:layout_height="wrap_content" + android:layout_gravity="end" + android:text="–" + android:textAlignment="viewEnd" /> + + <TableLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_marginTop="15dp" + android:stretchColumns="1,3" + android:layout_marginLeft="10dp"> + + <TableRow + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <TextView + style="@style/Details.Label" + android:text="Operation" /> + + <TextView + android:id="@+id/selfIdDescription" + android:layout_width="162dp" + android:layout_height="wrap_content" + android:text="–" /> + + <TextView + style="@style/Details.Label" + android:text="Type" /> + + <TextView + android:id="@+id/selfIdType" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="–" /> + </TableRow> + + </TableLayout> + </androidx.cardview.widget.CardView> + + <androidx.cardview.widget.CardView + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_margin="4dp" + app:cardElevation="1dp" + app:contentPadding="8dp"> + + <TextView + style="@style/Details.Heading" + android:layout_width="130dp" + android:layout_height="wrap_content" + android:text="System/Operator" /> + + <TextView + android:id="@+id/systemLastUpdate" + android:layout_width="78dp" + android:layout_height="wrap_content" + android:layout_gravity="end" + android:text="–" + android:textAlignment="viewEnd" /> + + <TableLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_marginTop="15dp" + android:stretchColumns="1,3" + android:layout_marginLeft="10dp"> + + <TableRow + android:layout_width="match_parent" + android:layout_height="wrap_content"> + + <TextView + style="@style/Details.Label" + android:text="Latitude" /> + + <TextView + android:id="@+id/systemLatitude" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="–" /> + + <TextView + style="@style/Details.Label" + android:text="Longitude" /> + + <TextView + android:id="@+id/systemLongitude" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="–" /> + </TableRow> + + <TableRow + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <TextView + style="@style/Details.Label" + android:text="Location" /> + + <TextView + android:id="@+id/operatorLocationType" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="–" /> + + <TextView + style="@style/Details.Label" + android:text="Classification" /> + + <TextView + android:id="@+id/classificationType" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="–" /> + </TableRow> + + <TableRow + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <TextView + style="@style/Details.Label" + android:text="Category" /> + + <TextView + android:id="@+id/category" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="–" /> + + <TextView + style="@style/Details.Label" + android:text="Class" /> + + <TextView + android:id="@+id/classValue" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="–" /> + </TableRow> + + <TableRow + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <TextView + style="@style/Details.Label" + android:text="Area Count" /> + + <TextView + android:id="@+id/systemAreaCount" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="–" /> + + <TextView + style="@style/Details.Label" + android:text="Area Radius" /> + + <TextView + android:id="@+id/systemAreaRadius" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="–" /> + </TableRow> + + <TableRow + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <TextView + style="@style/Details.Label" + android:text="Area Ceiling" /> + + <TextView + android:id="@+id/systemAreaCeiling" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="–" /> + + <TextView + style="@style/Details.Label" + android:text="Area Floor" /> + + <TextView + android:id="@+id/systemAreaFloor" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="–" /> + </TableRow> + + </TableLayout> + </androidx.cardview.widget.CardView> + + <androidx.cardview.widget.CardView + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_margin="4dp" + app:cardElevation="1dp" + app:contentPadding="8dp"> + + <TextView + style="@style/Details.Heading" + android:layout_width="99dp" + android:layout_height="wrap_content" + android:text="Operator ID" /> + + <TextView + android:id="@+id/operatorIdLastUpdate" + android:layout_width="78dp" + android:layout_height="wrap_content" + android:layout_gravity="end" + android:text="–" + android:textAlignment="viewEnd" /> + + <TableLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_marginTop="15dp" + android:stretchColumns="1,3" + android:layout_marginLeft="10dp"> + + <TableRow + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <TextView + style="@style/Details.Label" + android:text="Operator ID" /> + + <TextView + android:id="@+id/operatorId" + android:layout_width="162dp" + android:layout_height="wrap_content" + android:text="–" /> + + <TextView + style="@style/Details.Label" + android:text="Type" /> + + <TextView + android:id="@+id/operatorIdType" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="–" /> + </TableRow> + + </TableLayout> + </androidx.cardview.widget.CardView> + + <androidx.cardview.widget.CardView + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_margin="4dp" + app:cardElevation="1dp" + app:contentPadding="8dp"> + + <TextView + style="@style/Details.Heading" + android:layout_width="99dp" + android:layout_height="wrap_content" + android:text="Authentication" /> + + <TextView + android:id="@+id/authLastUpdate" + android:layout_width="78dp" + android:layout_height="wrap_content" + android:layout_gravity="end" + android:text="–" + android:textAlignment="viewEnd" /> + + <TableLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_marginTop="15dp" + android:stretchColumns="1,3" + android:layout_marginLeft="10dp"> + + <TableRow + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <TextView + style="@style/Details.Label" + android:text="Type" /> + + <TextView + android:id="@+id/authType" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="–" /> + + <TextView + style="@style/Details.Label" + android:text="Length" /> + + <TextView + android:id="@+id/authLength" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="–" /> + </TableRow> + + <TableRow + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <TextView + style="@style/Details.Label" + android:text="Timestamp" /> + + <TextView + android:id="@+id/authTimestamp" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_span="3" + android:text="–" /> + </TableRow> + + <TableRow + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <TextView + style="@style/Details.Label" + android:text="Authentication" /> + + <TextView + android:id="@+id/authData" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="–" + android:layout_span="3" /> + </TableRow> + + </TableLayout> + + </androidx.cardview.widget.CardView> + + <Button + android:id="@+id/manAuthBtn" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="Manual public key entry" + android:visibility="visible" + tools:visibility="visible" /> + + </LinearLayout> +</ScrollView> diff --git a/drip-android-observer-master/Android/app/src/main/res/layout/aircraft_list.xml b/drip-android-observer-master/Android/app/src/main/res/layout/aircraft_list.xml new file mode 100644 index 0000000000000000000000000000000000000000..aa2b9b9687208ac07a2754fec7be4c26b609e84e --- /dev/null +++ b/drip-android-observer-master/Android/app/src/main/res/layout/aircraft_list.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 Intel Corporation + + SPDX-License-Identifier: Apache-2.0 + +!--> +<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:id="@+id/device_list"> +</androidx.recyclerview.widget.RecyclerView> \ No newline at end of file diff --git a/drip-android-observer-master/Android/app/src/main/res/layout/listitem_aircraft.xml b/drip-android-observer-master/Android/app/src/main/res/layout/listitem_aircraft.xml new file mode 100644 index 0000000000000000000000000000000000000000..1d20cbb59251b14dd025a62b884cf0c30384a9b6 --- /dev/null +++ b/drip-android-observer-master/Android/app/src/main/res/layout/listitem_aircraft.xml @@ -0,0 +1,63 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 Intel Corporation + + SPDX-License-Identifier: Apache-2.0 + +!--> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:padding="5dp" + android:orientation="horizontal"> + + <ImageView + xmlns:app="http://schemas.android.com/apk/res-auto" + android:id="@+id/drone_icon" + android:layout_width="32dp" + android:layout_height="32dp" + android:src="@mipmap/ic_plane_icon" + android:layout_gravity="center" + android:contentDescription="@string/drone_icon_content_description"/> + + <LinearLayout + android:layout_width="0dp" + android:layout_weight="1" + android:layout_margin="4dp" + android:layout_height="match_parent" + android:orientation="vertical"> + + <TextView + tools:text="text" + android:id="@+id/aircraftName" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textAppearance="?attr/textAppearanceListItem" + android:textSize="16dp" /> + + <TextView + tools:text="small" + android:id="@+id/aircraftFun" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textSize="12dp" /> + + + </LinearLayout> + + <TextView + android:id="@+id/last_seen" + android:layout_width="50dp" + android:layout_height="match_parent" + android:gravity="center" + android:textSize="10dp" /> + + <Button + android:id="@+id/modButton" + style="@style/Widget.AppCompat.Button.Borderless" + android:layout_width="70dp" + android:layout_height="wrap_content" + android:text="info" + /> +</LinearLayout> diff --git a/drip-android-observer-master/Android/app/src/main/res/layout/listitem_scanresult.xml b/drip-android-observer-master/Android/app/src/main/res/layout/listitem_scanresult.xml new file mode 100644 index 0000000000000000000000000000000000000000..9f8bb5c1ae102212856bbfe6e220b5fd137bd520 --- /dev/null +++ b/drip-android-observer-master/Android/app/src/main/res/layout/listitem_scanresult.xml @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 Intel Corporation + + SPDX-License-Identifier: Apache-2.0 + +!--> +<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/linearLayout" + android:layout_width="match_parent" + android:layout_height="wrap_content"> + + <TextView + android:id="@+id/device_name" + android:layout_width="184dp" + android:layout_height="wrap_content" + android:textSize="16dp" + app:layout_constraintBottom_toTopOf="@+id/device_address" + app:layout_constraintEnd_toEndOf="parent" + tools:text="test" /> + + <TextView + android:id="@+id/device_address" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:textSize="12dp" + app:layout_constraintBottom_toTopOf="@+id/last_seen" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="@+id/device_name" /> + + <TextView + android:id="@+id/last_seen" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:textSize="12dp" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="@+id/device_address" /> +</androidx.constraintlayout.widget.ConstraintLayout> diff --git a/drip-android-observer-master/Android/app/src/main/res/menu/main_menu.xml b/drip-android-observer-master/Android/app/src/main/res/menu/main_menu.xml new file mode 100644 index 0000000000000000000000000000000000000000..f1dee3bbd61bc03939e629d8c53289590398d17d --- /dev/null +++ b/drip-android-observer-master/Android/app/src/main/res/menu/main_menu.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 Intel Corporation + + SPDX-License-Identifier: Apache-2.0 + +!--> +<menu xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto"> + <item + android:id="@+id/clear" + android:title="Clear" + app:showAsAction="ifRoom" /> + <item + android:id="@+id/menu_log" + android:checkable="true" + android:title="Log enabled" + app:showAsAction="never" /> + <item + android:id="@+id/log_location" + android:title="Show log location" /> + <item + android:id="@+id/coded_phy" + android:title="@string/coded_phy_not_supported" /> + <item + android:id="@+id/extended_advertising" + android:title="@string/ea_not_supported" /> + <item + android:id="@+id/wifi_nan" + android:title="@string/nan_not_supported" /> + <item + android:id="@+id/wifi_beacon_scan" + android:title="@string/wifi_beacon_scan_not_supported" /> + <item + android:id="@+id/settings" + android:title="@string/settings" /> + <item + android:id="@+id/version" + android:title="@string/app_version" /> +</menu> \ No newline at end of file diff --git a/drip-android-observer-master/Android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/drip-android-observer-master/Android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000000000000000000000000000000000000..036d09bc5fd523323794379703c4a111d1e28a04 --- /dev/null +++ b/drip-android-observer-master/Android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> + <background android:drawable="@color/ic_launcher_background"/> + <foreground android:drawable="@mipmap/ic_launcher_foreground"/> +</adaptive-icon> \ No newline at end of file diff --git a/drip-android-observer-master/Android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/drip-android-observer-master/Android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000000000000000000000000000000000000..036d09bc5fd523323794379703c4a111d1e28a04 --- /dev/null +++ b/drip-android-observer-master/Android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> + <background android:drawable="@color/ic_launcher_background"/> + <foreground android:drawable="@mipmap/ic_launcher_foreground"/> +</adaptive-icon> \ No newline at end of file diff --git a/drip-android-observer-master/Android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/drip-android-observer-master/Android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..218f92dcd55acf4f389a47a502d50d182e02aff6 Binary files /dev/null and b/drip-android-observer-master/Android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/drip-android-observer-master/Android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/drip-android-observer-master/Android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..21ac8aaa1424c5810e81408ebd4595d7b797f92b Binary files /dev/null and b/drip-android-observer-master/Android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png differ diff --git a/drip-android-observer-master/Android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/drip-android-observer-master/Android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..2e03757215a4d6bf85a7fee959c55d1fc4a57d35 Binary files /dev/null and b/drip-android-observer-master/Android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/drip-android-observer-master/Android/app/src/main/res/mipmap-hdpi/ic_plane_icon.png b/drip-android-observer-master/Android/app/src/main/res/mipmap-hdpi/ic_plane_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..b2be26bc5cec563fa5bc6356cc7b1028d755b657 Binary files /dev/null and b/drip-android-observer-master/Android/app/src/main/res/mipmap-hdpi/ic_plane_icon.png differ diff --git a/drip-android-observer-master/Android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/drip-android-observer-master/Android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..acde64b59f808a245551fc01a806d88af3ecd551 Binary files /dev/null and b/drip-android-observer-master/Android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/drip-android-observer-master/Android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/drip-android-observer-master/Android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..45332e1499c3b50b99e0e16fdaf6f39cf136a2c2 Binary files /dev/null and b/drip-android-observer-master/Android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png differ diff --git a/drip-android-observer-master/Android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/drip-android-observer-master/Android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..a7a3c6270094ec6e157e1ea6ee241e7b6e96ea8a Binary files /dev/null and b/drip-android-observer-master/Android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/drip-android-observer-master/Android/app/src/main/res/mipmap-mdpi/ic_plane_icon.png b/drip-android-observer-master/Android/app/src/main/res/mipmap-mdpi/ic_plane_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..91c7fd70f6ad4c932725b7df664ae08526491b15 Binary files /dev/null and b/drip-android-observer-master/Android/app/src/main/res/mipmap-mdpi/ic_plane_icon.png differ diff --git a/drip-android-observer-master/Android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/drip-android-observer-master/Android/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..0502c2efdd083a90029ccad2a7a43a4fc7595fc1 Binary files /dev/null and b/drip-android-observer-master/Android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/drip-android-observer-master/Android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/drip-android-observer-master/Android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..7744e4304fb08494455d73d7689191673fceddad Binary files /dev/null and b/drip-android-observer-master/Android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png differ diff --git a/drip-android-observer-master/Android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/drip-android-observer-master/Android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..a701a478c960cece85274df942c90a40a3e256de Binary files /dev/null and b/drip-android-observer-master/Android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/drip-android-observer-master/Android/app/src/main/res/mipmap-xhdpi/ic_plane_icon.png b/drip-android-observer-master/Android/app/src/main/res/mipmap-xhdpi/ic_plane_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..857a07ffc4b4f8d143c0c56dfb7ac2defcfe1bbc Binary files /dev/null and b/drip-android-observer-master/Android/app/src/main/res/mipmap-xhdpi/ic_plane_icon.png differ diff --git a/drip-android-observer-master/Android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/drip-android-observer-master/Android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..79d34e19492c4df19faf55204a1cd068be7c1781 Binary files /dev/null and b/drip-android-observer-master/Android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/drip-android-observer-master/Android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/drip-android-observer-master/Android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..0bbfcce51a3910da61db8db754d7df525cd64ac6 Binary files /dev/null and b/drip-android-observer-master/Android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png differ diff --git a/drip-android-observer-master/Android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/drip-android-observer-master/Android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..9f3869a1c987bc2dc20a58f1a5468b7611fb6c16 Binary files /dev/null and b/drip-android-observer-master/Android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/drip-android-observer-master/Android/app/src/main/res/mipmap-xxhdpi/ic_plane_icon.png b/drip-android-observer-master/Android/app/src/main/res/mipmap-xxhdpi/ic_plane_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..87a5efca39290ae27bb5bed43f7320dbcd8dcd50 Binary files /dev/null and b/drip-android-observer-master/Android/app/src/main/res/mipmap-xxhdpi/ic_plane_icon.png differ diff --git a/drip-android-observer-master/Android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/drip-android-observer-master/Android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..6d04e4c90d675a717dfd49d9626552340389cdab Binary files /dev/null and b/drip-android-observer-master/Android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/drip-android-observer-master/Android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/drip-android-observer-master/Android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..609daf366330ba9e663a380c5c13cb89e82f4f3e Binary files /dev/null and b/drip-android-observer-master/Android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png differ diff --git a/drip-android-observer-master/Android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/drip-android-observer-master/Android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..73444aba87f055c52613fee86865c33df018bb08 Binary files /dev/null and b/drip-android-observer-master/Android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/drip-android-observer-master/Android/app/src/main/res/mipmap-xxxhdpi/ic_plane_icon.png b/drip-android-observer-master/Android/app/src/main/res/mipmap-xxxhdpi/ic_plane_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..e2ee573523f995927286ab5a07b95ca23ea2ba82 Binary files /dev/null and b/drip-android-observer-master/Android/app/src/main/res/mipmap-xxxhdpi/ic_plane_icon.png differ diff --git a/drip-android-observer-master/Android/app/src/main/res/raw/eloboma_chain_crt.crt b/drip-android-observer-master/Android/app/src/main/res/raw/eloboma_chain_crt.crt new file mode 100644 index 0000000000000000000000000000000000000000..ebbff419f2fd2cff9693b96b8fee380871ec93ad Binary files /dev/null and b/drip-android-observer-master/Android/app/src/main/res/raw/eloboma_chain_crt.crt differ diff --git a/drip-android-observer-master/Android/app/src/main/res/raw/eloboma_crt.crt b/drip-android-observer-master/Android/app/src/main/res/raw/eloboma_crt.crt new file mode 100644 index 0000000000000000000000000000000000000000..ebbff419f2fd2cff9693b96b8fee380871ec93ad Binary files /dev/null and b/drip-android-observer-master/Android/app/src/main/res/raw/eloboma_crt.crt differ diff --git a/drip-android-observer-master/Android/app/src/main/res/values/attrs.xml b/drip-android-observer-master/Android/app/src/main/res/values/attrs.xml new file mode 100644 index 0000000000000000000000000000000000000000..542d9aca00f89c8cd597c18c5fd2e3ebe3b34927 --- /dev/null +++ b/drip-android-observer-master/Android/app/src/main/res/values/attrs.xml @@ -0,0 +1,18 @@ +<!-- + Copyright (C) 2019 Intel Corporation + + SPDX-License-Identifier: Apache-2.0 + +!--> +<resources> + + <!-- Declare custom theme attributes that allow changing which styles are + used for button bars depending on the API level. + ?android:attr/buttonBarStyle is new as of API 11 so this is + necessary to support previous API levels. --> + <declare-styleable name="ButtonBarContainerTheme"> + <attr name="metaButtonBarStyle" format="reference" /> + <attr name="metaButtonBarButtonStyle" format="reference" /> + </declare-styleable> + +</resources> diff --git a/drip-android-observer-master/Android/app/src/main/res/values/colors.xml b/drip-android-observer-master/Android/app/src/main/res/values/colors.xml new file mode 100644 index 0000000000000000000000000000000000000000..148b277d757646dfd35b93bd358c5d08b615968c --- /dev/null +++ b/drip-android-observer-master/Android/app/src/main/res/values/colors.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 Intel Corporation + + SPDX-License-Identifier: Apache-2.0 + +!--> +<resources> + <color name="colorPrimary">#3F51B5</color> + <color name="colorPrimaryDark">#303F9F</color> + <color name="colorAccent">#FF4081</color> + + <color name="black_overlay">#66000000</color> +</resources> diff --git a/drip-android-observer-master/Android/app/src/main/res/values/ic_launcher_background.xml b/drip-android-observer-master/Android/app/src/main/res/values/ic_launcher_background.xml new file mode 100644 index 0000000000000000000000000000000000000000..c5d5899fdf0a1b144bf341b29e0c66ba50bbcedd --- /dev/null +++ b/drip-android-observer-master/Android/app/src/main/res/values/ic_launcher_background.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <color name="ic_launcher_background">#FFFFFF</color> +</resources> \ No newline at end of file diff --git a/drip-android-observer-master/Android/app/src/main/res/values/strings.xml b/drip-android-observer-master/Android/app/src/main/res/values/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..039f682d76d7a0538b9b0b3c142655180841d28c --- /dev/null +++ b/drip-android-observer-master/Android/app/src/main/res/values/strings.xml @@ -0,0 +1,51 @@ +<!-- + Copyright (C) 2019 Intel Corporation + + SPDX-License-Identifier: Apache-2.0 + +!--> +<resources> + <string name="app_name">DRIP Drone Scanner</string> + <string name="bt_ads_not_supported">Bluetooth Advertisements are not supported on this device.</string> + <string name="bt_not_supported">Bluetooth is not supported on this device.</string> + <string name="bt_not_enabled_leaving">This application will not work properly without enabling Bluetooth.</string> + <string name="wifi_not_enabled_leaving">This application will not work properly without enabling WiFi.</string> + <string name="empty_list">No devices found - refresh to try again.</string> + <string name="seconds">seconds.</string> + <string name="scan_start_toast">Scanning Drone advertising</string> + <string name="already_scanning">Scanning already started.</string> + <string name="no_name">(no name)</string> + <string name="start_error_unknown">unknown error</string> + <string name="advertising_timedout">Advertising stopped due to timeout.</string> + <string name="last_seen">Last Seen:</string> + <string name="just_now">just now</string> + <string name="minute_ago">minute ago</string> + <string name="hour_ago">hour ago</string> + <string name="seconds_ago">seconds ago</string> + <string name="minutes_ago">minutes ago</string> + <string name="hours_ago">hours ago</string> + <string name="info">info</string> + + <string name="coded_phy_not_supported">Coded Phy not supported</string> + <string name="coded_phy_supported">Coded Phy supported</string> + <string name="ea_not_supported">Extended Advertising not supported</string> + <string name="ea_supported">Extended Advertising supported</string> + <string name="nan_not_supported">WiFi NaN not supported</string> + <string name="nan_supported">WiFi NaN supported</string> + + <string name="map_not_ready">Map is not ready yet</string> + <string name="wifi_beacon_scan_not_supported">WiFi Beacon Scan not supported</string> + <string name="wifi_beacon_scan_supported">WiFi Beacon Scan supported</string> + + <!-- Permissions --> + <string name="permission_rationale_location">Access to the location service is required to demonstrate the \'my location\' feature, which shows your current location on the map.</string> + <string name="location_permission_denied">This sample requires location permission to enable the \'my location\' layer. Please try again and grant access to use the location.\nIf the permission has been permanently denied, it can be enabled from the System Settings > Apps > \'Google Maps API Demos\'.</string> + <string name="permission_required_toast">Location permission is required for Bluetooth advertising scanning.</string> + + <string name="drone_icon_content_description">Drone icon</string> + + <string name="accept_button">Accept</string> + <string name="accept_content">For Intel internal usage only\n\nThis application uses a Google Maps key paid for by Intel\n\nDO NOT DISTRIBUTE\n\nAccept</string> + <string name="settings">Settings</string> + +</resources> diff --git a/drip-android-observer-master/Android/app/src/main/res/values/styles.xml b/drip-android-observer-master/Android/app/src/main/res/values/styles.xml new file mode 100644 index 0000000000000000000000000000000000000000..0e0a7c47aed8af5c8c23a6a9f131af68152dcebc --- /dev/null +++ b/drip-android-observer-master/Android/app/src/main/res/values/styles.xml @@ -0,0 +1,44 @@ +<!-- + Copyright (C) 2019 Intel Corporation + + SPDX-License-Identifier: Apache-2.0 + +!--> +<resources> + + <!-- Base application theme. --> + <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> + <!-- Customize your theme here. --> + <item name="colorPrimary">@color/colorPrimary</item> + <item name="colorPrimaryDark">@color/colorPrimaryDark</item> + <item name="colorAccent">@color/colorAccent</item> + </style> + + <style name="Details.Label" parent="TextAppearance.AppCompat"> + <item name="android:textColor">#666</item> + <item name="android:textSize">10sp</item> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:layout_marginRight">5dp</item> + <item name="android:layout_gravity">bottom|right</item> + </style> + + <style name="Details.Heading" parent="TextAppearance.AppCompat.Small"> + <item name="android:textColor">@color/colorPrimary</item> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">wrap_content</item> + </style> + + <style name="FullscreenTheme" parent="AppTheme"> + <item name="android:actionBarStyle">@style/FullscreenActionBarStyle</item> + <item name="android:windowActionBarOverlay">true</item> + <item name="android:windowBackground">@null</item> + <item name="metaButtonBarStyle">?android:attr/buttonBarStyle</item> + <item name="metaButtonBarButtonStyle">?android:attr/buttonBarButtonStyle</item> + </style> + + <style name="FullscreenActionBarStyle" parent="Widget.AppCompat.ActionBar"> + <item name="android:background">@color/black_overlay</item> + </style> + +</resources> diff --git a/drip-android-observer-master/Android/app/src/main/res/xml/network_security_config.xml b/drip-android-observer-master/Android/app/src/main/res/xml/network_security_config.xml new file mode 100644 index 0000000000000000000000000000000000000000..886cc17d6c8f3d32cf6d3bb829290b227a7636b7 --- /dev/null +++ b/drip-android-observer-master/Android/app/src/main/res/xml/network_security_config.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<network-security-config> + <base-config> + <trust-anchors> + <certificates src="user"/> + <certificates src="@raw/eloboma_crt"/> + <certificates src="@raw/eloboma_chain_crt"/> + <certificates src="system"/> + </trust-anchors> + </base-config> +</network-security-config> \ No newline at end of file diff --git a/drip-android-observer-master/Android/build.gradle b/drip-android-observer-master/Android/build.gradle new file mode 100644 index 0000000000000000000000000000000000000000..1ab22bcc62ad7c267f235fe154862a9cefdcf0fa --- /dev/null +++ b/drip-android-observer-master/Android/build.gradle @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2019 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + * + */ +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + + repositories { + google() + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:4.0.1' + + + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +allprojects { + repositories { + google() + jcenter() + maven { url 'https://jitpack.io' } + } +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/drip-android-observer-master/Android/gradle.properties b/drip-android-observer-master/Android/gradle.properties new file mode 100644 index 0000000000000000000000000000000000000000..8de505811b131e6031978d1e92fe4100da9a293a --- /dev/null +++ b/drip-android-observer-master/Android/gradle.properties @@ -0,0 +1,15 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +android.enableJetifier=true +android.useAndroidX=true +org.gradle.jvmargs=-Xmx1536m +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true diff --git a/drip-android-observer-master/Android/gradle/wrapper/gradle-wrapper.jar b/drip-android-observer-master/Android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..7a3265ee94c0ab25cf079ac8ccdf87f41d455d42 Binary files /dev/null and b/drip-android-observer-master/Android/gradle/wrapper/gradle-wrapper.jar differ diff --git a/drip-android-observer-master/Android/gradle/wrapper/gradle-wrapper.properties b/drip-android-observer-master/Android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000000000000000000000000000000000000..80b4beba49561d523aa5877e36e15cd15e4d8750 --- /dev/null +++ b/drip-android-observer-master/Android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Fri Mar 12 14:23:18 EET 2021 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip diff --git a/drip-android-observer-master/Android/gradlew b/drip-android-observer-master/Android/gradlew new file mode 100644 index 0000000000000000000000000000000000000000..cccdd3d517fc5249beaefa600691cf150f2fa3e6 --- /dev/null +++ b/drip-android-observer-master/Android/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/drip-android-observer-master/Android/gradlew.bat b/drip-android-observer-master/Android/gradlew.bat new file mode 100644 index 0000000000000000000000000000000000000000..f9553162f122c71b34635112e717c3e733b5b212 --- /dev/null +++ b/drip-android-observer-master/Android/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/drip-android-observer-master/Android/settings.gradle b/drip-android-observer-master/Android/settings.gradle new file mode 100644 index 0000000000000000000000000000000000000000..e7b4def49cb53d9aa04228dd3edb14c9e635e003 --- /dev/null +++ b/drip-android-observer-master/Android/settings.gradle @@ -0,0 +1 @@ +include ':app' diff --git a/drip-android-observer-master/Generating_class_diagram.txt b/drip-android-observer-master/Generating_class_diagram.txt new file mode 100644 index 0000000000000000000000000000000000000000..d209fa6460e538a0707ed0826322e2e8cec6b9fd --- /dev/null +++ b/drip-android-observer-master/Generating_class_diagram.txt @@ -0,0 +1,8 @@ +In Android Studio, open settings (CTRL + ALT + S). Select plugins. Find Code Iris and install this. Restart Android Studio +Android Studio no longer starts up +Download the zip file from https://plugins.jetbrains.com/plugin/7324-code-iris/versions +Us Beyond Compare or similar on the zip file against “.AndroidStudio3.5\config\plugins\CodeIris-Idea\lib” +Copy over all jars from the zip file except for the CodeIris-Idea.jar. Leave that from what Android Studio installed. Delete additional jars from the Android Studio plugin folder +Android Studio should start up +Right click at the top of the project directory structure and select “Create Graph” +The graph is now available on the right side of the IDE (Hidden. You need to expand it) diff --git a/drip-android-observer-master/Legal_notices/Attributions.txt b/drip-android-observer-master/Legal_notices/Attributions.txt new file mode 100644 index 0000000000000000000000000000000000000000..c88e4a7f78893d589707b9bd60a4287e302ca641 --- /dev/null +++ b/drip-android-observer-master/Legal_notices/Attributions.txt @@ -0,0 +1,13 @@ +Intel Open Drone ID example receiver application for Android +Third Party Components Attributions + + +This attributions file specifies all 3rd party SW components used for the Intel Open Drone ID example receiver application for Android, and the inbound license for each of these 3rd party components. + +*********** +FastAdapter +*********** + +https://github.com/mikepenz/FastAdapter + +Licensed under the Apache 2.0 license. See the "LICENSE-2.0.txt" file. diff --git a/drip-android-observer-master/Legal_notices/LICENSE-2.0.txt b/drip-android-observer-master/Legal_notices/LICENSE-2.0.txt new file mode 100644 index 0000000000000000000000000000000000000000..d645695673349e3947e8e5ae42332d0ac3164cd7 --- /dev/null +++ b/drip-android-observer-master/Legal_notices/LICENSE-2.0.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/drip-android-observer-master/Legal_notices/Readme.txt b/drip-android-observer-master/Legal_notices/Readme.txt new file mode 100644 index 0000000000000000000000000000000000000000..be7fbc6e4e44ad179387f35d5ce31e9bd4d3a91a --- /dev/null +++ b/drip-android-observer-master/Legal_notices/Readme.txt @@ -0,0 +1,14 @@ +Copyright 2019 Intel Corporation. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and +limitations under the License. diff --git a/drip-android-observer-master/OpenDroneID.jpg b/drip-android-observer-master/OpenDroneID.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e3dd9c72a731de19483c9155db7fab2bb2078165 Binary files /dev/null and b/drip-android-observer-master/OpenDroneID.jpg differ diff --git a/drip-android-observer-master/README.md b/drip-android-observer-master/README.md new file mode 100644 index 0000000000000000000000000000000000000000..63da39e361ab7ef31884b6a6967568505e524579 --- /dev/null +++ b/drip-android-observer-master/README.md @@ -0,0 +1,104 @@ +# Drone Scanner Android receiver application + +This project provides the source codes for an example receiver implementation for dectecting drones using Bluetooth, WiFi NAN (Neighbor Aware Network) and WiFi Beacon signals for Android phones. +The application is compliant with the Bluetooth and WiFi NAN part of the ASTM Remote ID standard. +The application is also compliant with the Bluetooth, WiFi NAN and WiFi Beacon parts of the upcoming ASD-STAN Direct Remote ID standard. + +The application continuously scans for Bluetooth advertising/WiFi NAN signals/beacons. +If any is found matching the specifiers for DRIP signals, it adds that transmitter to a list, will display the location of the drone on a map and can show the detailed content of the DRIP data. + + + +The red marker on the map shows the location of the drone and the blue marker the location of the operator (if that data field is being received). +A red line will be drawn to show where the drone has been flying. + +Please note: The user of this receiver application must always visually verify that the received Drone ID signal corresponds to an actual drone seen flying in the air, at the position the signal claims it to be. + +To build the application, use Android Studio. +Import the project (File -> New -> Import Project) and point to the folder named Android. +Then Build -> Make Project. + +For full functionality, before building the source, you need to obtain a Google Maps API key. +Without the key, the application can be started and will pick up transmitted signals and will show those in the list and detailed info views, but the map view will not work. +The sources are on purpose not delivered with a key and for the same reason ready built apk files are not provided. +Please generate your own key as detailed here: +https://developers.google.com/maps/documentation/android-sdk/get-api-key + +Place the key as a string resuorce with the name google_maps_key. +<string name="google_maps_key">AIZA_FAKE_KEY_HERE</string> + +You will also need to start the server and link the app to the server. The server can be found at: +https://gitlab.liu.se/tdde21-cryptographic-drone-id/tdde21-drip-backend-master + +Follow the guidelines there to start the server. Then find out the servers IP address. +Once you have the IP address go into the apps settings and enter the IP address in the form shown below: +https://XXX.XXX.XXX.XXX:8080 +The XXX are where the IP address should go. The 8080 is the port that the server runs on +and the https:// is needed since the server only allows https connections. + +## Transmission method support on different Android smartphones + +An extensive list of the support in different phone models is [available here](supported-smartphones.md). + +The transmission methods supported on a specific device (as per the Android feature flags) are listed in the settings menu of the application. +However, reception can still fail for various reasons. +See further details below and in the description of the [device list](supported-smartphones.md). + +### Bluetooth + +The Bluetooth reception of the application has been tested to work on several devices: +- Huawei Y6 Pro (Android 5.1) +- HTC one M9 (Android 5.1, 6.0, 7.0) +- OnePlus 6T (Android 9 and 10) +- Samsung Galaxy S10 (Android 9, 10 and 11) +- Huawei Mate 20 Pro (Android 9) +- HMD Global Nokia 2.2 (Android 9) +- Motorola One Vision (Android 9) +- Xiaomi Mi 9 (Android 9) + +The app will read the Android feature flags to determine whether the phone model supports receiving only Legacy Bluetooth advertising signals (BT4) or whether it also supports receiving Long Range + Extended Advertising signals (optional BT5 features). +If both are supported, it will listen for both types simultaneously. + +All tested devices receive Bluetooth Legacy advertisements continuously. + +For receiving Long Range and Extended Advertising signals, out of the tested devices, only the Samsung Galaxy S10 and the Huawei Mate 20 Pro devices can be recommended. +Both receive the signals continuously. +The S10 seems to have a bit better signal strength reception. + +The One Plus 6T and the Xiaomi Mi 9 both receive the signals but some power(?) saving feature or similar in the driver layer cause them to receive the signals for 5 seconds, then pause 15 seconds and this repeats. +This unfortunately makes tracking of Open Drone ID signals rather impractical due to the long pauses. +Both are based on Qualcomm Snapdragon chipsets. +A SW update somewhere in Q4 2020 changed this behavior so the One Plus 6T would listen for 1 second, pause for 4 seconds and repeat. +It might be possible that the driver layer alternate between listening to Coded PHY S=8 and S=2, causing this effect? + +The Motorola One Vision and the HMD Global Nokia 2.2 are not recommended. +Both have the Android feature flags for Long Range and Extended Advertising set to true, but in reality they never receive those signals. +There seems to be a clear error in the driver implementations of those devices or maybe they don't listen to the coded PHY on the primary channel but only on the secondary channel? + +The rest of the tested phones support receiving only Bluetooth Legacy Advertising signals. + +### WiFi NAN + +Reception of WiFi NAN signals have been successfully tested on the Samsung Galaxy S10 device. +It should be possible to receive WiFi NAN signals on the following devices, but please be aware that this has not been verified using this receiver application: +- Samsung Galaxy S9, S10, S20 (and the various +, note, ultra etc. variants of these) +- Google Pixel 2, 3, 4 (and the various A, XL variants of these) +- Xiaomi Mi 8, 9, Note 10, redmi K20 Pro/CC9 Pro/Note 10 Pro + +### WiFi Beacon + +WiFi Beacon reception in the current implementation is limited to devices running Android 6 and higher. +Reception has been tested on the following devices: +- Google Pixel 3 (Android 11) +- Samsung Galaxy S10 (Android 11) +- OnePlus 6T (Android 10) +- Samsung Galaxy A3 (Android 8) devices. + +Please note that On Android 8 and higher, by default the scanning frequency of Wi-Fi Beacons is [throttled](https://developer.android.com/guide/topics/connectivity/wifi-scan#wifi-scan-throttling). +The only way to increase this is to enable the [Android Developer Mode](https://developer.android.com/studio/debug/dev-options) and then disable the WiFi scan throttling option. + +## High level SW Architecture + +An auto-generated view of the class structure can be seen in the below figure. + + diff --git a/drip-android-observer-master/Screenshot.jpg b/drip-android-observer-master/Screenshot.jpg new file mode 100644 index 0000000000000000000000000000000000000000..dd84ef28206650d891766881c916088d4a36cc93 Binary files /dev/null and b/drip-android-observer-master/Screenshot.jpg differ diff --git a/drip-android-observer-master/drone.svg b/drip-android-observer-master/drone.svg new file mode 100644 index 0000000000000000000000000000000000000000..94e3da4cc2e663d126675bdb1a9df919b2899c66 --- /dev/null +++ b/drip-android-observer-master/drone.svg @@ -0,0 +1,163 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="210mm" + height="297mm" + viewBox="0 0 210 297" + version="1.1" + id="svg8" + inkscape:version="0.92.4 (5da689c313, 2019-01-14)" + sodipodi:docname="drone.svg"> + <defs + id="defs2" /> + <sodipodi:namedview + id="base" + pagecolor="#0050ff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:zoom="1.4" + inkscape:cx="392.70649" + inkscape:cy="480.18786" + inkscape:document-units="mm" + inkscape:current-layer="layer1" + showgrid="false" + inkscape:window-width="2400" + inkscape:window-height="1421" + inkscape:window-x="-9" + inkscape:window-y="-9" + inkscape:window-maximized="1" /> + <metadata + id="metadata5"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1"> + <g + id="g953"> + <path + d="M 59.66536,138.15137 A 26.919239,26.934183 0 0 1 31.678138,125.09347 26.919239,26.934183 0 0 1 34.369549,94.313422 26.919239,26.934183 0 0 1 64.197805,86.316528 26.919239,26.934183 0 0 1 81.910122,111.62638" + sodipodi:open="true" + sodipodi:end="0" + sodipodi:start="1.3962634" + sodipodi:ry="26.934183" + sodipodi:rx="26.919239" + sodipodi:cy="111.62638" + sodipodi:cx="54.990883" + sodipodi:type="arc" + id="path815-5" + style="opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#e5e5e5;stroke-width:12;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke" /> + <path + transform="rotate(-90)" + d="m -194.42985,82.141886 a 26.919231,26.934172 0 0 1 -25.29581,-17.722142 26.919231,26.934172 0 0 1 7.99246,-29.844802 26.919231,26.934172 0 0 1 30.76296,-2.692905 26.919231,26.934172 0 0 1 13.05065,28.002747" + sodipodi:open="true" + sodipodi:end="0.17453293" + sodipodi:start="1.5707963" + sodipodi:ry="26.934172" + sodipodi:rx="26.919231" + sodipodi:cy="55.207714" + sodipodi:cx="-194.42986" + sodipodi:type="arc" + id="path815-5-6" + style="opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#e5e5e5;stroke-width:12;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke" /> + <path + transform="scale(-1)" + d="m -151.64235,-171.43902 a 26.919283,26.934183 0 0 1 -11.83621,-32.53772 26.919283,26.934183 0 0 1 29.97034,-17.31296 26.919283,26.934183 0 0 1 22.24479,26.52499" + sodipodi:open="true" + sodipodi:end="0" + sodipodi:start="2.0943951" + sodipodi:ry="26.934183" + sodipodi:rx="26.919283" + sodipodi:cy="-194.76471" + sodipodi:cx="-138.18271" + sodipodi:type="arc" + id="path815-5-60" + style="opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#e5e5e5;stroke-width:12;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke" /> + <path + transform="rotate(90)" + d="m 112.10543,-111.13172 a 26.919283,26.934183 0 0 1 -26.510318,-22.25711 26.919283,26.934183 0 0 1 17.303378,-29.98693 26.919283,26.934183 0 0 1 32.51972,11.84276" + sodipodi:open="true" + sodipodi:end="5.7595865" + sodipodi:start="1.5707963" + sodipodi:ry="26.934183" + sodipodi:rx="26.919283" + sodipodi:cy="-138.0659" + sodipodi:cx="112.10543" + sodipodi:type="arc" + id="path815-5-1" + style="opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#e5e5e5;stroke-width:12;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke" /> + <g + id="g940"> + <path + style="opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:12;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke" + id="path904" + sodipodi:type="arc" + sodipodi:cx="110.91379" + sodipodi:cy="153.80769" + sodipodi:rx="32.366005" + sodipodi:ry="29.242249" + sodipodi:start="4.7105847" + sodipodi:end="1.5706165" + d="m 110.85539,124.56549 a 32.366005,29.242249 0 0 1 28.06772,14.58911 32.366005,29.242249 0 0 1 0.0321,29.25595 32.366005,29.242249 0 0 1 -28.03563,14.63939 l -0.006,-29.24225 z" /> + <rect + style="opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:8.18205643;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke" + id="rect906" + width="3.5167706" + height="62.876343" + x="77.373306" + y="122.41048" /> + <rect + style="opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:8.62253952;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke" + id="rect906-9" + width="2.9156606" + height="22.263962" + x="-125.51873" + y="67.205223" + transform="rotate(-90)" /> + <rect + style="opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:8.62266254;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke" + id="rect906-9-7" + width="2.9157598" + height="22.263844" + x="-185.228" + y="67.885811" + transform="rotate(-90)" /> + <rect + style="opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:5.53488302;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke" + id="rect906-9-1" + width="6.0033178" + height="4.4554806" + x="-127.34177" + y="102.12694" + transform="rotate(-90)" /> + <rect + style="opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:5.53488302;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke" + id="rect906-9-1-7" + width="6.0033178" + height="4.4554806" + x="-186.21191" + y="101.90469" + transform="rotate(-90)" /> + </g> + </g> + </g> +</svg> diff --git a/drip-android-observer-master/images/screen_nrf_connect_device_information.jpg b/drip-android-observer-master/images/screen_nrf_connect_device_information.jpg new file mode 100644 index 0000000000000000000000000000000000000000..330d1d1d18c220fb47691bec130130dad9598a5a Binary files /dev/null and b/drip-android-observer-master/images/screen_nrf_connect_device_information.jpg differ diff --git a/drip-android-observer-master/images/screen_nrf_connect_new_advertising_packet.jpg b/drip-android-observer-master/images/screen_nrf_connect_new_advertising_packet.jpg new file mode 100644 index 0000000000000000000000000000000000000000..86196a93459c228c9b1357ae47a50143636cd8ef Binary files /dev/null and b/drip-android-observer-master/images/screen_nrf_connect_new_advertising_packet.jpg differ diff --git a/drip-android-observer-master/images/screen_nrf_connect_scanning.jpg b/drip-android-observer-master/images/screen_nrf_connect_scanning.jpg new file mode 100644 index 0000000000000000000000000000000000000000..89088b4dab9d6ca5750fac428d89e0a95e4520a3 Binary files /dev/null and b/drip-android-observer-master/images/screen_nrf_connect_scanning.jpg differ diff --git a/drip-android-observer-master/images/screen_nrf_connect_scanning_rssi.jpg b/drip-android-observer-master/images/screen_nrf_connect_scanning_rssi.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f20f0f9f32340f51498072ed42ea0dff153ce714 Binary files /dev/null and b/drip-android-observer-master/images/screen_nrf_connect_scanning_rssi.jpg differ diff --git a/drip-android-observer-master/receiver_proofs/Asus_Zenfone6/bt_basic.jpg b/drip-android-observer-master/receiver_proofs/Asus_Zenfone6/bt_basic.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4ee966c67033d21085f661f91e3a793066d295e5 Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/Asus_Zenfone6/bt_basic.jpg differ diff --git a/drip-android-observer-master/receiver_proofs/Asus_Zenfone6/bt_lr_adv.jpg b/drip-android-observer-master/receiver_proofs/Asus_Zenfone6/bt_lr_adv.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f573ee45c3e6dca12340853a114578422085adf2 Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/Asus_Zenfone6/bt_lr_adv.jpg differ diff --git a/drip-android-observer-master/receiver_proofs/Asus_Zenfone6/bt_lr_rssi.jpg b/drip-android-observer-master/receiver_proofs/Asus_Zenfone6/bt_lr_rssi.jpg new file mode 100644 index 0000000000000000000000000000000000000000..880bc79534524da65576def17ca8247f6c2e488e Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/Asus_Zenfone6/bt_lr_rssi.jpg differ diff --git a/drip-android-observer-master/receiver_proofs/Asus_Zenfone6/wifi_beacon.jpg b/drip-android-observer-master/receiver_proofs/Asus_Zenfone6/wifi_beacon.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b788b0774fed8439c237e05d9bdfbf9207f09c40 Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/Asus_Zenfone6/wifi_beacon.jpg differ diff --git a/drip-android-observer-master/receiver_proofs/Asus_Zenfone6/wifi_nan.jpg b/drip-android-observer-master/receiver_proofs/Asus_Zenfone6/wifi_nan.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2b8b8b3e3d58191c97f428a862ca4cbd3b7bc675 Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/Asus_Zenfone6/wifi_nan.jpg differ diff --git a/drip-android-observer-master/receiver_proofs/Huawei_Honor_10_Lite/Huawei_Honor_10_Lite_beacon.png b/drip-android-observer-master/receiver_proofs/Huawei_Honor_10_Lite/Huawei_Honor_10_Lite_beacon.png new file mode 100644 index 0000000000000000000000000000000000000000..47a77c8c0f360e6a6c9ad908fdf9b2b8e4d27f27 Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/Huawei_Honor_10_Lite/Huawei_Honor_10_Lite_beacon.png differ diff --git a/drip-android-observer-master/receiver_proofs/Huawei_Honor_8S/bt_basic.jpg b/drip-android-observer-master/receiver_proofs/Huawei_Honor_8S/bt_basic.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4fed999f7880692bf23f44f0c53ba7673b6631b6 Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/Huawei_Honor_8S/bt_basic.jpg differ diff --git a/drip-android-observer-master/receiver_proofs/Huawei_Honor_8S/wifi_nan.jpg b/drip-android-observer-master/receiver_proofs/Huawei_Honor_8S/wifi_nan.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6fa30cbb6ab4ce044d60242b39fe815098e037c2 Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/Huawei_Honor_8S/wifi_nan.jpg differ diff --git a/drip-android-observer-master/receiver_proofs/Huawei_MediaPad_M5/bt_basic.jpg b/drip-android-observer-master/receiver_proofs/Huawei_MediaPad_M5/bt_basic.jpg new file mode 100644 index 0000000000000000000000000000000000000000..eb6495ff4f95229e0b9605bf59b1db888b07971d Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/Huawei_MediaPad_M5/bt_basic.jpg differ diff --git a/drip-android-observer-master/receiver_proofs/Huawei_MediaPad_M5/wifi_nan.jpg b/drip-android-observer-master/receiver_proofs/Huawei_MediaPad_M5/wifi_nan.jpg new file mode 100644 index 0000000000000000000000000000000000000000..cb3b6398be2e845ce6c1a47be5dafe0df180da16 Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/Huawei_MediaPad_M5/wifi_nan.jpg differ diff --git a/drip-android-observer-master/receiver_proofs/Huawei_Nexus_6P/Huawei_Nexus_6P_beacon.png b/drip-android-observer-master/receiver_proofs/Huawei_Nexus_6P/Huawei_Nexus_6P_beacon.png new file mode 100644 index 0000000000000000000000000000000000000000..95e28dc9cb7d9e750f242f4ef49cc94c78f9afbf Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/Huawei_Nexus_6P/Huawei_Nexus_6P_beacon.png differ diff --git a/drip-android-observer-master/receiver_proofs/LG_G5/bt_basic.jpg b/drip-android-observer-master/receiver_proofs/LG_G5/bt_basic.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c10f9f08f2bfe57579cbdb464387ff882ffda801 Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/LG_G5/bt_basic.jpg differ diff --git a/drip-android-observer-master/receiver_proofs/LG_G5/wifi_nan.jpg b/drip-android-observer-master/receiver_proofs/LG_G5/wifi_nan.jpg new file mode 100644 index 0000000000000000000000000000000000000000..fe18bf6a8733ea0ab619d66e56b82186a05b1d59 Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/LG_G5/wifi_nan.jpg differ diff --git a/drip-android-observer-master/receiver_proofs/Motorola_One_Vision/bt_basic.jpg b/drip-android-observer-master/receiver_proofs/Motorola_One_Vision/bt_basic.jpg new file mode 100644 index 0000000000000000000000000000000000000000..589271bc5525072a60db1235d258615e1d5382e5 Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/Motorola_One_Vision/bt_basic.jpg differ diff --git a/drip-android-observer-master/receiver_proofs/Nokia_2_2/bt_basic.jpg b/drip-android-observer-master/receiver_proofs/Nokia_2_2/bt_basic.jpg new file mode 100644 index 0000000000000000000000000000000000000000..67c8affacbfd565f2ffc2491cdafb49abf5cd2b1 Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/Nokia_2_2/bt_basic.jpg differ diff --git a/drip-android-observer-master/receiver_proofs/Nokia_2_2/wifi_nan.jpg b/drip-android-observer-master/receiver_proofs/Nokia_2_2/wifi_nan.jpg new file mode 100644 index 0000000000000000000000000000000000000000..dc46436b6738101769636395996f3b94e5216d3f Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/Nokia_2_2/wifi_nan.jpg differ diff --git a/drip-android-observer-master/receiver_proofs/Nokia_6_2/bt_basic.jpg b/drip-android-observer-master/receiver_proofs/Nokia_6_2/bt_basic.jpg new file mode 100644 index 0000000000000000000000000000000000000000..79a37cd7efff3c4763a230481a78c7a19b473250 Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/Nokia_6_2/bt_basic.jpg differ diff --git a/drip-android-observer-master/receiver_proofs/Nokia_6_2/wifi_nan.jpg b/drip-android-observer-master/receiver_proofs/Nokia_6_2/wifi_nan.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3710ac42be435a6e0b6221366be7e9fb89848879 Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/Nokia_6_2/wifi_nan.jpg differ diff --git a/drip-android-observer-master/receiver_proofs/Nokia_7_2/bt_basic.jpg b/drip-android-observer-master/receiver_proofs/Nokia_7_2/bt_basic.jpg new file mode 100644 index 0000000000000000000000000000000000000000..aed7bbeb9eee1c876b7c4767bf14505aa20fe589 Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/Nokia_7_2/bt_basic.jpg differ diff --git a/drip-android-observer-master/receiver_proofs/Nokia_7_2/wifi_nan.jpg b/drip-android-observer-master/receiver_proofs/Nokia_7_2/wifi_nan.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f484121e57e1797e71ed7d277147684895e1ced4 Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/Nokia_7_2/wifi_nan.jpg differ diff --git a/drip-android-observer-master/receiver_proofs/OnePlus_6/OnePlus_6_beacon.png b/drip-android-observer-master/receiver_proofs/OnePlus_6/OnePlus_6_beacon.png new file mode 100644 index 0000000000000000000000000000000000000000..ae34e46f52e284256785b686809c0452dbf092f8 Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/OnePlus_6/OnePlus_6_beacon.png differ diff --git a/drip-android-observer-master/receiver_proofs/OnePlus_6T/bt_basic.jpg b/drip-android-observer-master/receiver_proofs/OnePlus_6T/bt_basic.jpg new file mode 100644 index 0000000000000000000000000000000000000000..bc00a53d4630aed8c681ab66dd36ddb91e11875d Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/OnePlus_6T/bt_basic.jpg differ diff --git a/drip-android-observer-master/receiver_proofs/OnePlus_6T/bt_lr_adv.jpg b/drip-android-observer-master/receiver_proofs/OnePlus_6T/bt_lr_adv.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f15fb19f4be7b561de0d5efd21af333e66d42abe Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/OnePlus_6T/bt_lr_adv.jpg differ diff --git a/drip-android-observer-master/receiver_proofs/OnePlus_6T/bt_lr_rssi.jpg b/drip-android-observer-master/receiver_proofs/OnePlus_6T/bt_lr_rssi.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f8aefd04a49801530699b469fa973b2f63984dfe Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/OnePlus_6T/bt_lr_rssi.jpg differ diff --git a/drip-android-observer-master/receiver_proofs/OnePlus_6T/wifi_nan.jpg b/drip-android-observer-master/receiver_proofs/OnePlus_6T/wifi_nan.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1d405de43c18c2135d28f91167c29c80afd84f28 Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/OnePlus_6T/wifi_nan.jpg differ diff --git a/drip-android-observer-master/receiver_proofs/OnePlus_7T/bt_basic.jpg b/drip-android-observer-master/receiver_proofs/OnePlus_7T/bt_basic.jpg new file mode 100644 index 0000000000000000000000000000000000000000..7497863851dde954ed5c8627378ba3e07e1f6c75 Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/OnePlus_7T/bt_basic.jpg differ diff --git a/drip-android-observer-master/receiver_proofs/OnePlus_7T/wifi_nan.jpg b/drip-android-observer-master/receiver_proofs/OnePlus_7T/wifi_nan.jpg new file mode 100644 index 0000000000000000000000000000000000000000..47b84d25fd0bdd40e71267c82a1262396b16f119 Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/OnePlus_7T/wifi_nan.jpg differ diff --git a/drip-android-observer-master/receiver_proofs/OnePlus_7_Pro/bt_basic.jpg b/drip-android-observer-master/receiver_proofs/OnePlus_7_Pro/bt_basic.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e1a40ee68220094e2a72840793ed7b5a2bb5f5da Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/OnePlus_7_Pro/bt_basic.jpg differ diff --git a/drip-android-observer-master/receiver_proofs/OnePlus_7_Pro/wifi_nan.jpg b/drip-android-observer-master/receiver_proofs/OnePlus_7_Pro/wifi_nan.jpg new file mode 100644 index 0000000000000000000000000000000000000000..13eef3b3dd0b6d0c1ca58a92f0734f5dc4393570 Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/OnePlus_7_Pro/wifi_nan.jpg differ diff --git a/drip-android-observer-master/receiver_proofs/OnePlus_8T/bt_basic.jpg b/drip-android-observer-master/receiver_proofs/OnePlus_8T/bt_basic.jpg new file mode 100644 index 0000000000000000000000000000000000000000..330d1d1d18c220fb47691bec130130dad9598a5a Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/OnePlus_8T/bt_basic.jpg differ diff --git a/drip-android-observer-master/receiver_proofs/OnePlus_8T/bt_lr_adv.jpg b/drip-android-observer-master/receiver_proofs/OnePlus_8T/bt_lr_adv.jpg new file mode 100644 index 0000000000000000000000000000000000000000..89088b4dab9d6ca5750fac428d89e0a95e4520a3 Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/OnePlus_8T/bt_lr_adv.jpg differ diff --git a/drip-android-observer-master/receiver_proofs/OnePlus_8T/bt_lr_rssi.jpg b/drip-android-observer-master/receiver_proofs/OnePlus_8T/bt_lr_rssi.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f20f0f9f32340f51498072ed42ea0dff153ce714 Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/OnePlus_8T/bt_lr_rssi.jpg differ diff --git a/drip-android-observer-master/receiver_proofs/OnePlus_8T/wifi_nan.jpg b/drip-android-observer-master/receiver_proofs/OnePlus_8T/wifi_nan.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3fff18e0646879a49acae82585c4ac0d9203a276 Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/OnePlus_8T/wifi_nan.jpg differ diff --git a/drip-android-observer-master/receiver_proofs/OnePlus_N10_5G/bt_basic.jpg b/drip-android-observer-master/receiver_proofs/OnePlus_N10_5G/bt_basic.jpg new file mode 100644 index 0000000000000000000000000000000000000000..40eb96bc2563e3e8cb3670ea6885601c679a20da Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/OnePlus_N10_5G/bt_basic.jpg differ diff --git a/drip-android-observer-master/receiver_proofs/OnePlus_N10_5G/bt_lr_adv.jpg b/drip-android-observer-master/receiver_proofs/OnePlus_N10_5G/bt_lr_adv.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4166d0754932b7b9aecd5940526f2c8fecae4d22 Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/OnePlus_N10_5G/bt_lr_adv.jpg differ diff --git a/drip-android-observer-master/receiver_proofs/OnePlus_N10_5G/bt_lr_rssi.jpg b/drip-android-observer-master/receiver_proofs/OnePlus_N10_5G/bt_lr_rssi.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f215eb5aa6e723d2dc66914281d2bf387097d82f Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/OnePlus_N10_5G/bt_lr_rssi.jpg differ diff --git a/drip-android-observer-master/receiver_proofs/OnePlus_N10_5G/wifi_nan.jpg b/drip-android-observer-master/receiver_proofs/OnePlus_N10_5G/wifi_nan.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9bac2978c87c3ed792a93f8b41f68f4642bb4fa1 Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/OnePlus_N10_5G/wifi_nan.jpg differ diff --git a/drip-android-observer-master/receiver_proofs/OnePlus_Nord_5G/bt_basic.jpg b/drip-android-observer-master/receiver_proofs/OnePlus_Nord_5G/bt_basic.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f3d6c8c6549147cee56b4a70900b0eef55592b6a Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/OnePlus_Nord_5G/bt_basic.jpg differ diff --git a/drip-android-observer-master/receiver_proofs/OnePlus_Nord_5G/bt_lr_adv.jpg b/drip-android-observer-master/receiver_proofs/OnePlus_Nord_5G/bt_lr_adv.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ab49bbf76297997f65d03621e7fecaf0f2b0ecb7 Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/OnePlus_Nord_5G/bt_lr_adv.jpg differ diff --git a/drip-android-observer-master/receiver_proofs/OnePlus_Nord_5G/bt_lr_rssi.jpg b/drip-android-observer-master/receiver_proofs/OnePlus_Nord_5G/bt_lr_rssi.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8f5a08fb3f7eacbf761f1beaba947976f1460025 Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/OnePlus_Nord_5G/bt_lr_rssi.jpg differ diff --git a/drip-android-observer-master/receiver_proofs/OnePlus_Nord_5G/wifi_nan.jpg b/drip-android-observer-master/receiver_proofs/OnePlus_Nord_5G/wifi_nan.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a1ea7381e378cb8af49d13852e63ca1b57284eef Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/OnePlus_Nord_5G/wifi_nan.jpg differ diff --git a/drip-android-observer-master/receiver_proofs/Samsung_Galaxy_A71/bt_basic.jpg b/drip-android-observer-master/receiver_proofs/Samsung_Galaxy_A71/bt_basic.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e7917ef9f4bbe6b03708c20883f3b22e27ff42ab Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/Samsung_Galaxy_A71/bt_basic.jpg differ diff --git a/drip-android-observer-master/receiver_proofs/Samsung_Galaxy_A71/wifi_nan.jpg b/drip-android-observer-master/receiver_proofs/Samsung_Galaxy_A71/wifi_nan.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4244f5f465b83d180e0a64ef452aecfb68e7f66b Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/Samsung_Galaxy_A71/wifi_nan.jpg differ diff --git a/drip-android-observer-master/receiver_proofs/Samsung_Galaxy_A8/Samsung_Galaxy_A8_beacon.png b/drip-android-observer-master/receiver_proofs/Samsung_Galaxy_A8/Samsung_Galaxy_A8_beacon.png new file mode 100644 index 0000000000000000000000000000000000000000..1a785b63c964b71a054bddb4900a3d731e116a64 Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/Samsung_Galaxy_A8/Samsung_Galaxy_A8_beacon.png differ diff --git a/drip-android-observer-master/receiver_proofs/Samsung_Galaxy_Note_10+/Samsumg_Galaxy_Note_10+_beacon.png b/drip-android-observer-master/receiver_proofs/Samsung_Galaxy_Note_10+/Samsumg_Galaxy_Note_10+_beacon.png new file mode 100644 index 0000000000000000000000000000000000000000..2059fd9043b973f86bb27c1ebc903ecbbfc40b92 Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/Samsung_Galaxy_Note_10+/Samsumg_Galaxy_Note_10+_beacon.png differ diff --git a/drip-android-observer-master/receiver_proofs/Samsung_Galaxy_Note_10/Samsumg_Galaxy_Note_10_beacon.png b/drip-android-observer-master/receiver_proofs/Samsung_Galaxy_Note_10/Samsumg_Galaxy_Note_10_beacon.png new file mode 100644 index 0000000000000000000000000000000000000000..939baa1bdf2546700bf85295eb21ad577f9d94bf Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/Samsung_Galaxy_Note_10/Samsumg_Galaxy_Note_10_beacon.png differ diff --git a/drip-android-observer-master/receiver_proofs/Samsung_Galaxy_S10_Exynos/bt_basic.jpg b/drip-android-observer-master/receiver_proofs/Samsung_Galaxy_S10_Exynos/bt_basic.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d8d4c9987e2958928e80545f38beb4184c33fc5a Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/Samsung_Galaxy_S10_Exynos/bt_basic.jpg differ diff --git a/drip-android-observer-master/receiver_proofs/Samsung_Galaxy_S10_Exynos/bt_lr_adv.jpg b/drip-android-observer-master/receiver_proofs/Samsung_Galaxy_S10_Exynos/bt_lr_adv.jpg new file mode 100644 index 0000000000000000000000000000000000000000..7a4729d05ca6937fe37378bfa7e3d03f02bb246b Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/Samsung_Galaxy_S10_Exynos/bt_lr_adv.jpg differ diff --git a/drip-android-observer-master/receiver_proofs/Samsung_Galaxy_S10_Exynos/bt_lr_rssi.jpg b/drip-android-observer-master/receiver_proofs/Samsung_Galaxy_S10_Exynos/bt_lr_rssi.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9e3ceaa6d1e1015e576316650c5c1168cd776b00 Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/Samsung_Galaxy_S10_Exynos/bt_lr_rssi.jpg differ diff --git a/drip-android-observer-master/receiver_proofs/Samsung_Galaxy_S10_Exynos/wifi_nan.jpg b/drip-android-observer-master/receiver_proofs/Samsung_Galaxy_S10_Exynos/wifi_nan.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4a443f32232da739b1c9e03e94ae1dea20a82ce5 Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/Samsung_Galaxy_S10_Exynos/wifi_nan.jpg differ diff --git a/drip-android-observer-master/receiver_proofs/Samsung_Galaxy_S20_Exynos/Samsung_Galaxy_S20_beacon.png b/drip-android-observer-master/receiver_proofs/Samsung_Galaxy_S20_Exynos/Samsung_Galaxy_S20_beacon.png new file mode 100644 index 0000000000000000000000000000000000000000..d5a63dea193553d390607f17a99b781767eef312 Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/Samsung_Galaxy_S20_Exynos/Samsung_Galaxy_S20_beacon.png differ diff --git a/drip-android-observer-master/receiver_proofs/Samsung_Galaxy_S6/Samsung_Galaxy_S6_beacon.png b/drip-android-observer-master/receiver_proofs/Samsung_Galaxy_S6/Samsung_Galaxy_S6_beacon.png new file mode 100644 index 0000000000000000000000000000000000000000..0af7ba4330380068e1c93795dcb7918a3792df97 Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/Samsung_Galaxy_S6/Samsung_Galaxy_S6_beacon.png differ diff --git a/drip-android-observer-master/receiver_proofs/Samsung_Galaxy_S7/Samsung_Galaxy_S7_beacon.jpg b/drip-android-observer-master/receiver_proofs/Samsung_Galaxy_S7/Samsung_Galaxy_S7_beacon.jpg new file mode 100644 index 0000000000000000000000000000000000000000..554c55571f17a40aacde3e923cb6cd00dc35569d Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/Samsung_Galaxy_S7/Samsung_Galaxy_S7_beacon.jpg differ diff --git a/drip-android-observer-master/receiver_proofs/Samsung_Galaxy_XCover_Pro/bt_basic.jpg b/drip-android-observer-master/receiver_proofs/Samsung_Galaxy_XCover_Pro/bt_basic.jpg new file mode 100644 index 0000000000000000000000000000000000000000..dbf7c6b92622f3cd714bc31b94807845bf6fc464 Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/Samsung_Galaxy_XCover_Pro/bt_basic.jpg differ diff --git a/drip-android-observer-master/receiver_proofs/Samsung_Galaxy_XCover_Pro/wifi_nan.jpg b/drip-android-observer-master/receiver_proofs/Samsung_Galaxy_XCover_Pro/wifi_nan.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2f7eba7dd80cefcc981b3f0369b602e77f78b7c8 Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/Samsung_Galaxy_XCover_Pro/wifi_nan.jpg differ diff --git a/drip-android-observer-master/receiver_proofs/Sony_Xperia_5/Sony_Xperia_5_beacon.png b/drip-android-observer-master/receiver_proofs/Sony_Xperia_5/Sony_Xperia_5_beacon.png new file mode 100644 index 0000000000000000000000000000000000000000..66bd603379e0cf824689dbb06f1ee8663f37be1d Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/Sony_Xperia_5/Sony_Xperia_5_beacon.png differ diff --git a/drip-android-observer-master/receiver_proofs/Xiaomi_Mi_9/bt_basic.jpg b/drip-android-observer-master/receiver_proofs/Xiaomi_Mi_9/bt_basic.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ddddb105bab0ffcf76e3239492d06e1c06290662 Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/Xiaomi_Mi_9/bt_basic.jpg differ diff --git a/drip-android-observer-master/receiver_proofs/Xiaomi_Mi_9T_Pro/bt_basic.jpg b/drip-android-observer-master/receiver_proofs/Xiaomi_Mi_9T_Pro/bt_basic.jpg new file mode 100644 index 0000000000000000000000000000000000000000..457b2fb26dcb10ed6cba9cc6d46785d34cad0ca5 Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/Xiaomi_Mi_9T_Pro/bt_basic.jpg differ diff --git a/drip-android-observer-master/receiver_proofs/Xiaomi_Mi_9T_Pro/wifi_nan.jpg b/drip-android-observer-master/receiver_proofs/Xiaomi_Mi_9T_Pro/wifi_nan.jpg new file mode 100644 index 0000000000000000000000000000000000000000..7e1a11367c32a6e370e174f01fb8679646b04754 Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/Xiaomi_Mi_9T_Pro/wifi_nan.jpg differ diff --git a/drip-android-observer-master/receiver_proofs/Xiaomi_Mi_9_SE/bt_basic.jpg b/drip-android-observer-master/receiver_proofs/Xiaomi_Mi_9_SE/bt_basic.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b69c83477f0975c6d103e34f27311bea27e15253 Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/Xiaomi_Mi_9_SE/bt_basic.jpg differ diff --git a/drip-android-observer-master/receiver_proofs/Xiaomi_Mi_9_SE/wifi_nan.jpg b/drip-android-observer-master/receiver_proofs/Xiaomi_Mi_9_SE/wifi_nan.jpg new file mode 100644 index 0000000000000000000000000000000000000000..51f256fd405057146f0fb495c014c3c37e8808e5 Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/Xiaomi_Mi_9_SE/wifi_nan.jpg differ diff --git a/drip-android-observer-master/receiver_proofs/Xiaomi_Mi_A2/bt_basic.jpg b/drip-android-observer-master/receiver_proofs/Xiaomi_Mi_A2/bt_basic.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1069ae0c6910d828b6bf98d170e8d4e785ab2548 Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/Xiaomi_Mi_A2/bt_basic.jpg differ diff --git a/drip-android-observer-master/receiver_proofs/Xiaomi_Mi_A2/wifi_nan.jpg b/drip-android-observer-master/receiver_proofs/Xiaomi_Mi_A2/wifi_nan.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c4d9b1c512a68ac6a65b3ed62b063c0f9e4f5153 Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/Xiaomi_Mi_A2/wifi_nan.jpg differ diff --git a/drip-android-observer-master/receiver_proofs/Xiaomi_Mi_Note_10/bt_basic.jpg b/drip-android-observer-master/receiver_proofs/Xiaomi_Mi_Note_10/bt_basic.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d781376571b6e197527b8c2fe7b6cc0b7cf319d1 Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/Xiaomi_Mi_Note_10/bt_basic.jpg differ diff --git a/drip-android-observer-master/receiver_proofs/Xiaomi_Mi_Note_10/wifi_nan.jpg b/drip-android-observer-master/receiver_proofs/Xiaomi_Mi_Note_10/wifi_nan.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e812305223de9e3226bb682768fdc44157c575f6 Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/Xiaomi_Mi_Note_10/wifi_nan.jpg differ diff --git a/drip-android-observer-master/receiver_proofs/Xiaomi_Redmi_Note_7/bt_basic.jpg b/drip-android-observer-master/receiver_proofs/Xiaomi_Redmi_Note_7/bt_basic.jpg new file mode 100644 index 0000000000000000000000000000000000000000..998773061bb40c87b4fc31cf5be4f719b395f0ab Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/Xiaomi_Redmi_Note_7/bt_basic.jpg differ diff --git a/drip-android-observer-master/receiver_proofs/Xiaomi_Redmi_Note_7/wifi_nan.jpg b/drip-android-observer-master/receiver_proofs/Xiaomi_Redmi_Note_7/wifi_nan.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c6abfa564e7bb3ac27a8af125715a96f0fc5f635 Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/Xiaomi_Redmi_Note_7/wifi_nan.jpg differ diff --git a/drip-android-observer-master/receiver_proofs/Xiaomi_Redmi_Note_8T/bt_basic.jpg b/drip-android-observer-master/receiver_proofs/Xiaomi_Redmi_Note_8T/bt_basic.jpg new file mode 100644 index 0000000000000000000000000000000000000000..00bd3e55bd86bbb372c5f289ce73649d92e3ebfd Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/Xiaomi_Redmi_Note_8T/bt_basic.jpg differ diff --git a/drip-android-observer-master/receiver_proofs/Xiaomi_Redmi_Note_8T/wifi_nan.jpg b/drip-android-observer-master/receiver_proofs/Xiaomi_Redmi_Note_8T/wifi_nan.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ad3a2336152ad33c55d240186d6ff57cd2dd567f Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/Xiaomi_Redmi_Note_8T/wifi_nan.jpg differ diff --git a/drip-android-observer-master/receiver_proofs/Xiaomi_Redmi_Note_8_Pro/bt_basic.jpg b/drip-android-observer-master/receiver_proofs/Xiaomi_Redmi_Note_8_Pro/bt_basic.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f6bed0cfa89ff86fef81514624cdcf7efed6eaec Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/Xiaomi_Redmi_Note_8_Pro/bt_basic.jpg differ diff --git a/drip-android-observer-master/receiver_proofs/Xiaomi_Redmi_Note_8_Pro/wifi_nan.jpg b/drip-android-observer-master/receiver_proofs/Xiaomi_Redmi_Note_8_Pro/wifi_nan.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1e920719fb4f8e311b4a4177e5e087b6ff6cc461 Binary files /dev/null and b/drip-android-observer-master/receiver_proofs/Xiaomi_Redmi_Note_8_Pro/wifi_nan.jpg differ diff --git a/drip-android-observer-master/supported-smartphones.md b/drip-android-observer-master/supported-smartphones.md new file mode 100644 index 0000000000000000000000000000000000000000..39902a616e54e35c34efe6b714d65f299b2811a8 --- /dev/null +++ b/drip-android-observer-master/supported-smartphones.md @@ -0,0 +1,235 @@ +## Introduction + +This document contains a list of compatible smartphones that have been tested for receiving the Broadcast Remote ID signals from an Unmanned Aircraft (UA) or add-on device following the European and US standards. + +It is part of the documentation for the example Android Remote ID receiver application available [here](https://github.com/opendroneid/receiver-android). + +The current specification of both the ASD-STAN prEN4709-02 (EU version) and the ASTM F3411-19 (US version) standards rely on wireless protocols in the unlicensed spectrum to broadcast the desired identification and telemetry data from UAs to ground observers. +Particularly, they define transport methods over Bluetooth 4 Legacy Advertising, Bluetooth 5 Long Range (Advertising Extensions over Coded PHY S8), Wi-Fi Beacon and Wi-Fi Neighbor Awareness Network (NAN). +The main reason for choosing these wireless technologies is that they meet the requirement of being supported on ordinary mobile devices. + +Neither of the above standards cover the receiver side of the Broadcast Remote ID. +This document provides a quick overview of the compatibility of community tested smartphones with the specified Broadcast Remote ID technologies. + +The current stage of the prEN4709-02 (per January 2021) is a finished draft at CEN Enquiry, and the final version will be published in the upcoming months. +To obtain a copy of the ASTM Remote ID standard, please visit this [link](https://www.astm.org/Standards/F3411.htm). + +**Disclaimer:** The list of tested devices is not exhaustive and comes without any guarantees. +It might contain some errors and misleading information. +There might even be some changes via SW updates from the smartphone manufacturers that can either improve or degrade the receiving capabilities. +If you find any incomplete, inconsistent, or wrong information, please feel free to open a GitHub issue. + +## Testing Methodology + +To determine if your smartphone can receive the Broadcast Remote ID messages, a simple test methodology is described below. +Testing is now defined only for Android phones, since iOS devices up until version 14 do not support Bluetooth 5 Long Range, Wi-Fi NAN and the situation about Wi-Fi Beacon support is unclear. +Testing the Bluetooth 4 Legacy Advertising support is irrelevant, since all existing Android and iOS models support it and no devices have been found to behave unexpectedly. + +Please note that there should also be an advanced testing methodology, covering some hidden pitfalls of receiver implementations. +One of the examples is the Bluetooth Long Range feature (Extended Advertisements on Coded PHY S8), since this is an optional part of the Bluetooth 5.0 specification. +Some Android phones claim to support Long Range and Extended Advertising via the Android feature flags, but in reality, they only support advertising from the phone to the others, not reading the Remote ID information (scanning). +Another known pitfall for Bluetooth is that even if the phone supports scanning the Long Range advertisements, a power-saving feature will cause the loss of the majority of the Remote ID messages. +The work on the advanced testing methodology is now in progress. + +### Bluetooth 5 Long Range Advertising - Elimination Criteria + +This simple test is a prerequisite for Bluetooth 5 Long Range functionality support, as it can easily discard phones that don’t have sufficient capabilities to support the Long Range feature. +However, it doesn’t provide all the necessary information to prove the Long Range support. + +1. Install [nRF Connect for Mobile](https://play.google.com/store/apps/details?id=no.nordicsemi.android.mcp). +2. Open the side menu and go to Device information. +3. Check if the rows for the Long Range (PHY Coded) and Extended advertisement are both labeled YES. + +Both of the rows must say YES, in order for the device to be able to receive the Remote ID Bluetooth Long Range broadcast signals. + +<p align="center"> + <img src="images/screen_nrf_connect_device_information.jpg" width="200"> +</p> + +<p align="center"> +Figure 1: Example of OnePlus 8T smartphone passing the elimination criteria +</p> + +### Bluetooth 5 Long Range Support - the Ability to Receive Data + +Suppose the previous test did not eliminate the device from Long Range support. +In that case, it is now necessary to test the target device’s basic receiving capability. +At the moment, this information cannot be read from the OS information and must be verified by using another device that supports the Long Range. + +1. Prepare the Device Under Test (DUT) for the Long Range receiving support by running [nRF Connect for Mobile](https://play.google.com/store/apps/details?id=no.nordicsemi.android.mcp). + * In the Settings menu of the app, select Scanner and set the Scanning period to 5 minutes (or to manual if needed). +2. Use another device (Master) that passed the Elimination Criteria and run [nRF Connect for Mobile](https://play.google.com/store/apps/details?id=no.nordicsemi.android.mcp). + * Alternatively: use any other device that supports Long Range advertisements (might be Remote ID add-on or Bluetooth development kit - e.g., [nRF52840 DK](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52840-DK)). +Please note that device must advertise only Long Range messages. Some add-on device might advertise also Legacy connectable messages so they cannot be used for the test. +3. On the Master, go to the Advertiser tab and create a New advertising packet. + * In options, select Advertising Extensions and in both Primary and Secondary PHY, select LE Coded (Long Range). + * Press the "Add Record" and select "Complete Local Name". + * Set the Interval to 160 (resulting in 100 ms). + * Set the Tx power to 1 dBm (the maximum). + * Leave the other fields at default. +4. On the Master, run the Advertisement by toggling the switch. Select "Until manually turned off" and "No maximum options". + * Alternatively: Start the add-on device or run the [Bluetooth Long Range sample](https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/nrf/samples/bluetooth/central_hr_coded/README.html). +5. On the DUT, run the scanning in the Scanner tab and try to find the Master device. + * It will have the device name of the Master. + + * If the Master is close to the DUT, it should have significantly better RSSI than other devices in the list. + + * Click the found device and verify that the Advertising type is Advertising Extension and that both the Primary and Secondary PHYs are LE Coded. + + * Click the round icon on the left to add the device as a favorite (that adds a small star banner under the round icon). + Then click the filter at the top and select the "Only favorites" checkbox. + + * Swipe on the device to the right in order to reveal the RSSI chart. + If you have more favorites devices present, notice the color of the DUT. + Find the correct color in the chart and observe whether there are gaps in the chart or whether the signals are received continuously. + + * If you are using other method than running nRF Connect on Master device, please ensure that you don't see any Legacy messages there. +If you do then your device also advertises simultaneously on Bluetooth 4 and it will not reveal the gaps in RSSI chart. +Testing with simultaneous Legacy and Long range advertisements is invalid and shouldn't be submitted. + + * Add this information to the report for the device. + +<p align="center"> + <img src="images/screen_nrf_connect_new_advertising_packet.jpg" width="200"> + <img src="images/screen_nrf_connect_scanning.jpg" width="200"> + <img src="images/screen_nrf_connect_scanning_rssi.jpg" width="200"> +</p> + +<p align="center"> Figure 2: Screenshots clarifying the testing steps above</p> + + + +### Wi-Fi Beacon Broadcast + +The only testing solution so far seems to be to have a drone or add-on device that transmits the Wi-Fi Beacon messages and test their reception on the smartphone in OpenDroneID receiver app. + +### Wi-Fi NAN Broadcast + +The easiest way to verify support is to read the Android Feature flags. + +1. Install the [AIDA64](https://play.google.com/store/apps/details?id=com.finalwire.aida64) diagnostic app. +2. Open the Network menu. +3. Scroll down to the bottom and check the Wi-Fi Aware flag. + +### Contributing to the Repository + +#### I have a phone that isn’t listed and I want to add it. + +That’s great! Please follow the methodology to verify its capabilities and create a Pull Request with additional information and screenshots from the tests proving the support. + +For the Pull Request, please upload the screenshots to the `receiver_proofs/xxx_yyy` folder in the repository where `xxx` is the smartphone manufacturer (e.g. Samsung) and `yyy` is the smartphone model (e.g. `Galaxy_Note_10`). +Name the screenshots in the folder accordingly to tested capabilities. +We recommend to use the following template: + +- `bt_basic.jpg` showing the result of passing the elimination criteria - screenshot from the nRF Connect Device information. +- `bt_lr_adv.jpg` showing the reception of the LR advertisements - screenshot from the nRF Connect scanning. +- `bt_lr_rssi.jpg` showing the LR messages’ continuous reception - screenshot from the nRF Connect RSSI graph with the Favorites filter turned on. +- `wifi_beacon.jpg` showing the reception of the Wi-Fi Beacon DRI messages - screenshot from the OpenDroneID app. +- `wifi_nan.jpg` showing the capabilities to receive the Wi-Fi NAN signals - screenshot from the AIDA64 app. + +Then edit the table listing the devices with pass or fail icons, add the month and year of the test, and lastly put the link to the folder in the Proof column. +Alternatively, create an Issue with the necessary information and we will add it to the list. + +#### I found misleading information in the list. + +Well, that might happen. +Please create an Issue for that and we will do our best to inspect it. + +#### Found a better way to test smartphones for Broadcast Remote ID capabilities? + +Any contribution is welcome! Feel free to open an Issue so we can discuss it further. + +### List of Devices and Their Capabilities + +You can find the list of tested devices in the table below. +For each device, we provide either ✅ if it passed or ⌠if it failed. +Each test contains the approximate date it happened. +It is assumed that the device was tested with the latest OS version. + +Please note that most smartphones were tested in Q1 2020 and they do not contain proof screenshots. +Therefore, their functionality may have changed since. +We plan to continuously update this list and increase the reliability of information by adding screenshot evidence. + +| Smartphone Model | Chipset | Android version | BT 5 LR Basic Support (Elimination criteria) | BT 5 LR Receiver Support | Wi-Fi Beacon | Wi-Fi NAN | Proof | Note | +| ---------------- | ------- | --------------- | -------------------------------------------- | ------------------------ | ------------ | ---------- | ----- | ---- | +| Asus Zenfone 6 | Snapdragon 855 | 11 | ✅ 1/2021 | ✅ 7/2021 | ✅ 7/2021 | ✅ 1/2021 | [Link](receiver_proofs/Asus_Zenfone6) | | +| Google Pixel 4/4XL | Snapdragon 855 | 10 | | | | ✅ 1/2020 | | | +| Google Pixel 3/3XL | Snapdragon 845 | 9 | | | | ✅ 1/2020 | | | +| Google Pixel 3A | Snapdragon 670 | 10 | ⌠1/2020 | ⌠1/2020 | | ✅ 1/2020 | | | +| Google Pixel 2/2XL | Snapdragon 835 | 9 | | | ✅ 1/2020 | ✅ 1/2020 | | | +| HMD Global Nokia 7.2 | Snapdragon 660 | 9 | ⌠1/2020 | ⌠1/2020 | | ⌠1/2020 | [Link](receiver_proofs/Nokia_7_2) | | +| HMD Global Nokia 6.2 | Snapdragon 636 | 9 | ⌠1/2020 | ⌠1/2020 | | ⌠1/2020 | [Link](receiver_proofs/Nokia_6_2) | | +| HMD Global Nokia 2.2 | MT 6761 Helio A22 | 9 | ✅ 1/2020 | ⌠1/2020 | | ⌠1/2020 | [Link](receiver_proofs/Nokia_2_2) | Long range support is claimed but the signals are never received | +| HTC one M9 | Snapdragon 810 | 7 | ⌠1/2020 | ⌠1/2020 | ⌠1/2020 | ⌠1/2020 | | | +| Huawei Mate 20 Pro | Kirin 980 | 9 | ✅ 1/2020 | ✅ 1/2020 | | ⌠1/2020 | | Receives Long Range continuously | +| Huawei Mate 20 | Kirin 980 | | ✅ 11/2019 | ✅ 11/2019 | | | | | +| Huawei Mate 10 Pro | Kirin 970 | 8 | | | ✅ 1/2020 | | | | +| Huawei Mate 9 | Kirin 960 | 7 | | | ⌠1/2020 | | | | +| Huawei P30 Pro | Kirin 980 | 10 | ✅ 11/2019 | ✅ 11/2019 | ✅ 1/2020 | | | Does this receive LR continuously or not? | +| Huawei P30 | Kirin 980 | | ✅ 11/2019 | ✅ 11/2019 | | | | Does this receive LR continuously or not? | +| Huawei P20 Lite | Kirin 659 | 9 | | | ✅ 1/2020 | | | | +| Huawei P9 | Kirin 955 | 6 | | | ⌠1/2020 | | | | +| Huawei P8 Lite | Kirin 655 | 7 | | | ⌠1/2020 | | | | +| Huawei Nova 5T | Kirin 980 | | ✅ 5/2020 | | | | | | +| Huawei Honor Magic 2 | Kirin 980 | | | | | ⌠1/2020 | | | +| Huawei Honor 10 lite | Kirin 710 | 9 | | | ✅ 1/2020 | | [Link](receiver_proofs/Huawei_Honor_10_Lite) | | +| Huawei Honor View 10 | Kirin 970 | 9 | | | ✅ 1/2020 | | | | +| Huawei Honor 8S | MT 6761 Helio A22 | 9 | ✅ 1/2020 | | | ⌠1/2020 | [Link](receiver_proofs/Huawei_Honor_8S) | Not tested but expect the same behavior as the Nokia 2.2 | +| Huawei Y6 Pro | MT 6761 Helio A22 | 5 | ⌠1/2020 | ⌠1/2020 | | ⌠1/2020 | | | +| Huawei MediaPad M5 | Kirin 960s | 9 | ⌠1/2021 | ⌠1/2021 | ✅ 1/2020 | ⌠1/2021 | [Link](receiver_proofs/Huawei_MediaPad_M5) | | +| Huawei Nexus 6P | Snapdragon 810 | 8 | | | ✅ 1/2020 | | [Link](receiver_proofs/Huawei_Nexus_6P) | | +| LG velvet 5G | Snapdragon 765G | | | | | ✅ 1/2021 | | | +| LG G8X | Snapdragon 855 | | | | | ✅ 1/2021 | | | +| LG G5 | Snapdragon 820 | 8 | ⌠1/2021 | ⌠1/2021 | ✅ 1/2020 | ⌠1/2021 | [Link](receiver_proofs/LG_G5) | | +| LG V60 | Snapdragon 865 | 10 | | | | ✅ 1/2020 | | | +| LG Nexus 5X | Snapdragon 808 | 8 | | | ✅ 1/2020 | | | | +| LG X Cam | MT 6735 | 6 | | | ⌠1/2020 | | | | +| Motorola One Vision | Exynos 9609 | 9 | ✅ 1/2020 | ⌠1/2020 | | ⌠1/2020 | [Link](receiver_proofs/Motorola_One_Vision) | Long range support is claimed but the signals are never received | +| Motorola Moto G 6 plus | Snapdragon 630 | 9 | ⌠1/2020 | ⌠1/2020 | | ⌠1/2020 | | | +| Nokia 9 Pureview | Snapdragon 845 | 9 | | | ✅ 1/2020 | | | | +| OnePlus 8T | Snapdragon 865 | 11 | ✅ 1/2021 | ✅ 1/2021 | | ⌠1/2021 | [Link](receiver_proofs/OnePlus_8T) | Long Range receive is active only part of the time | +| One Plus 7 Pro | Snapdragon 855 | 10 | ✅ 1/2020 | | | ⌠1/2020 | [Link](receiver_proofs/OnePlus_7_Pro) | Probably similar LR receive behavior as in One Plus 6T and 8T (unconfirmed) | +| One Plus 7T | Snapdragon 855+ | 10 | ✅ 1/2020 | | | ⌠1/2020 | [Link](receiver_proofs/OnePlus_7T) | Probably similar LR receive behavior as in One Plus 6T and 8T (unconfirmed) | +| One Plus 6 / 6T | Snapdragon 845 | 10 | ✅ 1/2021 | ✅ 1/2021 | ✅ 1/2020 | ⌠1/2021 | [Link](receiver_proofs/OnePlus_6), [Link](receiver_proofs/OnePlus_6T) | Long Range receive is active only part of the time | +| One Plus Nord 5G | Snapdragon 765G | 10 | ✅ 1/2021 | ✅ 1/2021 | | ⌠1/2021 | [Link](receiver_proofs/OnePlus_Nord_5G) | Receives Long Range continuously | +| One Plus N10 5G | Snapdragon 690 | 10 | ✅ 1/2021 | ✅ 1/2021 | | ⌠1/2021 | [Link](receiver_proofs/OnePlus_N10_5G) | Receives Long Range continuously | +| Razer phone 2 | Snapdragon 845 | | | | | | | | +| Samsung Galaxy Note 10, Note 10+ | Exynos 9825 | 9 | | | ✅ 4/2021 | ✅ 1/2020 | [Link](receiver_proofs/Samsung_Galaxy_Note_10), [Link](receiver_proofs/Samsung_Galaxy_Note_10+) | | +| Samsung Galaxy Note 9 (Global) | Exynos 9810 | | | | | | | | +| Samsung Galaxy Note 9 (USA, China, Japan) | Snapdragon 845 | | | | | | | | +| Samsung Galaxy Note 8 (Global) | Exynos 8895 | 9 | | | ✅ 1/2020 | | | | +| Samsung Galaxy Note 8 (USA, China, Japan) | Snapdragon 835 | | | | | | | | +| Samsung S20, S20+, S20 ultra (Global) | Exynos 990 | 10 | ✅ 1/2021 | ✅ 1/2021 | ✅ 1/2020 | ✅ 1/2020 | [Link](receiver_proofs/Samsung_Galaxy_S20_Exynos) | | +| Samsung S20, S20+, S20 ultra (USA, China, Japan) | Snapdragon 865 | 10 | ✅ 2/2021 | ✅ 2/2021 | | ✅ 2/2021 | | Receives Long Range continuously | +| Samsung Galaxy S10, S10e, S10+, S10 5G | Exynos 9820 | 10 | ✅ 1/2021 | ✅ 1/2021 | ✅ 1/2020 | ✅ 1/2020 | [Link](receiver_proofs/Samsung_Galaxy_S10_Exynos) | Receives Long Range continuously | +| Samsung Galaxy S9, S9+ (Global) | Exynos 9810 | 9 | ⌠1/2020 | ⌠1/2020 | ✅ 1/2020 | ✅ 1/2020 | | | +| Samsung Galaxy S8 | Exynos 8895 | 9 | | | ✅ 1/2020 | | | | +| Samsung Galaxy S7 | Exynos 8890 | | | | ✅ 4/2021 | | [Link]( receiver_proofs/Samsung_Galaxy_S7) | | +| Samsung Galaxy S6 | Exynos 7420 | | | | ✅ 4/2021 | | [Link](receiver_proofs/Samsung_Galaxy_S6) | | +| Samsung Galaxy A5 | Snapdragon 410 | | | | ✅ 1/2020 | | | | +| Samsung Galaxy A71 | Snapdragon 730 | 10 | ⌠1/2021 | ⌠1/2021 | | ✅ 1/2021 | [Link](receiver_proofs/Samsung_Galaxy_A71) | | +| Samsung Galaxy A8 | Exynos 7885 | | | | ✅ 4/2021 | | [Link](receiver_proofs/Samsung_Galaxy_A8) | | +| Samsung Galaxy Xcover Pro | Exynos 9611 | 10 | ⌠1/2020 | ⌠1/2020 | | ⌠1/2020 | [Link](receiver_proofs/Samsung_Galaxy_XCover_Pro) | | +| Samsung Galaxy Xcover Pro | Snapdragon 865 | 10 | | | | ✅ 1/2020 | | | +| Samsung Galaxy Tab S7, S7+ | Snapdragon 865+ | | | | | ✅ 1/2021 | | | +| Samsung Galaxy Tab S6 | Snapdragon 855 | | ✅ 6/2020 | | | | | | +| Samsung Galaxy A3 | Exynos 7870 | | ⌠1/2021 | ⌠1/2021 | | ⌠1/2021 | | | +| Sony XQ-AD52 Xperia L4 | MT6762 Helio P22 | | ✅ 1/2021 | ⌠1/2021 | | ⌠1/2020 | | | +| Sony Xperia 5 | Snapdragon 855 | | | | ✅ 4/2021 | | [Link](receiver_proofs/Sony_Xperia_5) | | +| Sony Xperia XA2 | Snapdragon 630 | 9 | ⌠1/2020 | ⌠1/2020 | | ⌠1/2020 | | | +| Sony Xperia XZ1 Compact | Snapdragon 835 | 8 | | | ✅ 1/2020 | | | | +| Sony Xperia XZ2 | Snapdragon 845 | 10 | | | ✅ 1/2020 | | | | +| Xiaomi Note 10 | Snapdragon 730G | 9 | ✅ 1/2020 | | | ✅ 1/2020 | [Link](receiver_proofs/Xiaomi_Mi_Note_10) | | +| Xiaomi Mi 9T Pro | Snapdragon 855 | 9 | ✅ 1/2020 | | | ✅ 1/2020 | [Link](receiver_proofs/Xiaomi_Mi_9T_Pro) | | +| Xiaomi Mi 9 SE | Snapdragon 712 | 9 | ✅ 1/2020 | | | ⌠1/2020 | [Link](receiver_proofs/Xiaomi_Mi_9_SE) | | +| Xiaomi Mi 9 | Snapdragon 855 | 9 | ✅ 1/2020 | ✅ 1/2020 | | ✅ 1/2020 | [Link](receiver_proofs/Xiaomi_Mi_9) | Long Range receive is active only part of the time | +| Xiaomi Mi 8 | Snapdragon 845 | 9 | | | | ✅ 1/2020 | | | +| Xiaomi Redmi Note 9s | Snapdragon 720G | | ✅ 6/2020 | | | | | | +| Xiaomi Redmi note 8 Pro | MT Helio G90T | 9 | ✅ 1/2020 | | | ⌠1/2020 | [Link](receiver_proofs/Xiaomi_Redmi_Note_8_Pro) | | +| Xiaomi Redmi note 7 Pro | Snapdragon 675 | | | | | | | | +| Xiaomi Redmi note 8T | Snapdragon 665 | 9 | ⌠1/2020 | ⌠1/2020 | | ⌠1/2020 | [Link](receiver_proofs/Xiaomi_Redmi_Note_8T) | | +| Xiaomi Redmi note 7 | Snapdragon 660 | 9 | ⌠1/2020 | ⌠1/2020 | | ⌠1/2020 | [Link](receiver_proofs/Xiaomi_Redmi_Note_7) | | +| Xiaomi Redmi CC9 Pro/Note10 Pro | Snapdragon 730G | 10 | | | | ✅ 1/2020 | | | +| Xiaomi Redmi K20 Pro | Snapdragon 855 | 9 | | | | ✅ 1/2020 | | | +| Xiaomi Mi Mix 3 | Snapdragon 845 | 9 | | | ✅ 1/2020 | ✅ 1/2020 | | | +| Xiaomi Mi A2 | Snapdragon 660 | 9 | ⌠1/2020 | ⌠1/2020 | | ⌠1/2020 | [Link](receiver_proofs/Xiaomi_Mi_A2) | |