├── lib ├── .gitignore ├── consumer-rules.pro ├── src │ ├── main │ │ ├── java │ │ │ └── org │ │ │ │ └── altbeacon │ │ │ │ ├── beacon │ │ │ │ ├── distance │ │ │ │ │ ├── DistanceCalculatorFactory.kt │ │ │ │ │ ├── DistanceCalculator.java │ │ │ │ │ ├── ModelSpecificDistanceCalculatorFactory.kt │ │ │ │ │ ├── ModelSpecificDistanceUpdater.java │ │ │ │ │ ├── CurveFittedDistanceCalculator.java │ │ │ │ │ ├── AndroidModel.java │ │ │ │ │ └── DistanceConfigFetcher.java │ │ │ │ ├── simulator │ │ │ │ │ ├── BeaconSimulator.java │ │ │ │ │ └── StaticBeaconSimulator.java │ │ │ │ ├── logging │ │ │ │ │ ├── AbstractAndroidLogger.java │ │ │ │ │ ├── EmptyLogger.java │ │ │ │ │ ├── WarningAndroidLogger.java │ │ │ │ │ ├── InfoAndroidLogger.java │ │ │ │ │ ├── VerboseAndroidLogger.java │ │ │ │ │ ├── Loggers.java │ │ │ │ │ └── ApiTrackingLogger.kt │ │ │ │ ├── service │ │ │ │ │ ├── RssiFilter.java │ │ │ │ │ ├── scanner │ │ │ │ │ │ ├── CycledLeScanCallback.java │ │ │ │ │ │ ├── CycledLeScannerForAndroidO.java │ │ │ │ │ │ └── NonBeaconLeScanCallback.java │ │ │ │ │ ├── DetectionTracker.java │ │ │ │ │ ├── MonitoringData.java │ │ │ │ │ ├── ArmaRssiFilter.java │ │ │ │ │ ├── RangingData.java │ │ │ │ │ ├── RegionMonitoringState.java │ │ │ │ │ ├── Callback.java │ │ │ │ │ ├── ExtraDataBeaconTracker.java │ │ │ │ │ ├── RunningAverageRssiFilter.java │ │ │ │ │ ├── RangeState.java │ │ │ │ │ └── RangedBeacon.java │ │ │ │ ├── RegionViewModel.kt │ │ │ │ ├── startup │ │ │ │ │ ├── BootstrapNotifier.java │ │ │ │ │ └── StartupBroadcastReceiver.java │ │ │ │ ├── client │ │ │ │ │ ├── DataProviderException.java │ │ │ │ │ ├── BeaconDataFactory.java │ │ │ │ │ └── NullBeaconDataFactory.java │ │ │ │ ├── BeaconData.java │ │ │ │ ├── BeaconRegion.kt │ │ │ │ ├── BeaconDataNotifier.java │ │ │ │ ├── powersave │ │ │ │ │ └── BackgroundPowerSaver.java │ │ │ │ ├── utils │ │ │ │ │ ├── ProcessUtils.java │ │ │ │ │ ├── ChangeAwareCopyOnWriteArrayList.kt │ │ │ │ │ ├── EddystoneTelemetryAccessor.java │ │ │ │ │ ├── PermissionsInspector.kt │ │ │ │ │ └── DozeDetector.java │ │ │ │ ├── BleNotAvailableException.java │ │ │ │ ├── RangeNotifier.java │ │ │ │ ├── BeaconConsumer.java │ │ │ │ ├── InternalBeaconConsumer.java │ │ │ │ ├── BeaconIntentProcessor.java │ │ │ │ ├── MonitorNotifier.java │ │ │ │ ├── BeaconLocalBroadcastProcessor.java │ │ │ │ ├── AltBeaconParser.java │ │ │ │ ├── IntentHandler.java │ │ │ │ └── AltBeacon.java │ │ │ │ └── bluetooth │ │ │ │ ├── BleAdvertisement.java │ │ │ │ └── Pdu.java │ │ ├── resources │ │ │ └── model-distance-calculations.json │ │ └── AndroidManifest.xml │ └── test │ │ ├── java │ │ └── org │ │ │ └── altbeacon │ │ │ ├── beacon │ │ │ ├── service │ │ │ │ ├── ArmaRssiFilterTest.java │ │ │ │ ├── ScanStateTest.java │ │ │ │ ├── BeaconServiceTest.java │ │ │ │ ├── RunningAverageRssiFilterTest.java │ │ │ │ ├── MonitoringStatusTest.java │ │ │ │ └── RangingDataTest.java │ │ │ ├── SettingsJavaTest.java │ │ │ ├── logging │ │ │ │ ├── LoggersTest.java │ │ │ │ ├── LogManagerTest.java │ │ │ │ ├── VerboseAndroidLoggerTest.java │ │ │ │ └── WarningAndroidLoggerTest.java │ │ │ ├── utils │ │ │ │ └── EddystoneTelemetryAccessorTest.java │ │ │ ├── org │ │ │ │ └── altbeacon │ │ │ │ │ └── beacon │ │ │ │ │ └── simulator │ │ │ │ │ └── BeaconSimulatorTest.java │ │ │ ├── BeaconManagerTest.java │ │ │ ├── BeaconTransmitterTest.java │ │ │ ├── OverflowAreaBeaconTest.java │ │ │ ├── distance │ │ │ │ └── ModelSpecificDistanceCalculatorTest.java │ │ │ └── AltBeaconParserTest.java │ │ │ └── bluetooth │ │ │ └── BleAdvertisementTest.java │ │ ├── resources │ │ └── model-distance-calculations.json │ │ └── AndroidManifest.xml └── build.gradle ├── gradle ├── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── credentials.gradle └── oldpackage.gradle ├── settings.gradle ├── STATUS ├── proguard-rules.pro ├── NOTICE ├── circle.yml ├── .github └── ISSUE_TEMPLATE.md ├── .gitignore ├── gradle.properties ├── .circleci └── config.yml ├── gradlew.bat ├── CONTRIBUTING.md └── README.md /lib/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /lib/consumer-rules.pro: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AltBeacon/android-beacon-library/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':lib' 2 | project(":").name = "android-beacon-library-root" 3 | project(":lib").name = "android-beacon-library" 4 | -------------------------------------------------------------------------------- /STATUS: -------------------------------------------------------------------------------- 1 | Tests are failing 2 | build with this 3 | export JAVA_HOME=/Applications/Android\ Studio\ 2.app/Contents/jbr/Contents/Home; 4 | ./gradlew release -x test 5 | -------------------------------------------------------------------------------- /proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # These proguard rules will be used in apps that use obfuscation automatically. 2 | 3 | #AltBeacon 4 | -dontwarn org.altbeacon.** 5 | -keep class org.altbeacon.** { *; } 6 | -keep interface org.altbeacon.** { *; } 7 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Android Beacon Library 2 | Copyright 2014-2021 Radius Networks 3 | Copyright 2019-2021 Young Consulting 4 | 5 | This product includes software developed at 6 | Radius Networks (http://www.radiusnetworks.com/). 7 | Young Consulting (http://davidgyoungtech.com). 8 | -------------------------------------------------------------------------------- /lib/src/main/java/org/altbeacon/beacon/distance/DistanceCalculatorFactory.kt: -------------------------------------------------------------------------------- 1 | package org.altbeacon.beacon.distance 2 | 3 | import android.content.Context 4 | 5 | public interface DistanceCalculatorFactory { 6 | fun getInstance(context: Context): DistanceCalculator 7 | } -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sat Apr 03 11:24:35 EDT 2021 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip 7 | -------------------------------------------------------------------------------- /circle.yml: -------------------------------------------------------------------------------- 1 | machine: 2 | java: 3 | version: openjdk17 4 | dependencies: 5 | pre: 6 | - echo y | android update sdk --no-ui --all --filter "tools,android-31,build-tools-30.0.3,platform-tools,extra-android-m2repository,extra-google-m2repository" 7 | general: 8 | branches: 9 | ignore: 10 | 11 | -------------------------------------------------------------------------------- /lib/src/main/java/org/altbeacon/beacon/simulator/BeaconSimulator.java: -------------------------------------------------------------------------------- 1 | package org.altbeacon.beacon.simulator; 2 | 3 | import org.altbeacon.beacon.Beacon; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * Created by dyoung on 4/18/14. 9 | */ 10 | public interface BeaconSimulator { 11 | public List getBeacons(); 12 | } 13 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### Expected behavior 2 | 3 | ### Actual behavior 4 | 5 | ### Steps to reproduce this behavior 6 | 7 | ### Mobile device model and OS version 8 | 9 | ### Android Beacon Library version 10 | 11 | IMPORTANT: This forum is reserved for feature requests or reproducible bugs with the library itself. If you need help with using the library with your project, please open a new question on StackOverflow.com. 12 | -------------------------------------------------------------------------------- /lib/src/main/java/org/altbeacon/beacon/logging/AbstractAndroidLogger.java: -------------------------------------------------------------------------------- 1 | package org.altbeacon.beacon.logging; 2 | 3 | abstract class AbstractAndroidLogger implements Logger { 4 | protected String formatString(String message, Object... args) { 5 | // If no varargs are supplied, treat it as a request to log the string without formatting. 6 | return args.length == 0 ? message : String.format(message, args); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /lib/src/main/java/org/altbeacon/beacon/service/RssiFilter.java: -------------------------------------------------------------------------------- 1 | package org.altbeacon.beacon.service; 2 | 3 | /** 4 | * Interface that can be implemented to overwrite measurement and filtering 5 | * of RSSI values 6 | */ 7 | public interface RssiFilter { 8 | 9 | public void addMeasurement(Integer rssi); 10 | public boolean noMeasurementsAvailable(); 11 | public double calculateRssi(); 12 | public int getMeasurementCount(); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /lib/src/main/java/org/altbeacon/beacon/RegionViewModel.kt: -------------------------------------------------------------------------------- 1 | package org.altbeacon.beacon 2 | 3 | import androidx.lifecycle.MutableLiveData 4 | import androidx.lifecycle.ViewModel 5 | 6 | class RegionViewModel: ViewModel() { 7 | val regionState: MutableLiveData by lazy { 8 | MutableLiveData() 9 | } 10 | val rangedBeacons: MutableLiveData> by lazy { 11 | MutableLiveData>() 12 | } 13 | } -------------------------------------------------------------------------------- /lib/src/main/java/org/altbeacon/beacon/distance/DistanceCalculator.java: -------------------------------------------------------------------------------- 1 | package org.altbeacon.beacon.distance; 2 | 3 | /** 4 | * Interface for a class that can estimate the distance between a mobile 5 | * device and a beacon based on the measured RSSI and a reference txPower 6 | * calibration value. 7 | * 8 | * Created by dyoung on 8/28/14. 9 | */ 10 | public interface DistanceCalculator { 11 | public double calculateDistance(int txPower, double rssi); 12 | } 13 | -------------------------------------------------------------------------------- /lib/src/main/java/org/altbeacon/beacon/startup/BootstrapNotifier.java: -------------------------------------------------------------------------------- 1 | package org.altbeacon.beacon.startup; 2 | 3 | import android.content.Context; 4 | 5 | import org.altbeacon.beacon.MonitorNotifier; 6 | 7 | /** 8 | * @deprecated Will be removed in 3.0. See http://altbeacon.github.io/android-beacon-library/autobind.html 9 | */ 10 | @Deprecated 11 | public interface BootstrapNotifier extends MonitorNotifier { 12 | public Context getApplicationContext(); 13 | } 14 | -------------------------------------------------------------------------------- /lib/src/main/java/org/altbeacon/beacon/client/DataProviderException.java: -------------------------------------------------------------------------------- 1 | package org.altbeacon.beacon.client; 2 | 3 | public class DataProviderException extends Exception { 4 | 5 | /** 6 | * 7 | */ 8 | private static final long serialVersionUID = -2574842662565384114L; 9 | public DataProviderException() { 10 | super(); 11 | } 12 | public DataProviderException(String msg) { 13 | super(msg); 14 | } 15 | public DataProviderException(String msg, Throwable t) { 16 | super(msg, t); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /lib/src/main/java/org/altbeacon/beacon/distance/ModelSpecificDistanceCalculatorFactory.kt: -------------------------------------------------------------------------------- 1 | package org.altbeacon.beacon.distance 2 | 3 | import android.content.Context 4 | import org.altbeacon.beacon.BeaconManager 5 | 6 | class ModelSpecificDistanceCalculatorFactory: DistanceCalculatorFactory { 7 | override fun getInstance(context: Context): DistanceCalculator { 8 | val updateUrl = BeaconManager.getInstanceForApplication(context).activeSettings.distanceModelUpdateUrl 9 | return ModelSpecificDistanceCalculator(context, updateUrl) 10 | } 11 | } -------------------------------------------------------------------------------- /lib/src/main/java/org/altbeacon/beacon/simulator/StaticBeaconSimulator.java: -------------------------------------------------------------------------------- 1 | package org.altbeacon.beacon.simulator; 2 | 3 | import org.altbeacon.beacon.Beacon; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * Created by dyoung on 4/18/14. 9 | */ 10 | public class StaticBeaconSimulator implements BeaconSimulator { 11 | 12 | public List beacons = null; 13 | 14 | @Override 15 | public List getBeacons() { 16 | return beacons; 17 | } 18 | public void setBeacons(List beacons) { 19 | this.beacons = beacons; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lib/src/main/java/org/altbeacon/beacon/BeaconData.java: -------------------------------------------------------------------------------- 1 | package org.altbeacon.beacon; 2 | 3 | /** 4 | * Server-side data associated with a beacon. Requires registration of a web service to fetch the 5 | * data. 6 | */ 7 | public interface BeaconData { 8 | public Double getLatitude(); 9 | public void setLatitude(Double latitude); 10 | public void setLongitude(Double longitude); 11 | public Double getLongitude(); 12 | public String get(String key); 13 | public void set(String key, String value); 14 | public void sync(BeaconDataNotifier notifier); 15 | public boolean isDirty(); 16 | } 17 | -------------------------------------------------------------------------------- /lib/src/main/java/org/altbeacon/beacon/BeaconRegion.kt: -------------------------------------------------------------------------------- 1 | package org.altbeacon.beacon 2 | 3 | class BeaconRegion(uniqueId: String, beaconParser: BeaconParser?, identifiers: List, bluetoothAddress: String?): Region(uniqueId, beaconParser, identifiers, bluetoothAddress, 3) { 4 | constructor(uniqueId: String, beaconParser: BeaconParser, id1: String?, id2: String?, id3: String?) : this(uniqueId, beaconParser, listOf(Identifier.parse(id1), Identifier.parse(id2), Identifier.parse(id3)), null) { 5 | } 6 | constructor(uniqueId: String, macAddress: String) : this(uniqueId, null, arrayListOf(), macAddress) { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /lib/src/main/java/org/altbeacon/beacon/service/scanner/CycledLeScanCallback.java: -------------------------------------------------------------------------------- 1 | package org.altbeacon.beacon.service.scanner; 2 | 3 | import android.bluetooth.BluetoothDevice; 4 | import androidx.annotation.MainThread; 5 | 6 | /** 7 | * Android API agnostic Bluetooth scan callback wrapper. 8 | *

9 | * Since Android bluetooth scan callbacks occur on the main thread it is expected that these 10 | * callbacks will also occur on the main thread. 11 | * 12 | * Created by dyoung on 10/6/14. 13 | */ 14 | @MainThread 15 | public interface CycledLeScanCallback { 16 | void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord, long timestampMs); 17 | void onCycleEnd(); 18 | } 19 | -------------------------------------------------------------------------------- /lib/src/test/java/org/altbeacon/beacon/service/ArmaRssiFilterTest.java: -------------------------------------------------------------------------------- 1 | package org.altbeacon.beacon.service; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.robolectric.RobolectricTestRunner; 6 | import org.robolectric.annotation.Config; 7 | import static org.junit.Assert.assertEquals; 8 | 9 | @RunWith(RobolectricTestRunner.class) 10 | @Config(sdk = 28) 11 | 12 | public class ArmaRssiFilterTest { 13 | 14 | @Test 15 | public void initTest1() { 16 | ArmaRssiFilter filter = new ArmaRssiFilter(); 17 | filter.addMeasurement(-50); 18 | assertEquals("First measurement should be -50", String.valueOf(filter.calculateRssi()), "-50.0"); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /gradle/credentials.gradle: -------------------------------------------------------------------------------- 1 | ext.bintrayUsername = project.hasProperty('bintrayUsername') ? project.getProperty('bintrayUsername') : System.getenv('BINTRAY_USER') ?: '' 2 | ext.bintrayKey = project.hasProperty('bintrayKey') ? project.getProperty('bintrayKey') : System.getenv('BINTRAY_KEY') ?: '' 3 | ext.signingPassword = project.hasProperty('signingPassword') ? project.getProperty('signingPassword') : System.getenv('SIGNING_PASSWORD') ?: '' 4 | ext.sonatypeUserName = project.hasProperty('sonatypeUserName') ? project.getProperty('sonatypeUserName') : System.getenv('SONATYPE_USER_NAME') ?: '' 5 | ext.sonatypePassword = project.hasProperty('sonatypePassword') ? project.getProperty('sonatypePassword') : System.getenv('SONATYPE_PASSWORD') ?: '' 6 | -------------------------------------------------------------------------------- /lib/src/main/java/org/altbeacon/beacon/service/DetectionTracker.java: -------------------------------------------------------------------------------- 1 | package org.altbeacon.beacon.service; 2 | 3 | import android.os.SystemClock; 4 | 5 | /** 6 | * Created by dyoung on 1/10/15. 7 | */ 8 | public class DetectionTracker { 9 | private static final DetectionTracker INSTANCE = new DetectionTracker(); 10 | 11 | private long mLastDetectionTime = 0l; 12 | private DetectionTracker() { 13 | 14 | } 15 | public static DetectionTracker getInstance() { 16 | return INSTANCE; 17 | } 18 | public long getLastDetectionTime() { 19 | return mLastDetectionTime; 20 | } 21 | public void recordDetection() { 22 | mLastDetectionTime = SystemClock.elapsedRealtime(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/src/main/java/org/altbeacon/beacon/BeaconDataNotifier.java: -------------------------------------------------------------------------------- 1 | package org.altbeacon.beacon; 2 | 3 | import org.altbeacon.beacon.client.DataProviderException; 4 | 5 | /** 6 | * Notifies when server-side beacon data are available from a web service. 7 | */ 8 | public interface BeaconDataNotifier { 9 | /** 10 | * This method is called after a request to get or sync beacon data 11 | * If fetching data was successful, the data is returned and the exception is null. 12 | * If fetching of the data is not successful, an exception is provided. 13 | * @param beacon 14 | * @param data 15 | * @param exception 16 | */ 17 | public void beaconDataUpdate(Beacon beacon, BeaconData data, DataProviderException exception); 18 | } 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # files for the dex VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # generated files 12 | bin/ 13 | gen/ 14 | doc/ 15 | build/ 16 | src/main/gen 17 | 18 | # Local configuration file (sdk path, etc) 19 | local.properties 20 | 21 | # Proguard folder generated by Eclipse 22 | proguard/ 23 | 24 | # Intellij project files 25 | *.iml 26 | *.ipr 27 | *.iws 28 | .idea/ 29 | 30 | # Per https://intellij-support.jetbrains.com/entries/23393067 31 | .idea/workspace.xml 32 | .idea/tasks.xml 33 | .idea/gradle.xml 34 | .idea/libraries/*.xml 35 | .idea/dictionaries/*.xml 36 | 37 | # Other 38 | .gradle 39 | /local.properties 40 | /.idea/workspace.xml 41 | .DS_Store 42 | .directory 43 | .metadata 44 | -------------------------------------------------------------------------------- /lib/src/main/java/org/altbeacon/beacon/client/BeaconDataFactory.java: -------------------------------------------------------------------------------- 1 | package org.altbeacon.beacon.client; 2 | 3 | import org.altbeacon.beacon.Beacon; 4 | import org.altbeacon.beacon.BeaconDataNotifier; 5 | 6 | /** 7 | * This can be configured for the public beacon data store, or a private beacon data store. 8 | * In the public data store, you can read any value but only write to the values to the beacons you created 9 | * 10 | * @author dyoung 11 | * 12 | */ 13 | public interface BeaconDataFactory { 14 | /** 15 | * Asynchronous call 16 | * When data is available, it is passed back to the beaconDataNotifier interface 17 | * @param beacon 18 | */ 19 | public void requestBeaconData(Beacon beacon, BeaconDataNotifier notifier); 20 | } 21 | 22 | -------------------------------------------------------------------------------- /lib/src/main/java/org/altbeacon/beacon/client/NullBeaconDataFactory.java: -------------------------------------------------------------------------------- 1 | package org.altbeacon.beacon.client; 2 | 3 | import android.os.Handler; 4 | 5 | import org.altbeacon.beacon.Beacon; 6 | import org.altbeacon.beacon.BeaconDataNotifier; 7 | 8 | public class NullBeaconDataFactory implements BeaconDataFactory { 9 | 10 | @Override 11 | public void requestBeaconData(Beacon beacon, final BeaconDataNotifier notifier) { 12 | final Handler handler = new Handler(); 13 | handler.post(new Runnable() { 14 | @Override 15 | public void run() { 16 | notifier.beaconDataUpdate(null, null, new DataProviderException("You need to configure a beacon data service to use this feature.")); 17 | } 18 | }); 19 | } 20 | } 21 | 22 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 2 | # AndroidX package structure to make it clearer which packages are bundled with the 3 | # Android operating system, and which are packaged with your app"s APK 4 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 5 | android.useAndroidX=true 6 | # Automatically convert third-party libraries to use AndroidX 7 | android.enableJetifier=true 8 | 9 | project_vendor = altbeacon 10 | project_vendor_name = altbeacon 11 | project_description = Allows Android apps to interact with BLE beacons 12 | project_url = https://github.com/AltBeacon/android-beacon-library 13 | project_scm = https://github.com/AltBeacon/android-beacon-library 14 | project_issues_url = https://github.com/AltBeacon/android-beacon-library/issues 15 | project_connection = scm:git:https://github.com/AltBeacon/android-beacon-library.git 16 | project_dev_connection = scm:git:git@github.com:AltBeacon/android-beacon-library.git 17 | 18 | -------------------------------------------------------------------------------- /lib/src/test/resources/model-distance-calculations.json: -------------------------------------------------------------------------------- 1 | { 2 | "models": 3 | [ 4 | { 5 | "coefficient1": 0.89976, 6 | "coefficient2": 7.7095, 7 | "coefficient3": 0.111, 8 | "version":"4.4.2", 9 | "build_number":"KOT49H", 10 | "model":"Nexus 4", 11 | "manufacturer":"LGE" 12 | }, 13 | { 14 | "coefficient1": 0.42093, 15 | "coefficient2": 6.9476, 16 | "coefficient3": 0.54992, 17 | "version":"4.4.2", 18 | "build_number":"LPV79", 19 | "model":"Nexus 5", 20 | "manufacturer":"LGE", 21 | "default": true 22 | }, 23 | { 24 | "coefficient1": 0.9401940951, 25 | "coefficient2": 6.170094565, 26 | "coefficient3": 0.0, 27 | "version":"5.0.2", 28 | "build_number":"LXG22.67-7.1", 29 | "model":"Moto X Pro", 30 | "manufacturer":"XT1115", 31 | "default": false 32 | }, 33 | { 34 | "coefficient1": 0.1862616782, 35 | "coefficient2": 8.235367435, 36 | "coefficient3": -0.45324519, 37 | "version":"6.0", 38 | "build_number":"MPE24.49-18", 39 | "model":"XT1092", 40 | "manufacturer":"Motorola", 41 | "default": false 42 | } 43 | ] 44 | } 45 | -------------------------------------------------------------------------------- /lib/src/main/resources/model-distance-calculations.json: -------------------------------------------------------------------------------- 1 | { 2 | "models": 3 | [ 4 | { 5 | "coefficient1": 0.42093, 6 | "coefficient2": 6.9476, 7 | "coefficient3": 0.54992, 8 | "version":"4.4.2", 9 | "build_number":"KOT49H", 10 | "model":"Nexus 4", 11 | "manufacturer":"LGE" 12 | }, 13 | { 14 | "coefficient1": 0.42093, 15 | "coefficient2": 6.9476, 16 | "coefficient3": 0.54992, 17 | "version":"4.4.2", 18 | "build_number":"LPV79", 19 | "model":"Nexus 5", 20 | "manufacturer":"LGE", 21 | "default": true 22 | }, 23 | { 24 | "coefficient1": 0.9401940951, 25 | "coefficient2": 6.170094565, 26 | "coefficient3": 0.0, 27 | "version":"5.0.2", 28 | "build_number":"LXG22.67-7.1", 29 | "model":"Moto X Pro", 30 | "manufacturer":"XT1115", 31 | "default": false 32 | }, 33 | { 34 | "coefficient1": 0.1862616782, 35 | "coefficient2": 8.235367435, 36 | "coefficient3": -0.45324519, 37 | "version":"6.0", 38 | "build_number":"MPE24.49-18", 39 | "model":"XT1092", 40 | "manufacturer":"Motorola", 41 | "default": false 42 | } 43 | ] 44 | } 45 | -------------------------------------------------------------------------------- /lib/src/main/java/org/altbeacon/beacon/powersave/BackgroundPowerSaver.java: -------------------------------------------------------------------------------- 1 | package org.altbeacon.beacon.powersave; 2 | 3 | import android.annotation.TargetApi; 4 | import android.app.Activity; 5 | import android.app.Application; 6 | import android.content.Context; 7 | import android.os.Bundle; 8 | import androidx.annotation.NonNull; 9 | 10 | import org.altbeacon.beacon.BeaconManager; 11 | import org.altbeacon.beacon.logging.LogManager; 12 | 13 | /** 14 | * Simply creating an instance of this class and holding a reference to it in your Application can 15 | * improve battery life by 60% by slowing down scans when your app is in the background. 16 | * @deprecated Will be removed in 3.0. See http://altbeacon.github.io/android-beacon-library/autobind.html 17 | */ 18 | @Deprecated 19 | @TargetApi(18) 20 | public class BackgroundPowerSaver extends BackgroundPowerSaverInternal { 21 | /** 22 | * Constructs a new BackgroundPowerSaver using the default background determination strategy 23 | * 24 | * @param context 25 | */ 26 | public BackgroundPowerSaver(Context context) { 27 | super(context); 28 | } 29 | @Deprecated 30 | public BackgroundPowerSaver(Context context, boolean countActiveActivityStrategy) { 31 | this(context); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /lib/src/test/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 22 | 23 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /lib/src/main/java/org/altbeacon/beacon/service/scanner/CycledLeScannerForAndroidO.java: -------------------------------------------------------------------------------- 1 | package org.altbeacon.beacon.service.scanner; 2 | 3 | import android.annotation.TargetApi; 4 | import android.content.Context; 5 | import org.altbeacon.bluetooth.BluetoothCrashResolver; 6 | import java.util.Set; 7 | 8 | /** 9 | * The scanner used for Android O is effectively the same as used for JellyBeaconMr2. There is no 10 | * point in using the low power scanning APIs introduced in Lollipop, because they only work when 11 | * the app is running, effectively requiring a long running service, something newly disallowed 12 | * by Android O. The new strategy for Android O is to use a JobScheduler combined with background 13 | * scans delivered by Intents. 14 | * 15 | * @see org.altbeacon.beacon.service.ScanJob 16 | * @see org.altbeacon.beacon.service.ScanHelper#startAndroidOBackgroundScan(Set) 17 | * 18 | * Created by dyoung on 5/28/17. 19 | */ 20 | 21 | @TargetApi(26) 22 | class CycledLeScannerForAndroidO extends CycledLeScannerForLollipop { 23 | private static final String TAG = CycledLeScannerForAndroidO.class.getSimpleName(); 24 | 25 | CycledLeScannerForAndroidO(Context context, long scanPeriod, long betweenScanPeriod, boolean backgroundFlag, CycledLeScanCallback cycledLeScanCallback, BluetoothCrashResolver crashResolver) { 26 | super(context, scanPeriod, betweenScanPeriod, backgroundFlag, cycledLeScanCallback, crashResolver); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lib/src/main/java/org/altbeacon/beacon/utils/ProcessUtils.java: -------------------------------------------------------------------------------- 1 | package org.altbeacon.beacon.utils; 2 | 3 | import android.app.ActivityManager; 4 | import android.content.Context; 5 | import androidx.annotation.NonNull; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * Created by dyoung on 3/10/17. 11 | * 12 | * Internal class used to determine current process state in multi-process setups 13 | * @hide 14 | */ 15 | 16 | public class ProcessUtils { 17 | Context mContext; 18 | 19 | public ProcessUtils(@NonNull Context context) { 20 | mContext = context; 21 | } 22 | 23 | public String getProcessName() { 24 | ActivityManager manager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE); 25 | List processes = manager.getRunningAppProcesses(); 26 | if (processes != null) { 27 | for (ActivityManager.RunningAppProcessInfo processInfo : processes) { 28 | if (processInfo.pid == getPid()) { 29 | return processInfo.processName; 30 | } 31 | } 32 | } 33 | return null; 34 | } 35 | 36 | public String getPackageName() { 37 | return mContext.getApplicationContext().getPackageName(); 38 | } 39 | 40 | public int getPid() { 41 | return android.os.Process.myPid(); 42 | } 43 | 44 | public boolean isMainProcess() { 45 | return (getPackageName().equals(getProcessName())); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lib/src/main/java/org/altbeacon/beacon/BleNotAvailableException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Radius Networks, Inc. 3 | * http://www.radiusnetworks.com 4 | * 5 | * @author David G. Young 6 | * 7 | * Licensed to the Apache Software Foundation (ASF) under one 8 | * or more contributor license agreements. See the NOTICE file 9 | * distributed with this work for additional information 10 | * regarding copyright ownership. The ASF licenses this file 11 | * to you under the Apache License, Version 2.0 (the 12 | * "License"); you may not use this file except in compliance 13 | * with the License. You may obtain a copy of the License at 14 | * 15 | * http://www.apache.org/licenses/LICENSE-2.0 16 | * 17 | * Unless required by applicable law or agreed to in writing, 18 | * software distributed under the License is distributed on an 19 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 20 | * KIND, either express or implied. See the License for the 21 | * specific language governing permissions and limitations 22 | * under the License. 23 | */ 24 | package org.altbeacon.beacon; 25 | 26 | /** 27 | * Indicates that Bluetooth Low Energy is not available on this device 28 | * @see BeaconManager#checkAvailability 29 | * @author David G. Young 30 | * 31 | */ 32 | public class BleNotAvailableException extends RuntimeException { 33 | 34 | private static final long serialVersionUID = 2242747823097637729L; 35 | 36 | public BleNotAvailableException(String message) { 37 | super(message); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /lib/src/main/java/org/altbeacon/beacon/service/scanner/NonBeaconLeScanCallback.java: -------------------------------------------------------------------------------- 1 | package org.altbeacon.beacon.service.scanner; 2 | 3 | import android.bluetooth.BluetoothDevice; 4 | import androidx.annotation.WorkerThread; 5 | 6 | /** 7 | * Allows an implementation to see non-Beacon BLE devices as they are scanned. 8 | *

9 | * To use: 10 | *


11 |  * public class BeaconReferenceApplication extends Application implements ..., NonBeaconLeScanCallback {
12 |  *     public void onCreate() {
13 |  *         super.onCreate();
14 |  *         BeaconManager beaconManager = BeaconManager.getInstanceForApplication(this);
15 |  *         ...
16 |  *         beaconManager.setNonBeaconLeScanCallback(this);
17 |  *         ...
18 |  *     }
19 |  *
20 |  *     {@literal @}Override
21 |  *     public void onNonBeaconLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
22 |  *          ...
23 |  *     }
24 |  *  }
25 |  * 
26 | */ 27 | @WorkerThread 28 | public interface NonBeaconLeScanCallback { 29 | /** 30 | * NOTE: This method is NOT called on the main UI thread. 31 | * 32 | * @param device Identifies the remote device 33 | * @param rssi The RSSI value for the remote device as reported by the 34 | * Bluetooth hardware. 0 if no RSSI value is available. 35 | * @param scanRecord The content of the advertisement record offered by 36 | * the remote device. 37 | */ 38 | void onNonBeaconLeScan(BluetoothDevice device, int rssi, byte[] scanRecord); 39 | } 40 | -------------------------------------------------------------------------------- /lib/src/main/java/org/altbeacon/bluetooth/BleAdvertisement.java: -------------------------------------------------------------------------------- 1 | package org.altbeacon.bluetooth; 2 | 3 | import android.util.Log; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | /** 9 | * Parses a byte array representing a BLE advertisement into 10 | * a number of "Payload Data Units" (PDUs). 11 | * 12 | * Created by dyoung on 4/14/15. 13 | */ 14 | public class BleAdvertisement { 15 | private static final String TAG = "BleAdvertisement"; 16 | private List mPdus; 17 | private byte[] mBytes; 18 | public BleAdvertisement(byte[] bytes) { 19 | mBytes = bytes; 20 | ArrayList pdus = new ArrayList(); 21 | // Get PDUs from the main advert 22 | parsePdus(0, bytes.length < 31 ? bytes.length : 31, pdus); 23 | // Get PDUs from the scan response 24 | // Android puts the scan response at offset 31 25 | if (bytes.length > 31) { 26 | parsePdus(31, bytes.length, pdus); 27 | } 28 | mPdus = pdus; 29 | } 30 | private void parsePdus(int startIndex, int endIndex, ArrayList pdus) { 31 | int index = startIndex; 32 | Pdu pdu = null; 33 | do { 34 | pdu = Pdu.parse(mBytes, index); 35 | if (pdu != null) { 36 | index = index + pdu.getDeclaredLength()+1; 37 | pdus.add(pdu); 38 | } 39 | } 40 | while (pdu != null && index < endIndex); 41 | } 42 | 43 | /** 44 | * The list of PDUs inside the advertisement 45 | * @return 46 | */ 47 | public List getPdus() { 48 | return mPdus; 49 | } 50 | } -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # Execute unit tests on Circle CI. 2 | # 3 | # References: 4 | # https://circleci.com/developer/orbs/orb/circleci/android 5 | # https://circleci.com/blog/building-android-on-circleci 6 | 7 | version: 2.1 8 | 9 | orbs: 10 | android: circleci/android@2.0.0 11 | 12 | jobs: 13 | build: 14 | working_directory: ~/code 15 | executor: 16 | name: android/android-machine 17 | tag: 2021.10.1 18 | environment: 19 | JVM_OPTS: -Xmx3200m -Dfile.encoding=utf-8 20 | steps: 21 | - checkout 22 | - run: 23 | name: Install OpenJDK 17 24 | command: | 25 | sudo apt-get update && sudo apt-get install openjdk-17-jdk 26 | sudo update-alternatives --set java /usr/lib/jvm/java-17-openjdk-amd64/bin/java 27 | sudo update-alternatives --set javac /usr/lib/jvm/java-17-openjdk-amd64/bin/javac 28 | echo 'export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64/' >> ~/.circlerc 29 | - run: 30 | name: Display version 31 | command: ./gradlew --version 32 | - android/restore-gradle-cache 33 | - android/restore-build-cache 34 | - android/run-tests: 35 | test-command: ./gradlew testRelease 36 | - android/save-gradle-cache 37 | - android/save-build-cache 38 | - run: 39 | name: Save test results 40 | command: | 41 | mkdir -p ~/test-results/junit/ 42 | find . -type f -regex ".*/build/test-results/.*xml" -exec cp {} ~/test-results/junit/ \; 43 | when: always 44 | - store_test_results: 45 | path: ~/test-results 46 | - store_artifacts: 47 | path: ~/test-results/junit 48 | -------------------------------------------------------------------------------- /lib/src/test/java/org/altbeacon/beacon/service/ScanStateTest.java: -------------------------------------------------------------------------------- 1 | package org.altbeacon.beacon.service; 2 | 3 | /** 4 | * Created by dyoung on 7/30/17. 5 | */ 6 | 7 | import android.annotation.TargetApi; 8 | import android.content.Context; 9 | import android.os.Build; 10 | 11 | import org.altbeacon.beacon.BeaconManager; 12 | import org.altbeacon.beacon.logging.LogManager; 13 | import org.altbeacon.beacon.logging.Loggers; 14 | import org.junit.Before; 15 | import org.junit.Test; 16 | import org.junit.runner.RunWith; 17 | import org.robolectric.RobolectricTestRunner; 18 | import org.robolectric.RuntimeEnvironment; 19 | import org.robolectric.annotation.Config; 20 | 21 | import static org.junit.Assert.assertEquals; 22 | 23 | /** 24 | * Created by dyoung on 7/1/15. 25 | */ 26 | @RunWith(RobolectricTestRunner.class) 27 | @Config(sdk = 28) 28 | public class ScanStateTest { 29 | 30 | @Before 31 | public void before() { 32 | org.robolectric.shadows.ShadowLog.stream = System.err; 33 | LogManager.setLogger(Loggers.verboseLogger()); 34 | LogManager.setVerboseLoggingEnabled(true); 35 | BeaconManager.setsManifestCheckingDisabled(true); 36 | } 37 | 38 | @Test 39 | public void serializationTest() throws Exception { 40 | Context context = RuntimeEnvironment.application; 41 | ScanState scanState = new ScanState(context); 42 | MonitoringStatus monitoringStatus = new MonitoringStatus(context); 43 | scanState.setMonitoringStatus(monitoringStatus); 44 | scanState.setLastScanStartTimeMillis(1234); 45 | scanState.save(); 46 | ScanState scanState2 = ScanState.restore(context); 47 | assertEquals("Scan start time should be restored", 48 | scanState.getLastScanStartTimeMillis(), scanState2.getLastScanStartTimeMillis()); 49 | } 50 | } -------------------------------------------------------------------------------- /lib/src/main/java/org/altbeacon/beacon/utils/ChangeAwareCopyOnWriteArrayList.kt: -------------------------------------------------------------------------------- 1 | package org.altbeacon.beacon.utils 2 | 3 | import android.os.Build 4 | import androidx.annotation.RequiresApi 5 | import java.util.ArrayList 6 | import java.util.function.Predicate 7 | 8 | class ChangeAwareCopyOnWriteArrayList: ArrayList() { 9 | var notifier: ChangeAwareCopyOnWriteArrayListNotifier? = null 10 | 11 | override fun add(element: E): Boolean { 12 | val result = super.add(element) 13 | notifier?.onChange() 14 | return result 15 | } 16 | 17 | override fun remove(element: E): Boolean { 18 | val result = super.remove(element) 19 | notifier?.onChange() 20 | return result 21 | } 22 | 23 | override fun clear() { 24 | super.clear() 25 | notifier?.onChange() 26 | } 27 | 28 | override fun addAll(elements: Collection): Boolean { 29 | val result = super.addAll(elements) 30 | notifier?.onChange() 31 | return result 32 | } 33 | 34 | override fun removeAll(elements: Collection): Boolean { 35 | val result = super.removeAll(elements) 36 | notifier?.onChange() 37 | return result 38 | } 39 | 40 | @RequiresApi(Build.VERSION_CODES.N) 41 | override fun removeIf(filter: Predicate): Boolean { 42 | val result = super.removeIf(filter) 43 | notifier?.onChange() 44 | return result 45 | } 46 | 47 | override fun removeRange(fromIndex: Int, toIndex: Int) { 48 | super.removeRange(fromIndex, toIndex) 49 | notifier?.onChange() 50 | } 51 | 52 | override fun set(index: Int, element: E): E { 53 | val result = super.set(index, element) 54 | notifier?.onChange() 55 | return result 56 | } 57 | } 58 | 59 | interface ChangeAwareCopyOnWriteArrayListNotifier { 60 | fun onChange() 61 | } -------------------------------------------------------------------------------- /lib/src/main/java/org/altbeacon/beacon/RangeNotifier.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Radius Networks, Inc. 3 | * http://www.radiusnetworks.com 4 | * 5 | * @author David G. Young 6 | * 7 | * Licensed to the Apache Software Foundation (ASF) under one 8 | * or more contributor license agreements. See the NOTICE file 9 | * distributed with this work for additional information 10 | * regarding copyright ownership. The ASF licenses this file 11 | * to you under the Apache License, Version 2.0 (the 12 | * "License"); you may not use this file except in compliance 13 | * with the License. You may obtain a copy of the License at 14 | * 15 | * http://www.apache.org/licenses/LICENSE-2.0 16 | * 17 | * Unless required by applicable law or agreed to in writing, 18 | * software distributed under the License is distributed on an 19 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 20 | * KIND, either express or implied. See the License for the 21 | * specific language governing permissions and limitations 22 | * under the License. 23 | */ 24 | package org.altbeacon.beacon; 25 | 26 | import java.util.Collection; 27 | /** 28 | * This interface is implemented by classes that receive beacon ranging notifications 29 | * 30 | * @see BeaconManager#setRangeNotifier(RangeNotifier notifier) 31 | * @see BeaconManager#startRangingBeaconsInRegion(Region region) 32 | * @see Region 33 | * @see Beacon 34 | * 35 | * @author David G. Young 36 | * 37 | */ 38 | public interface RangeNotifier { 39 | /** 40 | * Called once per second to give an estimate of the mDistance to visible beacons 41 | * @param beacons a collection of Beacon objects that have been seen in the past second 42 | * @param region the Region object that defines the criteria for the ranged beacons 43 | */ 44 | public void didRangeBeaconsInRegion(Collection beacons, Region region); 45 | } 46 | -------------------------------------------------------------------------------- /lib/src/main/java/org/altbeacon/beacon/logging/EmptyLogger.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Radius Networks, Inc. 3 | * Copyright 2015 Andrew Reitz 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.altbeacon.beacon.logging; 18 | 19 | /** 20 | * A logger that doesn't do anything. 21 | * 22 | * @author Android Reitz 23 | * @since 2.2 24 | */ 25 | final class EmptyLogger implements Logger { 26 | 27 | @Override 28 | public void v(String tag, String message, Object... args) { 29 | 30 | } 31 | 32 | @Override 33 | public void v(Throwable t, String tag, String message, Object... args) { 34 | 35 | } 36 | 37 | @Override 38 | public void d(String tag, String message, Object... args) { 39 | 40 | } 41 | 42 | @Override 43 | public void d(Throwable t, String tag, String message, Object... args) { 44 | 45 | } 46 | 47 | @Override 48 | public void i(String tag, String message, Object... args) { 49 | 50 | } 51 | 52 | @Override 53 | public void i(Throwable t, String tag, String message, Object... args) { 54 | 55 | } 56 | 57 | @Override 58 | public void w(String tag, String message, Object... args) { 59 | 60 | } 61 | 62 | @Override 63 | public void w(Throwable t, String tag, String message, Object... args) { 64 | 65 | } 66 | 67 | @Override 68 | public void e(String tag, String message, Object... args) { 69 | 70 | } 71 | 72 | @Override 73 | public void e(Throwable t, String tag, String message, Object... args) { 74 | 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /lib/src/test/java/org/altbeacon/beacon/SettingsJavaTest.java: -------------------------------------------------------------------------------- 1 | package org.altbeacon.beacon; 2 | 3 | import android.app.Notification; 4 | import android.content.Context; 5 | 6 | import androidx.annotation.NonNull; 7 | 8 | import org.altbeacon.beacon.logging.LogManager; 9 | import org.altbeacon.beacon.logging.Loggers; 10 | import org.junit.Before; 11 | import org.junit.Test; 12 | import org.junit.runner.RunWith; 13 | import org.junit.runner.manipulation.Ordering; 14 | import org.robolectric.RobolectricTestRunner; 15 | import org.robolectric.RuntimeEnvironment; 16 | import org.robolectric.annotation.Config; 17 | 18 | @RunWith(RobolectricTestRunner.class) 19 | @Config(sdk = 28) 20 | public class SettingsJavaTest { 21 | 22 | @Before 23 | public void before() { 24 | org.robolectric.shadows.ShadowLog.stream = System.err; 25 | LogManager.setLogger(Loggers.verboseLogger()); 26 | LogManager.setVerboseLoggingEnabled(true); 27 | BeaconManager.setsManifestCheckingDisabled(true); 28 | } 29 | 30 | @Test 31 | public void setSettingsTest() throws Exception { 32 | Context context = RuntimeEnvironment.getApplication(); 33 | BeaconManager beaconManager = BeaconManager 34 | .getInstanceForApplication(context); 35 | 36 | 37 | Settings settings = new Settings.Builder() 38 | .setDebug(true) 39 | .setDistanceModelUpdateUrl("https://s3.amazonaws.com/android-beacon-library/android-distance.json") 40 | .setScanPeriods(new Settings.ScanPeriods(1100, 0, 10000, 0)) 41 | .setScanStrategy(new Settings.ForegroundServiceScanStrategy( 42 | new Notification.Builder(context, "BeaconReferenceApp").build(),1) 43 | ) 44 | .setLongScanForcingEnabled(true) 45 | .build(); 46 | beaconManager.adjustSettings(settings); 47 | //beaconManager.getActiveSettings().setDebug(false); 48 | beaconManager.getActiveSettings().getScanPeriods().getBackgroundScanPeriodMillis(); 49 | Settings.Defaults.INSTANCE.getScanPeriods().getBackgroundScanPeriodMillis(); 50 | 51 | 52 | } 53 | } -------------------------------------------------------------------------------- /lib/src/test/java/org/altbeacon/bluetooth/BleAdvertisementTest.java: -------------------------------------------------------------------------------- 1 | package org.altbeacon.bluetooth; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.robolectric.RobolectricTestRunner; 6 | 7 | import static org.junit.Assert.assertEquals; 8 | 9 | import org.robolectric.annotation.Config; 10 | 11 | @Config(sdk = 28) 12 | 13 | @RunWith(RobolectricTestRunner.class) 14 | 15 | public class BleAdvertisementTest { 16 | public static byte[] hexStringToByteArray(String s) { 17 | int len = s.length(); 18 | byte[] data = new byte[len / 2]; 19 | for (int i = 0; i < len; i += 2) { 20 | data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) 21 | + Character.digit(s.charAt(i+1), 16)); 22 | } 23 | return data; 24 | } 25 | 26 | @Test 27 | public void testCanParsePdusFromAltBeacon() { 28 | org.robolectric.shadows.ShadowLog.stream = System.err; 29 | byte[] bytes = hexStringToByteArray("02011a1aff1801beac2f234454cf6d4a0fadf2f4911ba9ffa600010002c50900000000000000000000000000000000000000000000000000000000000000"); 30 | BleAdvertisement bleAdvert = new BleAdvertisement(bytes); 31 | assertEquals("An AltBeacon advert should have two PDUs", 3, bleAdvert.getPdus().size()); 32 | } 33 | 34 | @Test 35 | public void testCanParsePdusFromOtherBeacon() { 36 | byte[] bytes = hexStringToByteArray("0201060303aafe1516aafe00e72f234454f4911ba9ffa600000000000100000c09526164426561636f6e2047000000000000000000000000000000000000"); 37 | BleAdvertisement bleAdvert = new BleAdvertisement(bytes); 38 | assertEquals("An otherBeacon advert should find four PDUs", 4, bleAdvert.getPdus().size()); 39 | assertEquals("First PDU should be flags type 1", 1, bleAdvert.getPdus().get(0).getType()); 40 | assertEquals("Second PDU should be services type 3", 3, bleAdvert.getPdus().get(1).getType()); 41 | assertEquals("Third PDU should be serivce type 0x16", 0x16, bleAdvert.getPdus().get(2).getType()); 42 | assertEquals("fourth PDU should be scan response type 9", 9, bleAdvert.getPdus().get(3).getType()); 43 | 44 | } 45 | } -------------------------------------------------------------------------------- /lib/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 24 | 25 | 29 | 30 | 33 | 34 | 35 | 36 | 37 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /lib/src/main/java/org/altbeacon/beacon/logging/WarningAndroidLogger.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Radius Networks, Inc. 3 | * Copyright 2015 Andrew Reitz 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.altbeacon.beacon.logging; 18 | 19 | import android.util.Log; 20 | 21 | /** 22 | * Android logger that only logs out warning and above to the {@link android.util.Log}. 23 | * 24 | * @since 2.2 25 | * @author Andrew Reitz 26 | */ 27 | final class WarningAndroidLogger extends AbstractAndroidLogger { 28 | @Override 29 | public void v(String tag, String message, Object... args) { } 30 | 31 | @Override 32 | public void v(Throwable t, String tag, String message, Object... args) { } 33 | 34 | @Override 35 | public void d(String tag, String message, Object... args) { } 36 | 37 | @Override 38 | public void d(Throwable t, String tag, String message, Object... args) { } 39 | 40 | @Override 41 | public void i(String tag, String message, Object... args) { } 42 | 43 | @Override 44 | public void i(Throwable t, String tag, String message, Object... args) { } 45 | 46 | @Override 47 | public void w(String tag, String message, Object... args) { 48 | Log.w(tag, formatString(message, args)); 49 | } 50 | 51 | @Override 52 | public void w(Throwable t, String tag, String message, Object... args) { 53 | Log.w(tag, formatString(message, args), t); 54 | } 55 | 56 | @Override 57 | public void e(String tag, String message, Object... args) { 58 | Log.e(tag, formatString(message, args)); 59 | } 60 | 61 | @Override 62 | public void e(Throwable t, String tag, String message, Object... args) { 63 | Log.e(tag, formatString(message, args), t); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /lib/src/main/java/org/altbeacon/beacon/distance/ModelSpecificDistanceUpdater.java: -------------------------------------------------------------------------------- 1 | package org.altbeacon.beacon.distance; 2 | 3 | import android.content.Context; 4 | import android.content.pm.PackageInfo; 5 | import android.os.AsyncTask; 6 | import android.os.Build; 7 | import android.provider.Settings; 8 | import android.util.Log; 9 | 10 | import org.altbeacon.beacon.BuildConfig; 11 | import org.json.JSONObject; 12 | 13 | /** 14 | * Created by dyoung on 9/12/14. 15 | */ 16 | public class ModelSpecificDistanceUpdater extends AsyncTask { 17 | 18 | private static final String TAG = "ModelSpecificDistanceUpdater"; 19 | private Exception exception = null; 20 | private String urlString = null; 21 | private String response = null; 22 | private Context mContext; 23 | private DistanceConfigFetcher mDistanceConfigFetcher; 24 | private CompletionHandler mCompletionHandler; 25 | 26 | @Override 27 | protected Void doInBackground(Void... params) { 28 | mDistanceConfigFetcher.request(); 29 | if (mCompletionHandler != null) { 30 | mCompletionHandler.onComplete(mDistanceConfigFetcher.getResponseString(), mDistanceConfigFetcher.getException(), mDistanceConfigFetcher.getResponseCode()); 31 | } 32 | return null; 33 | } 34 | 35 | protected void onPostExecute() { 36 | } 37 | 38 | public ModelSpecificDistanceUpdater(Context context, String urlString, CompletionHandler completionHandler) { 39 | mContext = context; 40 | mDistanceConfigFetcher = new DistanceConfigFetcher(urlString, getUserAgentString()); 41 | mCompletionHandler = completionHandler; 42 | } 43 | 44 | private String getUserAgentString() { 45 | return "Android Beacon Library;"+getVersion()+";"+getPackage()+";"+getInstallId()+";"+getModel(); 46 | } 47 | private String getPackage() { 48 | return mContext.getPackageName(); 49 | } 50 | private String getModel() { 51 | return AndroidModel.forThisDevice().toString(); 52 | } 53 | private String getInstallId() { 54 | return Settings.Secure.getString(mContext.getContentResolver(), Settings.Secure.ANDROID_ID); 55 | } 56 | private String getVersion() { 57 | return BuildConfig.VERSION_NAME; 58 | } 59 | 60 | interface CompletionHandler { 61 | public void onComplete(String body, Exception exception, int code); 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /lib/src/main/java/org/altbeacon/beacon/logging/InfoAndroidLogger.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Radius Networks, Inc. 3 | * Copyright 2015 Andrew Reitz 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.altbeacon.beacon.logging; 18 | 19 | import android.util.Log; 20 | 21 | /** 22 | * Android logger that only logs out warning and above to the {@link android.util.Log}. 23 | * 24 | * @since 2.2 25 | * @author Andrew Reitz 26 | */ 27 | final class InfoAndroidLogger extends AbstractAndroidLogger { 28 | @Override 29 | public void v(String tag, String message, Object... args) { } 30 | 31 | @Override 32 | public void v(Throwable t, String tag, String message, Object... args) { } 33 | 34 | @Override 35 | public void d(String tag, String message, Object... args) { } 36 | 37 | @Override 38 | public void d(Throwable t, String tag, String message, Object... args) { } 39 | 40 | @Override 41 | public void i(String tag, String message, Object... args) { 42 | Log.i(tag, formatString(message, args)); 43 | } 44 | 45 | @Override 46 | public void i(Throwable t, String tag, String message, Object... args) { 47 | Log.i(tag, formatString(message, args), t); 48 | } 49 | 50 | @Override 51 | public void w(String tag, String message, Object... args) { 52 | Log.w(tag, formatString(message, args)); 53 | } 54 | 55 | @Override 56 | public void w(Throwable t, String tag, String message, Object... args) { 57 | Log.w(tag, formatString(message, args), t); 58 | } 59 | 60 | @Override 61 | public void e(String tag, String message, Object... args) { 62 | Log.e(tag, formatString(message, args)); 63 | } 64 | 65 | @Override 66 | public void e(Throwable t, String tag, String message, Object... args) { 67 | Log.e(tag, formatString(message, args), t); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /lib/src/main/java/org/altbeacon/beacon/service/MonitoringData.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Radius Networks, Inc. 3 | * http://www.radiusnetworks.com 4 | * 5 | * @author David G. Young 6 | * 7 | * Licensed to the Apache Software Foundation (ASF) under one 8 | * or more contributor license agreements. See the NOTICE file 9 | * distributed with this work for additional information 10 | * regarding copyright ownership. The ASF licenses this file 11 | * to you under the Apache License, Version 2.0 (the 12 | * "License"); you may not use this file except in compliance 13 | * with the License. You may obtain a copy of the License at 14 | * 15 | * http://www.apache.org/licenses/LICENSE-2.0 16 | * 17 | * Unless required by applicable law or agreed to in writing, 18 | * software distributed under the License is distributed on an 19 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 20 | * KIND, either express or implied. See the License for the 21 | * specific language governing permissions and limitations 22 | * under the License. 23 | */ 24 | package org.altbeacon.beacon.service; 25 | 26 | import org.altbeacon.beacon.Region; 27 | 28 | import android.os.Bundle; 29 | 30 | public class MonitoringData { 31 | @SuppressWarnings("unused") 32 | private static final String TAG = "MonitoringData"; 33 | private final boolean mInside; 34 | private final Region mRegion; 35 | private static final String REGION_KEY = "region"; 36 | private static final String INSIDE_KEY = "inside"; 37 | 38 | public MonitoringData (boolean inside, Region region) { 39 | this.mInside = inside; 40 | this.mRegion = region; 41 | } 42 | public boolean isInside() { 43 | return mInside; 44 | } 45 | public Region getRegion() { 46 | return mRegion; 47 | } 48 | 49 | public Bundle toBundle() { 50 | Bundle bundle = new Bundle(); 51 | bundle.putSerializable(REGION_KEY, mRegion); 52 | bundle.putBoolean(INSIDE_KEY, mInside); 53 | 54 | return bundle; 55 | } 56 | public static MonitoringData fromBundle(Bundle bundle) { 57 | bundle.setClassLoader(Region.class.getClassLoader()); 58 | Region region = null; 59 | if (bundle.get(REGION_KEY) != null) { 60 | region = (Region) bundle.getSerializable(REGION_KEY); 61 | } 62 | Boolean inside = bundle.getBoolean(INSIDE_KEY); 63 | return new MonitoringData(inside, region); 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /lib/src/main/java/org/altbeacon/beacon/BeaconConsumer.java: -------------------------------------------------------------------------------- 1 | package org.altbeacon.beacon; 2 | /** 3 | * An interface for an Android Activity or Service 4 | * that wants to interact with beacons. The interface is used in conjunction 5 | * with BeaconManager and provides a callback when the BeaconService 6 | * is ready to use. Until this callback is made, ranging and monitoring of beacons is not 7 | * possible. 8 | * 9 | * In the example below, an Activity implements the BeaconConsumer interface, binds 10 | * to the service, then when it gets the callback saying the service is ready, it starts ranging. 11 | * 12 | *

13 |  *  public class RangingActivity extends Activity implements BeaconConsumer {
14 |  *      protected static final String TAG = "RangingActivity";
15 |  *      private BeaconManager beaconManager = BeaconManager.getInstanceForApplication(this);
16 |  *      {@literal @}Override
17 |  *      protected void onCreate(Bundle savedInstanceState) {
18 |  *          super.onCreate(savedInstanceState);
19 |  *          setContentView(R.layout.activity_ranging);
20 |  *          beaconManager.bind(this);
21 |  *      }
22 |  *
23 |  *      {@literal @}Override
24 |  *      protected void onDestroy() {
25 |  *          super.onDestroy();
26 |  *          beaconManager.unbind(this);
27 |  *      }
28 |  *
29 |  *      {@literal @}Override
30 |  *      public void onBeaconServiceConnect() {
31 |  *          beaconManager.setRangeNotifier(new RangeNotifier() {
32 |  *            {@literal @}Override
33 |  *            public void didRangeBeaconsInRegion(Collection beacons, Region region) {
34 |  *                 if (beacons.size() > 0) {
35 |  *                      Log.i(TAG, "The first beacon I see is about "+beacons.iterator().next().getDistance()+" meters away.");
36 |  *                 }
37 |  *            }
38 |  *          });
39 |  *
40 |  *          try {
41 |  *              beaconManager.startRangingBeaconsInRegion(new Region("myRangingUniqueId", null, null, null));
42 |  *          } catch (RemoteException e) {
43 |  *              e.printStackTrace();
44 |  *          }
45 |  *      }
46 |  *  }
47 |  *  
48 | * 49 | * @see BeaconManager 50 | * 51 | * @author David G. Young 52 | * @deprecated Will be removed in 3.0. See http://altbeacon.github.io/android-beacon-library/autobind.html 53 | */ 54 | @Deprecated 55 | public interface BeaconConsumer extends InternalBeaconConsumer { } -------------------------------------------------------------------------------- /lib/src/test/java/org/altbeacon/beacon/logging/LoggersTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Radius Networks, Inc. 3 | * Copyright 2015 Andrew Reitz 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.altbeacon.beacon.logging; 18 | 19 | import org.junit.Test; 20 | 21 | import static org.hamcrest.Matchers.instanceOf; 22 | import static org.hamcrest.Matchers.sameInstance; 23 | import static org.junit.Assert.assertThat; 24 | 25 | /** 26 | * Ensure correct instances are returned from factory methods. 27 | * 28 | * @author Andrew Reitz 29 | */ 30 | public class LoggersTest { 31 | @Test 32 | public void loggersReturnsVerboseInstance() { 33 | Logger logger = Loggers.verboseLogger(); 34 | 35 | assertThat(logger, instanceOf(VerboseAndroidLogger.class)); 36 | } 37 | 38 | @Test 39 | public void verboseLoggerReturnsSameInstance() { 40 | Logger logger1 = Loggers.verboseLogger(); 41 | Logger logger2 = Loggers.verboseLogger(); 42 | 43 | assertThat(logger1, sameInstance(logger2)); 44 | } 45 | 46 | @Test 47 | public void loggersReturnsEmptyInstance() { 48 | Logger logger = Loggers.empty(); 49 | 50 | assertThat(logger, instanceOf(EmptyLogger.class)); 51 | } 52 | 53 | @Test 54 | public void emptyLoggerReturnsSameInstance() { 55 | Logger logger1 = Loggers.empty(); 56 | Logger logger2 = Loggers.empty(); 57 | 58 | assertThat(logger1, sameInstance(logger2)); 59 | } 60 | 61 | @Test 62 | public void loggersReturnsWarningLoggerInstance() { 63 | Logger logger = Loggers.warningLogger(); 64 | 65 | assertThat(logger, instanceOf(WarningAndroidLogger.class)); 66 | } 67 | 68 | @Test 69 | public void warningLoggerReturnsSameInstance() { 70 | Logger logger1 = Loggers.warningLogger(); 71 | Logger logger2 = Loggers.warningLogger(); 72 | 73 | assertThat(logger1, sameInstance(logger2)); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /lib/src/main/java/org/altbeacon/beacon/InternalBeaconConsumer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Radius Networks, Inc. 3 | * http://www.radiusnetworks.com 4 | * 5 | * @author David G. Young 6 | * 7 | * Licensed to the Apache Software Foundation (ASF) under one 8 | * or more contributor license agreements. See the NOTICE file 9 | * distributed with this work for additional information 10 | * regarding copyright ownership. The ASF licenses this file 11 | * to you under the Apache License, Version 2.0 (the 12 | * "License"); you may not use this file except in compliance 13 | * with the License. You may obtain a copy of the License at 14 | * 15 | * http://www.apache.org/licenses/LICENSE-2.0 16 | * 17 | * Unless required by applicable law or agreed to in writing, 18 | * software distributed under the License is distributed on an 19 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 20 | * KIND, either express or implied. See the License for the 21 | * specific language governing permissions and limitations 22 | * under the License. 23 | */ 24 | package org.altbeacon.beacon; 25 | 26 | import android.content.Context; 27 | import android.content.Intent; 28 | import android.content.ServiceConnection; 29 | 30 | public interface InternalBeaconConsumer { 31 | 32 | /** 33 | * Called when the beacon service is running and ready to accept your commands through the BeaconManager 34 | */ 35 | public void onBeaconServiceConnect(); 36 | 37 | /** 38 | * Called by the BeaconManager to get the context of your Service or Activity. This method is implemented by Service or Activity. 39 | * You generally should not override it. 40 | * @return the application context of your service or activity 41 | */ 42 | public Context getApplicationContext(); 43 | 44 | /** 45 | * Called by the BeaconManager to unbind your BeaconConsumer to the BeaconService. This method is implemented by Service or Activity, and 46 | * You generally should not override it. 47 | * @return the application context of your service or activity 48 | */ 49 | public void unbindService(ServiceConnection connection); 50 | 51 | /** 52 | * Called by the BeaconManager to bind your BeaconConsumer to the BeaconService. This method is implemented by Service or Activity, and 53 | * You generally should not override it. 54 | * @return the application context of your service or activity 55 | */ 56 | public boolean bindService(Intent intent, ServiceConnection connection, int mode); 57 | } 58 | -------------------------------------------------------------------------------- /lib/src/main/java/org/altbeacon/beacon/BeaconIntentProcessor.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Radius Networks, Inc. 3 | * http://www.radiusnetworks.com 4 | * 5 | * @author David G. Young 6 | * 7 | * Licensed to the Apache Software Foundation (ASF) under one 8 | * or more contributor license agreements. See the NOTICE file 9 | * distributed with this work for additional information 10 | * regarding copyright ownership. The ASF licenses this file 11 | * to you under the Apache License, Version 2.0 (the 12 | * "License"); you may not use this file except in compliance 13 | * with the License. You may obtain a copy of the License at 14 | * 15 | * http://www.apache.org/licenses/LICENSE-2.0 16 | * 17 | * Unless required by applicable law or agreed to in writing, 18 | * software distributed under the License is distributed on an 19 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 20 | * KIND, either express or implied. See the License for the 21 | * specific language governing permissions and limitations 22 | * under the License. 23 | */ 24 | package org.altbeacon.beacon; 25 | 26 | import android.app.IntentService; 27 | import android.content.Intent; 28 | 29 | import org.altbeacon.beacon.logging.LogManager; 30 | import org.altbeacon.beacon.service.MonitoringData; 31 | import org.altbeacon.beacon.service.MonitoringStatus; 32 | import org.altbeacon.beacon.service.RangingData; 33 | 34 | import java.util.Set; 35 | 36 | /** 37 | * Converts internal intents to notifier callbacks 38 | * 39 | * This is used with the BeaconService and supports scanning in a separate process. 40 | * It is not used with the ScanJob, as an IntentService will not be able to be started in some cases 41 | * where the app is in the background on Android O. 42 | * 43 | * @see BeaconLocalBroadcastProcessor for the equivalent use with ScanJob. 44 | * 45 | * This IntentService may be running in a different process from the BeaconService, which justifies 46 | * its continued existence for multi-process service cases. 47 | * 48 | * Internal library class. Do not use directly from outside the library 49 | * 50 | * @hide 51 | */ 52 | public class BeaconIntentProcessor extends IntentService { 53 | private static final String TAG = "BeaconIntentProcessor"; 54 | 55 | public BeaconIntentProcessor() { 56 | super("BeaconIntentProcessor"); 57 | } 58 | 59 | @Override 60 | protected void onHandleIntent(Intent intent) { 61 | new IntentHandler().convertIntentsToCallbacks(this.getApplicationContext(), intent); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /lib/src/main/java/org/altbeacon/beacon/distance/CurveFittedDistanceCalculator.java: -------------------------------------------------------------------------------- 1 | package org.altbeacon.beacon.distance; 2 | 3 | import org.altbeacon.beacon.BeaconManager; 4 | import org.altbeacon.beacon.logging.LogManager; 5 | 6 | /** 7 | * This class estimates the distance between the mobile device and a BLE beacon based on the measured 8 | * RSSI and a txPower calibration value that represents the expected RSSI for an iPhone 5 receiving 9 | * the signal when it is 1 meter away. 10 | * 11 | * This class uses a best-fit curve equation with configurable coefficients. The coefficients must 12 | * be supplied by the caller and are specific to the Android device being used. See the 13 | * ModelSpecificDistanceCalculator for more information on the coefficients. 14 | * 15 | * Created by dyoung on 8/28/14. 16 | */ 17 | public class CurveFittedDistanceCalculator implements DistanceCalculator { 18 | 19 | public static final String TAG = "CurveFittedDistanceCalculator"; 20 | private double mCoefficient1; 21 | private double mCoefficient2; 22 | private double mCoefficient3; 23 | 24 | /** 25 | * Construct a calculator with coefficients specific for the device's signal vs. distance 26 | * 27 | * @param coefficient1 28 | * @param coefficient2 29 | * @param coefficient3 30 | */ 31 | public CurveFittedDistanceCalculator(double coefficient1, double coefficient2, double coefficient3) { 32 | mCoefficient1 = coefficient1; 33 | mCoefficient2 = coefficient2; 34 | mCoefficient3 = coefficient3; 35 | } 36 | 37 | /** 38 | * Calculated the estimated distance in meters to the beacon based on a reference rssi at 1m 39 | * and the known actual rssi at the current location 40 | * 41 | * @param txPower 42 | * @param rssi 43 | * @return estimated distance 44 | */ 45 | @Override 46 | public double calculateDistance(int txPower, double rssi) { 47 | if (rssi == 0) { 48 | return -1.0; // if we cannot determine accuracy, return -1. 49 | } 50 | 51 | LogManager.d(TAG, "calculating distance based on mRssi of %s and txPower of %s", rssi, txPower); 52 | 53 | 54 | double ratio = rssi*1.0/txPower; 55 | double distance; 56 | if (ratio < 1.0) { 57 | distance = Math.pow(ratio,10); 58 | } 59 | else { 60 | distance = (mCoefficient1)*Math.pow(ratio,mCoefficient2) + mCoefficient3; 61 | } 62 | LogManager.d(TAG, "avg mRssi: %s distance: %s", rssi, distance); 63 | return distance; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /lib/src/main/java/org/altbeacon/beacon/service/ArmaRssiFilter.java: -------------------------------------------------------------------------------- 1 | package org.altbeacon.beacon.service; 2 | 3 | import org.altbeacon.beacon.logging.LogManager; 4 | 5 | /** 6 | * This filter calculates its rssi on base of an auto regressive moving average (ARMA) 7 | * It needs only the current value to do this; the general formula is n(t) = n(t-1) - c * (n(t-1) - n(t)) 8 | * where c is a coefficient, that denotes the smoothness - the lower the value, the smoother the average 9 | * Note: a smoother average needs longer to "settle down" 10 | * Note: For signals, that change rather frequently (say, 1Hz or faster) and tend to vary more a recommended 11 | * value would be 0,1 (that means the actual value is changed by 10% of the difference between the 12 | * actual measurement and the actual average) 13 | * For signals at lower rates (10Hz) a value of 0.25 to 0.5 would be appropriate 14 | */ 15 | public class ArmaRssiFilter implements RssiFilter { 16 | 17 | private static double DEFAULT_ARMA_SPEED = 0.1; //How likely is it that the RSSI value changes? 18 | //Note: the more unlikely, the higher can that value be 19 | // also, the lower the (expected) sending frequency, 20 | // the higher should that value be 21 | 22 | private static final String TAG = "ArmaRssiFilter"; 23 | //initially set to min value 24 | private int armaMeasurement; 25 | private double armaSpeed = 0.1; 26 | private boolean isInitialized = false; 27 | 28 | public ArmaRssiFilter() { 29 | this.armaSpeed = DEFAULT_ARMA_SPEED; 30 | } 31 | 32 | public void addMeasurement(Integer rssi) { 33 | LogManager.d(TAG, "adding rssi: %s", rssi); 34 | //use first measurement as initialization 35 | if (!isInitialized) { 36 | armaMeasurement = rssi; 37 | isInitialized = true; 38 | }; 39 | armaMeasurement = Double.valueOf(armaMeasurement - armaSpeed * (armaMeasurement - rssi)).intValue(); 40 | LogManager.d(TAG, "armaMeasurement: %s", armaMeasurement); 41 | } 42 | 43 | @Override 44 | public int getMeasurementCount() { return 0; } 45 | 46 | public boolean noMeasurementsAvailable() { 47 | return false; 48 | } 49 | 50 | public double calculateRssi() { 51 | return armaMeasurement; 52 | 53 | } 54 | 55 | public static void setDEFAULT_ARMA_SPEED(double default_arma_speed) { 56 | DEFAULT_ARMA_SPEED = default_arma_speed; 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /lib/src/main/java/org/altbeacon/beacon/MonitorNotifier.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Radius Networks, Inc. 3 | * http://www.radiusnetworks.com 4 | * 5 | * @author David G. Young 6 | * 7 | * Licensed to the Apache Software Foundation (ASF) under one 8 | * or more contributor license agreements. See the NOTICE file 9 | * distributed with this work for additional information 10 | * regarding copyright ownership. The ASF licenses this file 11 | * to you under the Apache License, Version 2.0 (the 12 | * "License"); you may not use this file except in compliance 13 | * with the License. You may obtain a copy of the License at 14 | * 15 | * http://www.apache.org/licenses/LICENSE-2.0 16 | * 17 | * Unless required by applicable law or agreed to in writing, 18 | * software distributed under the License is distributed on an 19 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 20 | * KIND, either express or implied. See the License for the 21 | * specific language governing permissions and limitations 22 | * under the License. 23 | */ 24 | package org.altbeacon.beacon; 25 | 26 | /** 27 | * This interface is implemented by classes that receive beacon monitoring notifications 28 | * 29 | * @see BeaconManager#setMonitorNotifier(MonitorNotifier notifier) 30 | * @see BeaconManager#startMonitoringBeaconsInRegion(Region region) 31 | * @see Region 32 | * 33 | * @author David G. Young 34 | */ 35 | public interface MonitorNotifier { 36 | /** 37 | * Indicates the Android device is inside the Region of beacons 38 | */ 39 | public static final int INSIDE = 1; 40 | /** 41 | * Indicates the Android device is outside the Region of beacons 42 | */ 43 | public static final int OUTSIDE = 0; 44 | 45 | /** 46 | * Called when at least one beacon in a Region is visible. 47 | * @param region a Region that defines the criteria of beacons to look for 48 | */ 49 | public void didEnterRegion(Region region); 50 | 51 | /** 52 | * Called when no beacons in a Region are visible. 53 | * @param region a Region that defines the criteria of beacons to look for 54 | */ 55 | public void didExitRegion(Region region); 56 | 57 | /** 58 | * Called with a state value of MonitorNotifier.INSIDE when at least one beacon in a Region is visible. 59 | * Called with a state value of MonitorNotifier.OUTSIDE when no beacons in a Region are visible. 60 | * @param state either MonitorNotifier.INSIDE or MonitorNotifier.OUTSIDE 61 | * @param region a Region that defines the criteria of beacons to look for 62 | */ 63 | public void didDetermineStateForRegion(int state, Region region); 64 | } 65 | -------------------------------------------------------------------------------- /lib/src/main/java/org/altbeacon/beacon/logging/VerboseAndroidLogger.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Radius Networks, Inc. 3 | * Copyright 2015 Andrew Reitz 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.altbeacon.beacon.logging; 18 | 19 | import android.util.Log; 20 | 21 | /** 22 | * Logger class that logs using the default Android logging mechanism. This class will log verbose 23 | * messages and above (all logs). 24 | * 25 | * @author Android Reitz 26 | * @since 2.2 27 | */ 28 | final class VerboseAndroidLogger extends AbstractAndroidLogger { 29 | 30 | @Override 31 | public void v(String tag, String message, Object... args) { 32 | Log.v(tag, formatString(message, args)); 33 | } 34 | 35 | @Override 36 | public void v(Throwable t, String tag, String message, Object... args) { 37 | Log.v(tag, formatString(message, args), t); 38 | } 39 | 40 | @Override 41 | public void d(String tag, String message, Object... args) { 42 | Log.d(tag, formatString(message, args)); 43 | } 44 | 45 | @Override 46 | public void d(Throwable t, String tag, String message, Object... args) { 47 | Log.d(tag, formatString(message, args), t); 48 | } 49 | 50 | @Override 51 | public void i(String tag, String message, Object... args) { 52 | Log.i(tag, formatString(message, args)); 53 | } 54 | 55 | @Override 56 | public void i(Throwable t, String tag, String message, Object... args) { 57 | Log.i(tag, formatString(message, args), t); 58 | } 59 | 60 | @Override 61 | public void w(String tag, String message, Object... args) { 62 | Log.w(tag, formatString(message, args)); 63 | } 64 | 65 | @Override 66 | public void w(Throwable t, String tag, String message, Object... args) { 67 | Log.w(tag, formatString(message, args), t); 68 | } 69 | 70 | @Override 71 | public void e(String tag, String message, Object... args) { 72 | Log.e(tag, formatString(message, args)); 73 | } 74 | 75 | @Override 76 | public void e(Throwable t, String tag, String message, Object... args) { 77 | Log.e(tag, formatString(message, args), t); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /lib/src/test/java/org/altbeacon/beacon/service/BeaconServiceTest.java: -------------------------------------------------------------------------------- 1 | package org.altbeacon.beacon.service; 2 | 3 | import android.os.AsyncTask; 4 | 5 | import org.altbeacon.beacon.BeaconManager; 6 | import org.altbeacon.beacon.logging.LogManager; 7 | import org.altbeacon.beacon.logging.Loggers; 8 | import org.altbeacon.beacon.service.scanner.CycledLeScanCallback; 9 | import org.junit.Before; 10 | import org.junit.Test; 11 | import org.junit.runner.RunWith; 12 | import org.robolectric.Robolectric; 13 | import org.robolectric.RobolectricTestRunner; 14 | import org.robolectric.android.controller.ServiceController; 15 | import org.robolectric.annotation.Config; 16 | 17 | import java.util.concurrent.ThreadPoolExecutor; 18 | 19 | import static org.junit.Assert.assertEquals; 20 | 21 | /** 22 | * Created by dyoung on 7/1/15. 23 | */ 24 | @RunWith(RobolectricTestRunner.class) 25 | @Config(sdk = 28) 26 | public class BeaconServiceTest { 27 | 28 | @Before 29 | public void before() { 30 | org.robolectric.shadows.ShadowLog.stream = System.err; 31 | LogManager.setLogger(Loggers.verboseLogger()); 32 | LogManager.setVerboseLoggingEnabled(true); 33 | BeaconManager.setsManifestCheckingDisabled(true); 34 | } 35 | 36 | /** 37 | * This test verifies that processing a beacon in a scan (which starts its own thread) does not 38 | * affect the size of the available threads in the main Android AsyncTask.THREAD_POOL_EXECUTOR 39 | * @throws Exception 40 | */ 41 | @Test 42 | public void beaconScanCallbackTest() throws Exception { 43 | final ServiceController beaconServiceServiceController = 44 | Robolectric.buildService(BeaconService.class); 45 | // beaconServiceServiceController.attach(); 46 | BeaconService beaconService = beaconServiceServiceController.get(); 47 | beaconService.onCreate(); 48 | CycledLeScanCallback callback = beaconService.getCycledLeScanCallback(); 49 | 50 | ThreadPoolExecutor executor = (ThreadPoolExecutor) AsyncTask.THREAD_POOL_EXECUTOR; 51 | int activeThreadCountBeforeScan = executor.getActiveCount(); 52 | 53 | byte[] scanRecord = new byte[1]; 54 | callback.onLeScan(null, -59, scanRecord, 123456L); 55 | 56 | int activeThreadCountAfterScan = executor.getActiveCount(); 57 | 58 | assertEquals("The size of the Android thread pool should be unchanged by beacon scanning", 59 | activeThreadCountBeforeScan, activeThreadCountAfterScan); 60 | 61 | // Need to sleep here until the thread in the above method completes, otherwise an exception 62 | // is thrown. Maybe we don't care about this exception, so we could remove this. 63 | Thread.sleep(100); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /lib/src/test/java/org/altbeacon/beacon/service/RunningAverageRssiFilterTest.java: -------------------------------------------------------------------------------- 1 | package org.altbeacon.beacon.service; 2 | 3 | import org.altbeacon.beacon.Beacon; 4 | import org.junit.Test; 5 | import org.junit.runner.RunWith; 6 | import org.robolectric.RobolectricTestRunner; 7 | import org.robolectric.annotation.Config; 8 | import static org.junit.Assert.assertEquals; 9 | 10 | @RunWith(RobolectricTestRunner.class) 11 | @Config(sdk = 28) 12 | 13 | public class RunningAverageRssiFilterTest { 14 | 15 | @Test 16 | public void initTest1() { 17 | RunningAverageRssiFilter filter = new RunningAverageRssiFilter(); 18 | filter.addMeasurement(-50); 19 | assertEquals("First measurement should be -50", String.valueOf(filter.calculateRssi()), "-50.0"); 20 | } 21 | @Test 22 | public void rangedBeaconDoesNotOverrideSampleExpirationMillisecondsText() { 23 | RangedBeacon.setSampleExpirationMilliseconds(20000); 24 | RunningAverageRssiFilter.setSampleExpirationMilliseconds(20000); 25 | Beacon beacon = new Beacon.Builder().setId1("1").build(); 26 | RunningAverageRssiFilter.setSampleExpirationMilliseconds(33l); 27 | RangedBeacon rb = new RangedBeacon(beacon); 28 | assertEquals("RunningAverageRssiFilter sampleExprirationMilliseconds should not be altered by constructing RangedBeacon", 33l, RunningAverageRssiFilter.getSampleExpirationMilliseconds()); 29 | } 30 | 31 | @Test 32 | public void regressionCheckRangedBeaconCommitDoesNotOverrideSampleExpirationMilliseconds() { 33 | RangedBeacon.setSampleExpirationMilliseconds(20000); 34 | RunningAverageRssiFilter.setSampleExpirationMilliseconds(20000); 35 | Beacon beacon = new Beacon.Builder().setId1("1").build(); 36 | RangedBeacon rb = new RangedBeacon(beacon); 37 | RunningAverageRssiFilter.setSampleExpirationMilliseconds(33l); 38 | rb.commitMeasurements(); 39 | assertEquals( 40 | "RunningAverageRssiFilter sampleExprirationMilliseconds should not be altered by committing RangedBeacon", 41 | 33l, 42 | RunningAverageRssiFilter.getSampleExpirationMilliseconds() 43 | ); 44 | } 45 | 46 | @Test 47 | public void legacySetSampleExpirationMillisecondsWorksText() { 48 | RangedBeacon.setSampleExpirationMilliseconds(20000); 49 | RunningAverageRssiFilter.setSampleExpirationMilliseconds(20000); 50 | Beacon beacon = new Beacon.Builder().setId1("1").build(); 51 | RangedBeacon.setSampleExpirationMilliseconds(33l); 52 | RangedBeacon rb = new RangedBeacon(beacon); 53 | assertEquals("RunningAverageRssiFilter sampleExprirationMilliseconds should not be altered by constructing RangedBeacon", 33l, RunningAverageRssiFilter.getSampleExpirationMilliseconds()); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /lib/src/main/java/org/altbeacon/beacon/logging/Loggers.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Radius Networks, Inc. 3 | * Copyright 2015 Andrew Reitz 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.altbeacon.beacon.logging; 18 | 19 | /** 20 | * Static factory methods for getting different {@link org.altbeacon.beacon.logging.Logger} 21 | * implementations. 22 | * 23 | * @author Andrew Reitz 24 | * @since 2.2 25 | */ 26 | public final class Loggers { 27 | /** Empty Logger Singleton. */ 28 | private static final Logger EMPTY_LOGGER = new EmptyLogger(); 29 | 30 | /** Debug Logger Singleton. */ 31 | private static final Logger VERBOSE_ANDROID_LOGGER = new VerboseAndroidLogger(); 32 | 33 | /** Info Logger Singleton. */ 34 | private static final Logger INFO_ANDROID_LOGGER = new InfoAndroidLogger(); 35 | 36 | /** Warning Logger Singleton. */ 37 | private static final Logger WARNING_ANDROID_LOGGER = new WarningAndroidLogger(); 38 | 39 | /** Api Tracking Logger Singleton. */ 40 | private static final ApiTrackingLogger API_TRACKING_ANDROID_LOGGER = new ApiTrackingLogger(); 41 | 42 | /** 43 | * @return Get a logger that does nothing. 44 | */ 45 | public static Logger empty() { 46 | return EMPTY_LOGGER; 47 | } 48 | 49 | /** 50 | * @return Get a logger that logs all messages to default Android logs. 51 | * @see android.util.Log 52 | */ 53 | public static Logger verboseLogger() { 54 | return VERBOSE_ANDROID_LOGGER; 55 | } 56 | 57 | /** 58 | * @return Get a logger that logs messages of info and greater. 59 | * @see android.util.Log 60 | */ 61 | public static Logger infoLogger() { 62 | return INFO_ANDROID_LOGGER; 63 | } 64 | 65 | /** 66 | * @return Get a logger that logs messages of warning and greater. 67 | * @see android.util.Log 68 | */ 69 | public static Logger warningLogger() { 70 | return WARNING_ANDROID_LOGGER; 71 | } 72 | 73 | /** 74 | * @return Get a logger that logs all messages to default Android logs and tracks api calls 75 | * @see android.util.Log 76 | */ 77 | public static ApiTrackingLogger apiTrackingLogger() { 78 | return API_TRACKING_ANDROID_LOGGER; 79 | } 80 | 81 | private Loggers() { 82 | // No instances 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /lib/src/test/java/org/altbeacon/beacon/utils/EddystoneTelemetryAccessorTest.java: -------------------------------------------------------------------------------- 1 | package org.altbeacon.beacon.utils; 2 | 3 | import junit.framework.Assert; 4 | 5 | import java.net.MalformedURLException; 6 | import java.util.ArrayList; 7 | import java.util.Arrays; 8 | 9 | import org.altbeacon.beacon.Beacon; 10 | import org.junit.Test; 11 | import org.robolectric.RobolectricTestRunner; 12 | 13 | import org.junit.runner.RunWith; 14 | import org.robolectric.annotation.Config; 15 | 16 | import static org.junit.Assert.assertEquals; 17 | import static org.junit.Assert.assertNotNull; 18 | 19 | @Config(sdk = 28) 20 | @RunWith(RobolectricTestRunner.class) 21 | public class EddystoneTelemetryAccessorTest { 22 | 23 | public static String byteArrayToHexString(byte[] bytes) { 24 | StringBuilder sb = new StringBuilder(); 25 | for (int i = 0; i < bytes.length; i++) { 26 | sb.append(String.format("%02x", bytes[i])); 27 | } 28 | return sb.toString(); 29 | } 30 | 31 | @Test 32 | public void testAllowsAccessToTelemetryBytes() throws MalformedURLException { 33 | ArrayList telemetryFields = new ArrayList(); 34 | telemetryFields.add(0x01l); // version 35 | telemetryFields.add(0x0212l); // battery level 36 | telemetryFields.add(0x0313l); // temperature 37 | telemetryFields.add(0x04142434l); // pdu count 38 | telemetryFields.add(0x05152535l); // uptime 39 | 40 | Beacon beaconWithTelemetry = new Beacon.Builder().setId1("0x0102030405060708090a").setId2("0x01020304050607").setTxPower(-59).setExtraDataFields(telemetryFields).build(); 41 | byte[] telemetryBytes = new EddystoneTelemetryAccessor().getTelemetryBytes(beaconWithTelemetry); 42 | 43 | byte[] expectedBytes = {0x20, 0x01, 0x02, 0x12, 0x03, 0x13, 0x04, 0x14, 0x24, 0x34, 0x05, 0x15, 0x25, 0x35}; 44 | assertEquals(byteArrayToHexString(telemetryBytes), byteArrayToHexString(expectedBytes)); 45 | } 46 | 47 | 48 | @Test 49 | public void testAllowsAccessToBase64EncodedTelemetryBytes() throws MalformedURLException { 50 | ArrayList telemetryFields = new ArrayList(); 51 | telemetryFields.add(0x01l); // version 52 | telemetryFields.add(0x0212l); // battery level 53 | telemetryFields.add(0x0313l); // temperature 54 | telemetryFields.add(0x04142434l); // pdu count 55 | telemetryFields.add(0x05152535l); // uptime 56 | 57 | Beacon beaconWithTelemetry = new Beacon.Builder().setId1("0x0102030405060708090a").setId2("0x01020304050607").setTxPower(-59).setExtraDataFields(telemetryFields).build(); 58 | byte[] telemetryBytes = new EddystoneTelemetryAccessor().getTelemetryBytes(beaconWithTelemetry); 59 | 60 | String encodedTelemetryBytes = new EddystoneTelemetryAccessor().getBase64EncodedTelemetry(beaconWithTelemetry); 61 | assertNotNull(telemetryBytes); 62 | } 63 | } -------------------------------------------------------------------------------- /lib/src/test/java/org/altbeacon/beacon/org/altbeacon/beacon/simulator/BeaconSimulatorTest.java: -------------------------------------------------------------------------------- 1 | package org.altbeacon.beacon.org.altbeacon.beacon.simulator; 2 | 3 | 4 | import static org.junit.Assert.assertEquals; 5 | import static org.junit.Assert.assertTrue; 6 | 7 | import org.altbeacon.beacon.AltBeacon; 8 | import org.altbeacon.beacon.AltBeaconParser; 9 | import org.altbeacon.beacon.Beacon; 10 | import org.altbeacon.beacon.simulator.StaticBeaconSimulator; 11 | import org.robolectric.RobolectricTestRunner; 12 | 13 | import org.junit.runner.RunWith; 14 | import org.junit.AfterClass; 15 | import org.junit.BeforeClass; 16 | import org.junit.Test; 17 | 18 | import java.lang.Override; 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | 22 | import dalvik.annotation.TestTarget; 23 | import org.robolectric.annotation.Config; 24 | 25 | @Config(sdk = 28) 26 | 27 | @RunWith(RobolectricTestRunner.class) 28 | public class BeaconSimulatorTest { 29 | 30 | public static byte[] hexStringToByteArray(String s) { 31 | int len = s.length(); 32 | byte[] data = new byte[len / 2]; 33 | for (int i = 0; i < len; i += 2) { 34 | data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) 35 | + Character.digit(s.charAt(i+1), 16)); 36 | } 37 | return data; 38 | } 39 | 40 | @BeforeClass 41 | public static void testSetup() { 42 | } 43 | 44 | @AfterClass 45 | public static void testCleanup() { 46 | // Teardown for data used by the unit tests 47 | } 48 | 49 | @Test 50 | public void testSetBeacons(){ 51 | StaticBeaconSimulator staticBeaconSimulator = new StaticBeaconSimulator(); 52 | byte[] beaconBytes = hexStringToByteArray("02011a1bff1801beac2f234454cf6d4a0fadf2f4911ba9ffa600010002c509"); 53 | Beacon beacon = new AltBeaconParser().fromScanData(beaconBytes, -55, null,123456L); 54 | ArrayList beacons = new ArrayList(); 55 | beacons.add(beacon); 56 | staticBeaconSimulator.setBeacons(beacons); 57 | assertEquals("getBeacons should match values entered with setBeacons", staticBeaconSimulator.getBeacons(), beacons); 58 | } 59 | 60 | @Test 61 | public void testSetBeaconsEmpty(){ 62 | StaticBeaconSimulator staticBeaconSimulator = new StaticBeaconSimulator(); 63 | ArrayList beacons = new ArrayList(); 64 | staticBeaconSimulator.setBeacons(beacons); 65 | assertEquals("getBeacons should match values entered with setBeacons even when empty", staticBeaconSimulator.getBeacons(), beacons); 66 | } 67 | 68 | @Test 69 | public void testSetBeaconsNull(){ 70 | StaticBeaconSimulator staticBeaconSimulator = new StaticBeaconSimulator(); 71 | staticBeaconSimulator.setBeacons(null); 72 | assertEquals("getBeacons should return null",staticBeaconSimulator.getBeacons(), null); 73 | } 74 | } -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to Contribute to the Android Beacon Library 2 | 3 | This project welcomes code contributions from the communityProposed code changes should be submitted as a pull request on Github. Please follow the following guidelines when submitting a [pull request](https://github.com/altbeacon/android-beacon-library/pulls). 4 | 5 | ## Style 6 | 7 | Code style should generally follow the [Android coding style](https://source.android.com/source/code-style.html) 8 | 9 | ## API Changes 10 | 11 | Changes generally should not break the existing API and should be backward compatible with the current release version If the PR does represent a breaking change, the title or description must make this clear. Breaking changes will be held for the next major version release of the library. 12 | 13 | ## Testing 14 | 15 | PRs must include testing information to ensure the changes are functional and do not adversely affect other library functionsTesting information must include one or more of the following: 16 | 17 | ### 1. Automated Robolectric tests: 18 | 19 | Robolectric tests are required for most changes, and should be submitted along with the PRExceptions include Bluetooth or Android OS-level changes that cannot be tested with Robolectric. Examples of Robolectric tests exist in the src/test folder. 20 | 21 | Robolectric test updates are absolutely required if existing Robolectric tests exists for the modified code. 22 | 23 | Regardless of whether Robolectric tests are added or modified, all tests must be passing on the branch of the PR when running `./gradlew test ` 24 | 25 | ### 2. Manual tests: 26 | 27 | Changes affecting Bluetooth scanning, addressing device-specific issues often cannot be adequately tested using Robolectric since it stubs out Bluetooth and Android OS system callsChanges of this nature must be manually tested on a physical device. Manual tests should be performed with the library's reference application, if possible. 28 | 29 | When submitting a PR, a description of any manual testing performed should include: 30 | 31 | * Mobile device model and Android OS version. 32 | 33 | * Description of beacon device and configuration used during testing (if applicable) 34 | 35 | * A description of the steps taken to do the manual testing 36 | 37 | * A description of the conditions witnessed that verify the code works as designed and that other functions are not broken 38 | 39 | ### 3. Changes that cannot be tested manually or with Robolectric 40 | 41 | In some rare cases where changes cannot be verified manually (e.g. some intermittent issues), a description may be included of why testing cannot be performed and describing why the change is low-risk and can be verified by code reviewFor such changes to be considered low-risk they typically must be very small 42 | 43 | ## License 44 | 45 | Any code submitted must be the work of the author, or if third party must be covered by the same Apache 2 license as this library or the Android Open Source ProjectOnce submitted, the code is covered under the terms of the license of this library. 46 | -------------------------------------------------------------------------------- /lib/src/main/java/org/altbeacon/beacon/utils/EddystoneTelemetryAccessor.java: -------------------------------------------------------------------------------- 1 | package org.altbeacon.beacon.utils; 2 | 3 | import android.annotation.TargetApi; 4 | import android.os.Build; 5 | import android.util.Base64; 6 | import android.util.Log; 7 | 8 | import org.altbeacon.beacon.Beacon; 9 | import org.altbeacon.beacon.BeaconParser; 10 | 11 | /** 12 | * Utility class for working beacons that include Eddystone-TLM (telemetry) information 13 | * Created by dyoung on 12/21/15. 14 | */ 15 | public class EddystoneTelemetryAccessor { 16 | private static final String TAG = "EddystoneTLMAccessor"; 17 | /** 18 | * Extracts the raw Eddystone telemetry bytes from the extra data fields of an associated beacon. 19 | * This is useful for passing the telemetry to Google's backend services. 20 | * @param beacon 21 | * @return the bytes of the telemetry frame 22 | */ 23 | public byte[] getTelemetryBytes(Beacon beacon) { 24 | if (beacon.getExtraDataFields().size() >= 5) { 25 | Beacon telemetryBeacon = new Beacon.Builder() 26 | .setDataFields(beacon.getExtraDataFields()) 27 | .build(); 28 | BeaconParser telemetryParser = new BeaconParser() 29 | .setBeaconLayout(BeaconParser.EDDYSTONE_TLM_LAYOUT); 30 | byte[] telemetryBytes = telemetryParser.getBeaconAdvertisementData(telemetryBeacon); 31 | Log.d(TAG, "Rehydrated telemetry bytes are :" + byteArrayToString(telemetryBytes)); 32 | return telemetryBytes; 33 | } 34 | else { 35 | return null; 36 | } 37 | } 38 | 39 | /** 40 | * Extracts the raw Eddystone telemetry bytes from the extra data fields of an associated beacon 41 | * and base64 encodes them. This is useful for passing the telemetry to Google's backend 42 | * services. 43 | * @param beacon 44 | * @return base64 encoded telemetry bytes 45 | */ 46 | @TargetApi(Build.VERSION_CODES.FROYO) 47 | public String getBase64EncodedTelemetry(Beacon beacon) { 48 | byte[] bytes = getTelemetryBytes(beacon); 49 | if (bytes != null) { 50 | String base64EncodedTelemetry = Base64.encodeToString(bytes, Base64.DEFAULT); 51 | // 12-21 00:17:18.844 20180-20180/? D/EddystoneTLMAccessor: Rehydrated telemetry bytes are :20 00 00 00 88 29 18 4d 00 00 18 4d 00 00 52 | // 12-21 00:17:18.844 20180-20180/? D/EddystoneTLMAccessor: Base64 telemetry bytes are :IAAAAIgpGE0AABhNAAA= 53 | Log.d(TAG, "Base64 telemetry bytes are :"+base64EncodedTelemetry); 54 | return base64EncodedTelemetry; 55 | } 56 | else { 57 | return null; 58 | } 59 | } 60 | 61 | private String byteArrayToString(byte[] bytes) { 62 | StringBuilder sb = new StringBuilder(); 63 | for (int i = 0; i < bytes.length; i++) { 64 | sb.append(String.format("%02x", bytes[i])); 65 | sb.append(" "); 66 | } 67 | return sb.toString().trim(); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /lib/src/main/java/org/altbeacon/beacon/distance/AndroidModel.java: -------------------------------------------------------------------------------- 1 | package org.altbeacon.beacon.distance; 2 | 3 | import android.os.Build; 4 | import org.altbeacon.beacon.BeaconManager; 5 | import org.altbeacon.beacon.logging.LogManager; 6 | 7 | /** 8 | * Represents a specific Android device model based on the available device build information 9 | * 10 | * Created by dyoung on 8/28/14. 11 | */ 12 | public class AndroidModel { 13 | private static final String TAG = "AndroidModel"; 14 | String mVersion; 15 | String mBuildNumber; 16 | String mModel; 17 | String mManufacturer; 18 | 19 | 20 | public AndroidModel(String version, String buildNumber, 21 | String model, 22 | String manufacturer) { 23 | mVersion = version; 24 | mBuildNumber = buildNumber; 25 | mModel = model; 26 | mManufacturer = manufacturer; 27 | 28 | } 29 | public static AndroidModel forThisDevice() { 30 | return new AndroidModel( 31 | Build.VERSION.RELEASE, 32 | Build.ID, 33 | Build.MODEL, 34 | Build.MANUFACTURER); 35 | } 36 | 37 | public String getVersion() { 38 | return mVersion; 39 | } 40 | 41 | public void setVersion(String mVersion) { 42 | this.mVersion = mVersion; 43 | } 44 | 45 | public String getBuildNumber() { 46 | return mBuildNumber; 47 | } 48 | 49 | public String getModel() { 50 | return mModel; 51 | } 52 | 53 | 54 | public String getManufacturer() { 55 | return mManufacturer; 56 | } 57 | 58 | public void setBuildNumber(String mBuildNumber) { 59 | this.mBuildNumber = mBuildNumber; 60 | } 61 | 62 | public void setModel(String mModel) { 63 | this.mModel = mModel; 64 | } 65 | 66 | public void setManufacturer(String mManufacturer) { 67 | this.mManufacturer = mManufacturer; 68 | } 69 | 70 | /** 71 | * Calculates a qualitative match score between two different Android device models for the 72 | * purposes of how likely they are to have similar Bluetooth signal level responses 73 | * @param otherModel 74 | * @return match quality, higher numbers are a better match 75 | */ 76 | public int matchScore(AndroidModel otherModel) { 77 | int score = 0; 78 | if (this.mManufacturer.equalsIgnoreCase(otherModel.mManufacturer)) { 79 | score = 1; 80 | } 81 | if (score ==1 && this.mModel.equals(otherModel.mModel)) { 82 | score = 2; 83 | } 84 | if (score == 2 && this.mBuildNumber.equals(otherModel.mBuildNumber)) { 85 | score = 3; 86 | } 87 | if (score == 3 && this.mVersion.equals(otherModel.mVersion)) { 88 | score = 4; 89 | } 90 | LogManager.d(TAG, "Score is %s for %s compared to %s", score, toString(), otherModel); 91 | return score; 92 | } 93 | 94 | @Override 95 | public String toString() { 96 | return ""+mManufacturer+";"+mModel+";"+mBuildNumber+";"+mVersion; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /lib/src/main/java/org/altbeacon/beacon/service/RangingData.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Radius Networks, Inc. 3 | * http://www.radiusnetworks.com 4 | * 5 | * @author David G. Young 6 | * 7 | * Licensed to the Apache Software Foundation (ASF) under one 8 | * or more contributor license agreements. See the NOTICE file 9 | * distributed with this work for additional information 10 | * regarding copyright ownership. The ASF licenses this file 11 | * to you under the Apache License, Version 2.0 (the 12 | * "License"); you may not use this file except in compliance 13 | * with the License. You may obtain a copy of the License at 14 | * 15 | * http://www.apache.org/licenses/LICENSE-2.0 16 | * 17 | * Unless required by applicable law or agreed to in writing, 18 | * software distributed under the License is distributed on an 19 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 20 | * KIND, either express or implied. See the License for the 21 | * specific language governing permissions and limitations 22 | * under the License. 23 | */ 24 | package org.altbeacon.beacon.service; 25 | 26 | import java.io.Serializable; 27 | import java.util.ArrayList; 28 | import java.util.Collection; 29 | 30 | import org.altbeacon.beacon.Beacon; 31 | import org.altbeacon.beacon.Region; 32 | 33 | import android.os.Bundle; 34 | 35 | /** 36 | * Internal class used to transfer ranging data between the BeaconService and the client 37 | * @hide 38 | */ 39 | public class RangingData { 40 | private static final String TAG = "RangingData"; 41 | private final Collection mBeacons; 42 | private final Region mRegion; 43 | private static final String REGION_KEY = "region"; 44 | private static final String BEACONS_KEY = "beacons"; 45 | 46 | public RangingData (Collection beacons, Region region) { 47 | synchronized (beacons) { 48 | this.mBeacons = beacons; 49 | } 50 | this.mRegion = region; 51 | } 52 | 53 | public Collection getBeacons() { 54 | return mBeacons; 55 | } 56 | public Region getRegion() { 57 | return mRegion; 58 | } 59 | 60 | public Bundle toBundle() { 61 | Bundle bundle = new Bundle(); 62 | bundle.putSerializable(REGION_KEY, mRegion); 63 | ArrayList serializableBeacons = new ArrayList(); 64 | for (Beacon beacon : mBeacons) { 65 | serializableBeacons.add(beacon); 66 | } 67 | bundle.putSerializable(BEACONS_KEY, serializableBeacons); 68 | 69 | return bundle; 70 | } 71 | public static RangingData fromBundle(Bundle bundle) { 72 | bundle.setClassLoader(Region.class.getClassLoader()); 73 | Region region = null; 74 | Collection beacons = null; 75 | if (bundle.get(BEACONS_KEY) != null) { 76 | beacons = (Collection) bundle.getSerializable(BEACONS_KEY); 77 | } 78 | if (bundle.get(REGION_KEY) != null) { 79 | region = (Region) bundle.getSerializable(REGION_KEY); 80 | } 81 | 82 | return new RangingData(beacons, region); 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /lib/src/main/java/org/altbeacon/beacon/service/RegionMonitoringState.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Radius Networks, Inc. 3 | * http://www.radiusnetworks.com 4 | * 5 | * @author David G. Young 6 | * 7 | * Licensed to the Apache Software Foundation (ASF) under one 8 | * or more contributor license agreements. See the NOTICE file 9 | * distributed with this work for additional information 10 | * regarding copyright ownership. The ASF licenses this file 11 | * to you under the Apache License, Version 2.0 (the 12 | * "License"); you may not use this file except in compliance 13 | * with the License. You may obtain a copy of the License at 14 | * 15 | * http://www.apache.org/licenses/LICENSE-2.0 16 | * 17 | * Unless required by applicable law or agreed to in writing, 18 | * software distributed under the License is distributed on an 19 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 20 | * KIND, either express or implied. See the License for the 21 | * specific language governing permissions and limitations 22 | * under the License. 23 | */ 24 | package org.altbeacon.beacon.service; 25 | 26 | import android.os.SystemClock; 27 | 28 | import org.altbeacon.beacon.BeaconManager; 29 | import org.altbeacon.beacon.logging.LogManager; 30 | 31 | import java.io.Serializable; 32 | 33 | public class RegionMonitoringState implements Serializable { 34 | private static final String TAG = RegionMonitoringState.class.getSimpleName(); 35 | private boolean inside = false; 36 | private long lastSeenTime = 0l; 37 | private final Callback callback; 38 | private transient boolean activeSinceAppLaunch = false; 39 | 40 | public RegionMonitoringState(Callback c) { 41 | callback = c; 42 | } 43 | 44 | public Callback getCallback() { 45 | return callback; 46 | } 47 | 48 | // returns true if it is newly inside 49 | public boolean markInside() { 50 | lastSeenTime = SystemClock.elapsedRealtime(); 51 | if (!inside) { 52 | inside = true; 53 | return true; 54 | } 55 | return false; 56 | } 57 | 58 | public void markOutside() { 59 | inside = false; 60 | lastSeenTime = 0l; 61 | } 62 | 63 | public boolean markOutsideIfExpired() { 64 | if (inside) { 65 | if (lastSeenTime > 0 && SystemClock.elapsedRealtime() - lastSeenTime > BeaconManager.getRegionExitPeriod()) { 66 | LogManager.d(TAG, "We are newly outside the region because the lastSeenTime of %s " 67 | + "was %s seconds ago, and that is over the expiration duration " 68 | + "of %s", lastSeenTime, SystemClock.elapsedRealtime() - lastSeenTime, 69 | BeaconManager.getRegionExitPeriod()); 70 | markOutside(); 71 | return true; 72 | } 73 | } 74 | return false; 75 | } 76 | 77 | public boolean getInside() { 78 | return inside; 79 | } 80 | 81 | public boolean getActiveSinceAppLaunch() { return activeSinceAppLaunch; } 82 | public void setActiveSinceAppLaunch(boolean active) { activeSinceAppLaunch = active; } 83 | } 84 | -------------------------------------------------------------------------------- /lib/src/main/java/org/altbeacon/beacon/logging/ApiTrackingLogger.kt: -------------------------------------------------------------------------------- 1 | package org.altbeacon.beacon.logging 2 | 3 | import android.util.Log 4 | import java.lang.StringBuilder 5 | import java.text.SimpleDateFormat 6 | import java.util.* 7 | import kotlin.collections.ArrayList 8 | 9 | class ApiTrackingLogger: Logger { 10 | private var apiCalls = ArrayList() 11 | private val dateformat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS") 12 | protected fun formatString(message: String?, vararg args: Any?): String { 13 | // If no varargs are supplied, treat it as a request to log the string without formatting. 14 | try { 15 | return if (args.size == 0 || message == null) message!! else String.format(message!!, *args) 16 | } 17 | catch (e: java.util.MissingFormatArgumentException) { 18 | return message!! 19 | } 20 | } 21 | override fun v(tag: String?, message: String?, vararg args: Any?) { 22 | trackApiLogs(message) 23 | Log.v(tag, VerboseAndroidLogger().formatString(message, *args)) 24 | } 25 | 26 | override fun v(t: Throwable?, tag: String?, message: String?, vararg args: Any?) { 27 | trackApiLogs(message) 28 | Log.v(tag, formatString(message, *args), t) 29 | } 30 | 31 | override fun d(tag: String?, message: String?, vararg args: Any?) { 32 | trackApiLogs(message) 33 | Log.d(tag, formatString(message, *args)) 34 | } 35 | 36 | override fun d(t: Throwable?, tag: String?, message: String?, vararg args: Any?) { 37 | trackApiLogs(message) 38 | Log.d(tag, formatString(message, *args), t) 39 | } 40 | 41 | override fun i(tag: String?, message: String?, vararg args: Any?) { 42 | trackApiLogs(message) 43 | Log.i(tag, formatString(message, *args)) 44 | } 45 | 46 | override fun i(t: Throwable?, tag: String?, message: String?, vararg args: Any?) { 47 | trackApiLogs(message) 48 | Log.i(tag, formatString(message, *args), t) 49 | } 50 | 51 | override fun w(tag: String?, message: String?, vararg args: Any?) { 52 | trackApiLogs(message) 53 | Log.w(tag, formatString(message, *args)) 54 | } 55 | 56 | override fun w(t: Throwable?, tag: String?, message: String?, vararg args: Any?) { 57 | trackApiLogs(message) 58 | Log.w(tag, formatString(message, *args), t) 59 | } 60 | 61 | override fun e(tag: String?, message: String?, vararg args: Any?) { 62 | trackApiLogs(message) 63 | Log.e(tag, formatString(message, *args)) 64 | } 65 | 66 | override fun e(t: Throwable?, tag: String?, message: String?, vararg args: Any?) { 67 | trackApiLogs(message) 68 | Log.e(tag, formatString(message, *args), t) 69 | } 70 | private fun trackApiLogs(message: String?) { 71 | if (message != null && message.indexOf("API") == 0) { 72 | val sb = StringBuilder() 73 | sb.append(dateformat.format(Date())) 74 | sb.append(" ") 75 | sb.append(message) 76 | apiCalls.add(sb.toString()) 77 | } 78 | } 79 | public fun getApiCalls(): Array { 80 | return apiCalls.toTypedArray() 81 | } 82 | public fun clearApiCalls() { 83 | apiCalls.clear() 84 | } 85 | 86 | } -------------------------------------------------------------------------------- /lib/src/test/java/org/altbeacon/beacon/BeaconManagerTest.java: -------------------------------------------------------------------------------- 1 | package org.altbeacon.beacon; 2 | 3 | import org.altbeacon.beacon.logging.LogManager; 4 | import org.altbeacon.beacon.logging.Loggers; 5 | import org.altbeacon.beacon.simulator.BeaconSimulator; 6 | import org.junit.Before; 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | import org.robolectric.RobolectricTestRunner; 10 | import org.robolectric.RuntimeEnvironment; 11 | import org.robolectric.annotation.Config; 12 | 13 | import java.util.Collections; 14 | import java.util.List; 15 | 16 | import static org.junit.Assert.*; 17 | 18 | @RunWith(RobolectricTestRunner.class) 19 | @Config(sdk = 28) 20 | public class BeaconManagerTest { 21 | 22 | @Before 23 | public void before() { 24 | org.robolectric.shadows.ShadowLog.stream = System.err; 25 | LogManager.setLogger(Loggers.verboseLogger()); 26 | LogManager.setVerboseLoggingEnabled(true); 27 | BeaconManager.setsManifestCheckingDisabled(true); 28 | BeaconManager.setBeaconSimulator(new BeaconSimulator() { 29 | @Override 30 | public List getBeacons() { 31 | return Collections.emptyList(); 32 | } 33 | }); 34 | } 35 | 36 | @Test 37 | public void startRangingBeaconsInRegionMultipleInvocationsTest() throws Exception { 38 | BeaconManager beaconManager = BeaconManager 39 | .getInstanceForApplication(RuntimeEnvironment.application); 40 | 41 | String id = "id"; 42 | Region region1 = new Region(id, Collections.emptyList()); 43 | Region region2 = new Region(id, "00:11:22:33:FF:EE"); 44 | 45 | beaconManager.startRangingBeaconsInRegion(region1); 46 | assertEquals(beaconManager.getRangedRegions().size(), 1); 47 | assertSame(beaconManager.getRangedRegions().iterator().next(), region1); 48 | assertNotSame(beaconManager.getRangedRegions().iterator().next(), region2); 49 | 50 | beaconManager.startRangingBeaconsInRegion(region2); 51 | assertEquals(beaconManager.getRangedRegions().size(), 1); 52 | assertNotSame(beaconManager.getRangedRegions().iterator().next(), region1); 53 | assertSame(beaconManager.getRangedRegions().iterator().next(), region2); 54 | 55 | Region region3 = new Region(id + "-other", Collections.emptyList()); 56 | beaconManager.startRangingBeaconsInRegion(region3); 57 | assertEquals(beaconManager.getRangedRegions().size(), 2); 58 | } 59 | 60 | @Test 61 | public void stopRangingBeaconsInRegionTest() throws Exception { 62 | BeaconManager beaconManager = BeaconManager 63 | .getInstanceForApplication(RuntimeEnvironment.application); 64 | 65 | String id = "id"; 66 | Region region1 = new Region(id, Collections.emptyList()); 67 | Region region2 = new Region(id, "00:11:22:33:FF:EE"); 68 | Region region3 = new Region(id + "-other", Collections.emptyList()); 69 | 70 | beaconManager.startRangingBeaconsInRegion(region1); 71 | beaconManager.startRangingBeaconsInRegion(region2); 72 | beaconManager.startRangingBeaconsInRegion(region3); 73 | assertEquals(beaconManager.getRangedRegions().size(), 2); 74 | 75 | beaconManager.stopRangingBeaconsInRegion(region1); 76 | assertEquals(beaconManager.getRangedRegions().size(), 1); 77 | 78 | beaconManager.stopRangingBeaconsInRegion(region3); 79 | assertEquals(beaconManager.getRangedRegions().size(), 0); 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /lib/src/main/java/org/altbeacon/beacon/BeaconLocalBroadcastProcessor.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Radius Networks, Inc. 3 | * http://www.radiusnetworks.com 4 | * 5 | * @author David G. Young 6 | * 7 | * Licensed to the Apache Software Foundation (ASF) under one 8 | * or more contributor license agreements. See the NOTICE file 9 | * distributed with this work for additional information 10 | * regarding copyright ownership. The ASF licenses this file 11 | * to you under the Apache License, Version 2.0 (the 12 | * "License"); you may not use this file except in compliance 13 | * with the License. You may obtain a copy of the License at 14 | * 15 | * http://www.apache.org/licenses/LICENSE-2.0 16 | * 17 | * Unless required by applicable law or agreed to in writing, 18 | * software distributed under the License is distributed on an 19 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 20 | * KIND, either express or implied. See the License for the 21 | * specific language governing permissions and limitations 22 | * under the License. 23 | */ 24 | package org.altbeacon.beacon; 25 | 26 | import android.content.BroadcastReceiver; 27 | import android.content.Context; 28 | import android.content.Intent; 29 | import android.content.IntentFilter; 30 | import androidx.annotation.NonNull; 31 | 32 | import org.altbeacon.beacon.logging.LogManager; 33 | 34 | /** 35 | * Converts internal intents to notifier callbacks 36 | * 37 | * This is used with ScanJob and supports delivering intents even under Android O background 38 | * restrictions preventing starting a new IntentService. 39 | * 40 | * It is not used with the BeaconService, if running in a separate process, as local broadcast 41 | * intents cannot be deliverd across different processes which the BeaconService supports. 42 | * 43 | * @see BeaconIntentProcessor for the equivalent use with BeaconService in a separate process. 44 | ** 45 | * Internal library class. Do not use directly from outside the library 46 | * 47 | * @hide 48 | */ 49 | public class BeaconLocalBroadcastProcessor { 50 | private static final String TAG = "BeaconLocalBroadcastProcessor"; 51 | private static BeaconLocalBroadcastProcessor mInstance = null; 52 | 53 | public static final String RANGE_NOTIFICATION = "org.altbeacon.beacon.range_notification"; 54 | public static final String MONITOR_NOTIFICATION = "org.altbeacon.beacon.monitor_notification"; 55 | 56 | public static synchronized BeaconLocalBroadcastProcessor getInstance(Context context) { 57 | if (mInstance == null) { 58 | mInstance = new BeaconLocalBroadcastProcessor(context); 59 | } 60 | return mInstance; 61 | } 62 | 63 | @NonNull 64 | private Context mContext; 65 | private BeaconLocalBroadcastProcessor() { 66 | 67 | } 68 | private BeaconLocalBroadcastProcessor(Context context) { 69 | mContext = context; 70 | 71 | } 72 | 73 | int registerCallCount = 0; 74 | public void register() { 75 | registerCallCount += 1; 76 | LogManager.d(TAG, "Register calls: global="+registerCallCount); 77 | unregister(); 78 | } 79 | 80 | public void unregister() { 81 | registerCallCount -= 1; 82 | } 83 | 84 | 85 | public void onReceive(Context context, Intent intent) { 86 | if (registerCallCount > 0) { 87 | new IntentHandler().convertIntentsToCallbacks(context, intent); 88 | } 89 | } 90 | } -------------------------------------------------------------------------------- /lib/src/main/java/org/altbeacon/beacon/service/Callback.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Radius Networks, Inc. 3 | * http://www.radiusnetworks.com 4 | * 5 | * @author David G. Young 6 | * 7 | * Licensed to the Apache Software Foundation (ASF) under one 8 | * or more contributor license agreements. See the NOTICE file 9 | * distributed with this work for additional information 10 | * regarding copyright ownership. The ASF licenses this file 11 | * to you under the Apache License, Version 2.0 (the 12 | * "License"); you may not use this file except in compliance 13 | * with the License. You may obtain a copy of the License at 14 | * 15 | * http://www.apache.org/licenses/LICENSE-2.0 16 | * 17 | * Unless required by applicable law or agreed to in writing, 18 | * software distributed under the License is distributed on an 19 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 20 | * KIND, either express or implied. See the License for the 21 | * specific language governing permissions and limitations 22 | * under the License. 23 | */ 24 | package org.altbeacon.beacon.service; 25 | 26 | import android.content.ComponentName; 27 | import android.content.Context; 28 | import android.content.Intent; 29 | import android.os.Bundle; 30 | 31 | import org.altbeacon.beacon.BeaconIntentProcessor; 32 | import org.altbeacon.beacon.BeaconManager; 33 | import org.altbeacon.beacon.IntentHandler; 34 | import org.altbeacon.beacon.logging.LogManager; 35 | 36 | import java.io.IOException; 37 | import java.io.Serializable; 38 | 39 | public class Callback implements Serializable { 40 | private static final String TAG = "Callback"; 41 | 42 | //TODO: Remove this constructor in favor of an empty one, as the package name is no longer needed 43 | public Callback(String intentPackageName) { 44 | } 45 | 46 | /** 47 | * Tries making the callback, first via messenger, then via intent 48 | * 49 | * @param context 50 | * @param dataName 51 | * @param data 52 | * @return false if it callback cannot be made 53 | */ 54 | public boolean call(Context context, String dataName, Bundle data) { 55 | boolean success = false; 56 | 57 | Intent intent = new Intent(); 58 | intent.setComponent(new ComponentName(context.getPackageName(), "org.altbeacon.beacon.BeaconIntentProcessor")); 59 | intent.putExtra(dataName, data); 60 | BeaconManager beaconManager = BeaconManager.getInstanceForApplication(context); 61 | if (beaconManager.isScannerInDifferentProcess()) { 62 | LogManager.d(TAG, "attempting callback via global broadcast intent: %s",intent.getComponent()); 63 | try { 64 | context.startService(intent); 65 | success = true; 66 | } catch (Exception e) { 67 | LogManager.e( 68 | TAG, 69 | "Failed attempting to start service: " + intent.getComponent().flattenToString(), 70 | e 71 | ); 72 | } 73 | } 74 | else { 75 | LogManager.d(TAG, "attempting callback via direct method call"); 76 | new IntentHandler().convertIntentsToCallbacks(context, intent); 77 | } 78 | return success; 79 | } 80 | 81 | @SuppressWarnings("unused") 82 | private void readObject(java.io.ObjectInputStream in) 83 | throws IOException, ClassNotFoundException { 84 | in.defaultReadObject(); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /lib/src/main/java/org/altbeacon/bluetooth/Pdu.java: -------------------------------------------------------------------------------- 1 | package org.altbeacon.bluetooth; 2 | 3 | import android.annotation.TargetApi; 4 | import android.os.Build; 5 | 6 | /** 7 | * Converts a byte string from a Bluetooth LE scan into a Payload Data Unit (PDU) 8 | * Created by dyoung on 4/14/15. 9 | */ 10 | public class Pdu { 11 | private static final String TAG = "Pdu"; 12 | public static final byte MANUFACTURER_DATA_AD_TYPE = (byte) 0xff; 13 | public static final byte GATT_SERVICE_DATA_UUID_16_BIT_AD_TYPE = (byte) 0x16; 14 | public static final byte GATT_SERVICE_DATA_UUID_32_BIT_AD_TYPE = (byte) 0x20; 15 | public static final byte GATT_SERVICE_DATA_UUID_128_BIT_AD_TYPE = (byte) 0x21; 16 | public static final byte GATT_SERVICE_COMPLETE_UUID_128_BIT_AD_TYPE = (byte) 0x07; 17 | 18 | private byte mType; 19 | private int mDeclaredLength; 20 | private int mStartIndex; 21 | private int mEndIndex; 22 | private byte[] mBytes; 23 | 24 | /** 25 | * Parse a PDU from a byte array looking offset by startIndex 26 | * @param bytes 27 | * @param startIndex 28 | * @return 29 | */ 30 | 31 | @TargetApi(Build.VERSION_CODES.GINGERBREAD) 32 | public static Pdu parse(byte[] bytes, int startIndex) { 33 | Pdu pdu = null; 34 | if (bytes.length-startIndex >= 2) { 35 | byte length = bytes[startIndex]; 36 | if (length > 0) { 37 | byte type = bytes[startIndex + 1]; 38 | int firstIndex = startIndex + 2; 39 | if (firstIndex < bytes.length) { 40 | pdu = new Pdu(); 41 | // The End index is the startIndex + the length, because the first byte is the 42 | // length field and the length field does not include the length field itself in 43 | // the count 44 | pdu.mEndIndex = startIndex + length; 45 | if (pdu.mEndIndex >= bytes.length) { 46 | pdu.mEndIndex = bytes.length - 1; 47 | } 48 | pdu.mType = type; 49 | pdu.mDeclaredLength = length; 50 | pdu.mStartIndex = firstIndex; 51 | pdu.mBytes = bytes; 52 | } 53 | } 54 | } 55 | return pdu; 56 | } 57 | 58 | /** 59 | * PDU type field 60 | * @return 61 | */ 62 | public byte getType() { 63 | return mType; 64 | } 65 | 66 | /** 67 | * PDU length from header 68 | * @return 69 | */ 70 | public int getDeclaredLength() { 71 | return mDeclaredLength; 72 | } 73 | 74 | /** 75 | * Actual PDU length (may be less than declared length if fewer bytes are actually available.) 76 | * @return 77 | */ 78 | public int getActualLength() { 79 | return mEndIndex - mStartIndex + 1; 80 | } 81 | 82 | /** 83 | * Start index within byte buffer of PDU 84 | * This is the start of the payload data that starts after the length and the type, so the PDU 85 | * actually starts two bytes earlier 86 | * @return 87 | */ 88 | public int getStartIndex() { 89 | return mStartIndex; 90 | } 91 | 92 | /** 93 | * End index within byte buffer of PDU 94 | * @return 95 | */ 96 | public int getEndIndex() { 97 | return mEndIndex; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /lib/src/test/java/org/altbeacon/beacon/BeaconTransmitterTest.java: -------------------------------------------------------------------------------- 1 | package org.altbeacon.beacon; 2 | 3 | import android.content.Context; 4 | import android.util.Log; 5 | 6 | import org.junit.Test; 7 | import org.junit.runner.RunWith; 8 | import org.robolectric.RobolectricTestRunner; 9 | import org.robolectric.annotation.Config; 10 | import org.robolectric.RuntimeEnvironment; 11 | 12 | import java.util.Arrays; 13 | 14 | import static junit.framework.Assert.assertEquals; 15 | 16 | @Config(sdk = 28) 17 | 18 | /** 19 | * Created by dyoung on 7/22/14. 20 | */ 21 | @RunWith(RobolectricTestRunner.class) 22 | public class BeaconTransmitterTest { 23 | private static final String TAG = "BeaconTransmitterTest"; 24 | 25 | @Test 26 | public void testBeaconAdvertisingBytes() { 27 | org.robolectric.shadows.ShadowLog.stream = System.err; 28 | Context context = RuntimeEnvironment.application; 29 | 30 | Beacon beacon = new Beacon.Builder() 31 | .setId1("2f234454-cf6d-4a0f-adf2-f4911ba9ffa6") 32 | .setId2("1") 33 | .setId3("2") 34 | .setManufacturer(0x0118) 35 | .setTxPower(-59) 36 | .setDataFields(Arrays.asList(0L)) 37 | .build(); 38 | BeaconParser beaconParser = new BeaconParser() 39 | .setBeaconLayout("m:2-3=beac,i:4-19,i:20-21,i:22-23,p:24-24,d:25-25"); 40 | byte[] data = beaconParser.getBeaconAdvertisementData(beacon); 41 | // BeaconTransmitter beaconTransmitter = new BeaconTransmitter(context, beaconParser); 42 | // TODO: can't actually start transmitter here because Robolectric does not support API 21 43 | 44 | assertEquals("Data should be 24 bytes long", 24, data.length); 45 | String byteString = ""; 46 | for (int i = 0; i < data.length; i++) { 47 | byteString += String.format("%02X", data[i]); 48 | byteString += " "; 49 | } 50 | assertEquals("Advertisement bytes should be as expected", "BE AC 2F 23 44 54 CF 6D 4A 0F AD F2 F4 91 1B A9 FF A6 00 01 00 02 C5 00 ", byteString); 51 | } 52 | 53 | @Test 54 | public void testBeaconAdvertisingBytesForEddystone() { 55 | org.robolectric.shadows.ShadowLog.stream = System.err; 56 | Context context = RuntimeEnvironment.application; 57 | 58 | Beacon beacon = new Beacon.Builder() 59 | .setId1("0x2f234454f4911ba9ffa6") 60 | .setId2("0x000000000001") 61 | .setManufacturer(0x0118) 62 | .setTxPower(-59) 63 | .build(); 64 | BeaconParser beaconParser = new BeaconParser() 65 | .setBeaconLayout("s:0-1=feaa,m:2-2=00,p:3-3:-41,i:4-13,i:14-19"); 66 | byte[] data = beaconParser.getBeaconAdvertisementData(beacon); 67 | // BeaconTransmitter beaconTransmitter = new BeaconTransmitter(context, beaconParser); 68 | // TODO: can't actually start transmitter here because Robolectric does not support API 21 69 | 70 | String byteString = ""; 71 | for (int i = 0; i < data.length; i++) { 72 | byteString += String.format("%02X", data[i]); 73 | byteString += " "; 74 | } 75 | Log.d(TAG, "Advertising bytes are "+byteString ); 76 | assertEquals("Data should be 24 bytes long", 18, data.length); 77 | assertEquals("Advertisement bytes should be as expected", "00 C5 2F 23 44 54 F4 91 1B A9 FF A6 00 00 00 00 00 01 ", byteString); 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /lib/src/main/java/org/altbeacon/beacon/service/ExtraDataBeaconTracker.java: -------------------------------------------------------------------------------- 1 | package org.altbeacon.beacon.service; 2 | 3 | import androidx.annotation.NonNull; 4 | import androidx.annotation.Nullable; 5 | 6 | import org.altbeacon.beacon.Beacon; 7 | 8 | import java.io.Serializable; 9 | import java.util.HashMap; 10 | 11 | /** 12 | * Keeps track of beacons that have ever been seen and 13 | * merges them together depending on configured beacon parsers 14 | * Created by dyoung on 5/5/15. 15 | */ 16 | public class ExtraDataBeaconTracker implements Serializable { 17 | private static final String TAG = "BeaconTracker"; 18 | 19 | /** 20 | * This is a lookup table to find tracked beacons by the calculated beacon key 21 | */ 22 | @NonNull 23 | private final HashMap> mBeaconsByKey = new HashMap<>(); 24 | 25 | private final boolean matchBeaconsByServiceUUID; 26 | 27 | public ExtraDataBeaconTracker() { 28 | this(true); 29 | } 30 | 31 | public ExtraDataBeaconTracker(boolean matchBeaconsByServiceUUID) { 32 | this.matchBeaconsByServiceUUID = matchBeaconsByServiceUUID; 33 | } 34 | 35 | /** 36 | * Tracks a beacon. For Gatt-based beacons, returns a merged copy of fields from multiple 37 | * frames. Returns null when passed a Gatt-based beacon that has is only extra beacon data. 38 | */ 39 | @Nullable 40 | public synchronized Beacon track(@NonNull Beacon beacon) { 41 | Beacon trackedBeacon = null; 42 | if (beacon.isMultiFrameBeacon() || beacon.getServiceUuid() != -1) { 43 | trackedBeacon = trackGattBeacon(beacon); 44 | } 45 | else { 46 | trackedBeacon = beacon; 47 | } 48 | return trackedBeacon; 49 | } 50 | 51 | /** 52 | * The following code is for dealing with merging data fields in beacons 53 | */ 54 | @Nullable 55 | private Beacon trackGattBeacon(@NonNull Beacon beacon) { 56 | if (beacon.isExtraBeaconData()) { 57 | updateTrackedBeacons(beacon); 58 | return null; 59 | } 60 | 61 | String key = getBeaconKey(beacon); 62 | HashMap matchingTrackedBeacons = mBeaconsByKey.get(key); 63 | if (null == matchingTrackedBeacons) { 64 | matchingTrackedBeacons = new HashMap<>(); 65 | } 66 | else { 67 | Beacon trackedBeacon = matchingTrackedBeacons.values().iterator().next(); 68 | beacon.setExtraDataFields(trackedBeacon.getExtraDataFields()); 69 | } 70 | matchingTrackedBeacons.put(beacon.hashCode(), beacon); 71 | mBeaconsByKey.put(key, matchingTrackedBeacons); 72 | 73 | return beacon; 74 | } 75 | 76 | private void updateTrackedBeacons(@NonNull Beacon beacon) { 77 | HashMap matchingTrackedBeacons = mBeaconsByKey.get(getBeaconKey(beacon)); 78 | if (null != matchingTrackedBeacons) { 79 | for (Beacon matchingTrackedBeacon : matchingTrackedBeacons.values()) { 80 | matchingTrackedBeacon.setRssi(beacon.getRssi()); 81 | matchingTrackedBeacon.setExtraDataFields(beacon.getDataFields()); 82 | } 83 | } 84 | } 85 | 86 | private String getBeaconKey(@NonNull Beacon beacon) { 87 | if (matchBeaconsByServiceUUID) { 88 | return beacon.getBluetoothAddress() + beacon.getServiceUuid(); 89 | } else { 90 | return beacon.getBluetoothAddress(); 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /lib/src/main/java/org/altbeacon/beacon/service/RunningAverageRssiFilter.java: -------------------------------------------------------------------------------- 1 | package org.altbeacon.beacon.service; 2 | 3 | import android.os.SystemClock; 4 | import androidx.annotation.RestrictTo; 5 | import androidx.annotation.RestrictTo.Scope; 6 | 7 | import org.altbeacon.beacon.logging.LogManager; 8 | 9 | import java.util.ArrayList; 10 | import java.util.Collections; 11 | import java.util.Iterator; 12 | 13 | /** 14 | * Calculate a RSSI value on base of an arbitrary list of measured RSSI values 15 | * The list is clipped by a certain length at start and end and the average 16 | * is calculate by simple arithmetic average 17 | */ 18 | public class RunningAverageRssiFilter implements RssiFilter { 19 | 20 | private static final String TAG = "RunningAverageRssiFilter"; 21 | public static final long DEFAULT_SAMPLE_EXPIRATION_MILLISECONDS = 20000; /* 20 seconds */ 22 | private static long sampleExpirationMilliseconds = DEFAULT_SAMPLE_EXPIRATION_MILLISECONDS; 23 | private ArrayList mMeasurements = new ArrayList(); 24 | 25 | @Override 26 | public void addMeasurement(Integer rssi) { 27 | Measurement measurement = new Measurement(); 28 | measurement.rssi = rssi; 29 | measurement.timestamp = SystemClock.elapsedRealtime(); 30 | mMeasurements.add(measurement); 31 | } 32 | 33 | @Override 34 | public boolean noMeasurementsAvailable() { 35 | return mMeasurements.size() == 0; 36 | } 37 | 38 | 39 | @Override 40 | public int getMeasurementCount() { return mMeasurements.size(); } 41 | 42 | @Override 43 | public double calculateRssi() { 44 | refreshMeasurements(); 45 | int size = mMeasurements.size(); 46 | int startIndex = 0; 47 | int endIndex = size -1; 48 | if (size > 2) { 49 | startIndex = size/10+1; 50 | endIndex = size-size/10-2; 51 | } 52 | 53 | double sum = 0; 54 | for (int i = startIndex; i <= endIndex; i++) { 55 | sum += mMeasurements.get(i).rssi; 56 | } 57 | double runningAverage = sum/(endIndex-startIndex+1); 58 | 59 | LogManager.d(TAG, "Running average mRssi based on %s measurements: %s", 60 | size, runningAverage); 61 | return runningAverage; 62 | } 63 | 64 | private synchronized void refreshMeasurements() { 65 | ArrayList newMeasurements = new ArrayList(); 66 | Iterator iterator = mMeasurements.iterator(); 67 | while (iterator.hasNext()) { 68 | Measurement measurement = iterator.next(); 69 | if (SystemClock.elapsedRealtime() - measurement.timestamp < sampleExpirationMilliseconds ) { 70 | newMeasurements.add(measurement); 71 | } 72 | } 73 | mMeasurements = newMeasurements; 74 | Collections.sort(mMeasurements); 75 | } 76 | 77 | private class Measurement implements Comparable { 78 | Integer rssi; 79 | long timestamp; 80 | @Override 81 | public int compareTo(Measurement arg0) { 82 | return rssi.compareTo(arg0.rssi); 83 | } 84 | } 85 | 86 | public static void setSampleExpirationMilliseconds(long newSampleExpirationMilliseconds) { 87 | sampleExpirationMilliseconds = newSampleExpirationMilliseconds; 88 | } 89 | 90 | @RestrictTo(Scope.TESTS) 91 | static long getSampleExpirationMilliseconds() { 92 | return sampleExpirationMilliseconds; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /lib/src/test/java/org/altbeacon/beacon/OverflowAreaBeaconTest.java: -------------------------------------------------------------------------------- 1 | package org.altbeacon.beacon; 2 | 3 | import android.content.Context; 4 | 5 | import org.altbeacon.bluetooth.BleAdvertisement; 6 | import org.junit.Assert; 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | import org.robolectric.RobolectricTestRunner; 10 | import org.robolectric.RuntimeEnvironment; 11 | import org.robolectric.annotation.Config; 12 | import static junit.framework.Assert.assertEquals; 13 | import static junit.framework.Assert.assertNotNull; 14 | 15 | @Config(sdk = 28) 16 | 17 | /** 18 | * Created by David G. Young 5/19/2020 19 | */ 20 | @RunWith(RobolectricTestRunner.class) 21 | public class OverflowAreaBeaconTest { 22 | 23 | @Test 24 | public void testDetectsOverfowAreadBeacon() { 25 | org.robolectric.shadows.ShadowLog.stream = System.err; 26 | Context context = RuntimeEnvironment.application; 27 | BeaconManager.getInstanceForApplication(context).setDebug(true); 28 | byte[] bytes = hexStringToByteArray("02011a020a0c0eff4c000f05a0336aa5f110025b0c14ff4c000156fe87490000000000000000000000000000000000000000000000000000000000000000"); 29 | BeaconParser parser = new BeaconParser().setBeaconLayout("m:2-2=01,i:3-18,p:-:-59"); 30 | Beacon beacon = parser.fromScanData(bytes, -55, null, 123456L); 31 | assertNotNull("beacon should be not null if parsed successfully", beacon); 32 | assertEquals("id should be parsed", "56fe8749-0000-0000-0000-000000000000", beacon.getId1().toString()); 33 | } 34 | 35 | public void testDetectsOverfowAreadBeaconInOverflowArea() { 36 | org.robolectric.shadows.ShadowLog.stream = System.err; 37 | Context context = RuntimeEnvironment.application; 38 | BeaconManager.getInstanceForApplication(context).setDebug(true); 39 | byte[] bytes = hexStringToByteArray("02011a020a0c0eff4c000f05a0336aa5f110025b0c0000000000000000000014ff4c000156fe874900000000000000000000000000000000000000000000"); 40 | BeaconParser parser = new BeaconParser().setBeaconLayout("m:2-2=01,i:3-18,p:-:-59"); 41 | Beacon beacon = parser.fromScanData(bytes, -55, null, 123456L); 42 | assertNotNull("beacon should be not null if parsed successfully", beacon); 43 | assertEquals("id should be parsed", "56fe8749-0000-0000-0000-000000000000", beacon.getId1().toString()); 44 | } 45 | 46 | public static byte[] hexStringToByteArray(String s) { 47 | int len = s.length(); 48 | byte[] data = new byte[len / 2]; 49 | for (int i = 0; i < len; i += 2) { 50 | data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) 51 | + Character.digit(s.charAt(i+1), 16)); 52 | } 53 | return data; 54 | } 55 | 56 | @Test 57 | public void testCanParsePdus() { 58 | byte[] bytes = hexStringToByteArray("02011a020a0c0eff4c000f05a0336aa5f110025b0c14ff4c000156fe87490000000000000000000000000000000000000000000000000000000000000000"); 59 | BleAdvertisement bleAdvert = new BleAdvertisement(bytes); 60 | Assert.assertEquals("should find four PDUs", 4, bleAdvert.getPdus().size()); 61 | Assert.assertEquals("First PDU should be flags type 1", 1, bleAdvert.getPdus().get(0).getType()); 62 | Assert.assertEquals("Second PDU should be type 10", 10, bleAdvert.getPdus().get(1).getType()); 63 | Assert.assertEquals("Third PDU should be man type 0xff", -1, bleAdvert.getPdus().get(2).getType()); 64 | Assert.assertEquals("fourth PDU should be man type 0xff", -1, bleAdvert.getPdus().get(3).getType()); 65 | 66 | } 67 | } -------------------------------------------------------------------------------- /lib/src/main/java/org/altbeacon/beacon/utils/PermissionsInspector.kt: -------------------------------------------------------------------------------- 1 | package org.altbeacon.beacon.utils 2 | 3 | import android.content.Context 4 | import android.content.pm.PackageInfo 5 | import android.content.pm.PackageManager 6 | import android.os.Build 7 | import org.altbeacon.beacon.logging.LogManager 8 | 9 | 10 | class PermissionsInspector(private val context: Context) { 11 | fun hasDeclaredBluetoothScanPermissions(): Boolean { 12 | var ok = true 13 | if (!hasPermission(android.Manifest.permission.BLUETOOTH)) { 14 | LogManager.e( 15 | TAG, 16 | "BLUETOOTH permission not declared in AndroidManifest.xml. Will not be able to scan for bluetooth beacons" 17 | ) 18 | ok = false 19 | } 20 | if (!hasPermission(android.Manifest.permission.BLUETOOTH_ADMIN)) { 21 | LogManager.e( 22 | TAG, 23 | "BLUETOOTH_ADMIN permission not declared in AndroidManifest.xml. Will not be able to scan for bluetooth beacons" 24 | ) 25 | ok = false 26 | } 27 | if (!hasPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) && !hasPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION)) { 28 | LogManager.e( 29 | TAG, 30 | "Neither ACCESS_FINE_LOCATION nor ACCESS_COARSE_LOCATION permission declared in AndroidManifest.xml. Will not be able to scan for bluetooth beacons" 31 | ) 32 | ok = false 33 | } 34 | if (hasPermission(android.Manifest.permission.BLUETOOTH_SCAN, PackageInfo.REQUESTED_PERMISSION_NEVER_FOR_LOCATION)) { 35 | LogManager.e( 36 | TAG, 37 | "The neverForLocation permission flag is attached to BLUETOOTH_SCAN permission AndroidManifest.xml. This will block detection of bluetooth beacons. Please remove this from your AndroidManifest.xml, and if you don't see it, check the merged manifest in Android Studio, because it may have been added by another library you are using." 38 | ) 39 | ok = false 40 | } 41 | 42 | if (!hasPermission(android.Manifest.permission.ACCESS_BACKGROUND_LOCATION)) { 43 | LogManager.w( 44 | TAG, 45 | "ACCESS_BACKGROUND_LOCATION permission not declared in AndroidManifest.xml. Will not be able to scan for bluetooth beacons" 46 | ) 47 | } 48 | 49 | return ok 50 | } 51 | fun hasPermission(permission: String, permissionFlag: Int? = null): Boolean { 52 | try { 53 | val info: PackageInfo = context.getPackageManager().getPackageInfo( 54 | context.packageName, 55 | PackageManager.GET_PERMISSIONS 56 | ) 57 | if (info.requestedPermissions != null) { 58 | for (p in info.requestedPermissions) { 59 | if (p == permission) { 60 | if (permissionFlag != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { 61 | for (flag in info.requestedPermissionsFlags) { 62 | if ((flag and permissionFlag) != 0) { 63 | return true // permission flag found 64 | } 65 | } 66 | return false // permission flag not found 67 | } 68 | return true 69 | } 70 | } 71 | } 72 | } catch (e: RuntimeException) { 73 | LogManager.e(TAG, "Can't read permissions") 74 | } 75 | return false 76 | } 77 | companion object { 78 | private val TAG = PermissionsInspector::class.java.simpleName 79 | } 80 | 81 | } -------------------------------------------------------------------------------- /lib/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.library' 3 | id("org.jetbrains.dokka") version "1.5.0" 4 | } 5 | apply plugin: 'kotlin-android' 6 | 7 | android.buildFeatures.buildConfig true 8 | 9 | android { 10 | compileSdkVersion 34 11 | buildToolsVersion "34.0.0" 12 | 13 | kotlinOptions { 14 | jvmTarget = "17" 15 | } 16 | 17 | defaultConfig { 18 | minSdkVersion 14 19 | targetSdkVersion 34 20 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 21 | consumerProguardFiles "consumer-rules.pro" 22 | // The hack below is because VERSION_NAME has been removed from BuildConfig as of Gradle Plugin 4.1 23 | def addConstant = {constantName, constantValue -> 24 | manifestPlaceholders += [ (constantName):constantValue] 25 | buildConfigField "String", "${constantName}", "\"${constantValue}\"" 26 | } 27 | addConstant("VERSION_NAME", version) 28 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 29 | } 30 | 31 | buildTypes { 32 | release { 33 | minifyEnabled false 34 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 35 | } 36 | } 37 | compileOptions { 38 | sourceCompatibility JavaVersion.VERSION_17 39 | targetCompatibility JavaVersion.VERSION_17 40 | } 41 | packagingOptions { 42 | exclude 'LICENSE.txt' 43 | exclude 'META-INF/LICENSE' 44 | exclude 'META-INF/LICENSE.txt' 45 | exclude 'META-INF/NOTICE' 46 | } 47 | 48 | testOptions { 49 | execution 'ANDROIDX_TEST_ORCHESTRATOR' 50 | animationsDisabled true 51 | unitTests { 52 | includeAndroidResources = true 53 | } 54 | } 55 | lintOptions { 56 | abortOnError false 57 | } 58 | 59 | namespace 'org.altbeacon.beacon' 60 | } 61 | 62 | dependencies { 63 | implementation 'androidx.appcompat:appcompat:1.2.0' // 1.6.1 requires targeting android SDK 33 64 | testImplementation 'junit:junit:4.13.2' 65 | testImplementation 'org.hamcrest:hamcrest:2.2' 66 | androidTestImplementation 'androidx.test.ext:junit:1.1.5' 67 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' 68 | 69 | testImplementation 'junit:junit:4.13.2' 70 | 71 | testImplementation 'org.robolectric:robolectric:4.11.1' 72 | testImplementation 'com.google.android:android-test:4.1.1.4' 73 | //testImplementation 'com.squareup:fest-android:1.0.8@aar' 74 | testImplementation 'org.mockito:mockito-core:2.23.4' 75 | 76 | androidTestUtil 'androidx.test:orchestrator:1.4.2' 77 | 78 | androidTestImplementation 'androidx.test:rules:1.5.0' 79 | androidTestImplementation 'org.apache.commons:commons-math3:3.6.1' 80 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' 81 | implementation "androidx.core:core-ktx:1.12.0" 82 | implementation "androidx.lifecycle:lifecycle-process:2.6.2" 83 | 84 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 85 | dokkaHtmlPlugin("org.jetbrains.dokka:kotlin-as-java-plugin:1.5.0") 86 | } 87 | 88 | ext { 89 | PUBLISH_GROUP_ID = 'org.altbeacon' 90 | PUBLISH_VERSION = version 91 | PUBLISH_ARTIFACT_ID = 'android-beacon-library' 92 | } 93 | 94 | repositories { 95 | mavenCentral() 96 | } 97 | if (getGradle().getStartParameter().getTaskRequests().toString().contains("release") || 98 | getGradle().getStartParameter().getTaskRequests().toString().contains("mavenPublish") || 99 | getGradle().getStartParameter().getTaskRequests().toString().contains("closeAndReleaseRepository")) { 100 | apply from: "../gradle/publish-mavencentral.gradle" 101 | } 102 | 103 | -------------------------------------------------------------------------------- /lib/src/main/java/org/altbeacon/beacon/utils/DozeDetector.java: -------------------------------------------------------------------------------- 1 | package org.altbeacon.beacon.utils; 2 | 3 | import android.content.BroadcastReceiver; 4 | import android.content.Context; 5 | import android.content.IntentFilter; 6 | import android.os.Build; 7 | import android.os.PowerManager; 8 | 9 | import org.altbeacon.beacon.logging.LogManager; 10 | 11 | import java.lang.reflect.InvocationTargetException; 12 | import java.lang.reflect.Method; 13 | 14 | /** 15 | * Utility class for detecting when an Android device enters and leaves various Doze modes 16 | * April 02, 2019 17 | * (c) 2019 David G. Young 18 | * Apache 2 License 19 | */ 20 | public class DozeDetector { 21 | private static final String TAG = DozeDetector.class.getSimpleName(); 22 | 23 | public boolean isInDozeMode(Context context) { 24 | return isInFullDozeMode(context) == true || isInLightDozeMode(context) == true; 25 | } 26 | public boolean isInLightDozeMode(Context context) { 27 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 28 | PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); 29 | try { 30 | Method isLightDeviceIdleModeMethod = pm.getClass().getDeclaredMethod("isLightDeviceIdleMode"); 31 | boolean result = (boolean)isLightDeviceIdleModeMethod.invoke(pm); 32 | LogManager.d(TAG, "Light Doze mode? pm.isLightDeviceIdleMode: " + result); 33 | return result; 34 | } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { 35 | LogManager.d(TAG, "Reflection failed for isLightDeviceIdleMode: " + e.toString(), e); 36 | } 37 | } 38 | else { 39 | LogManager.d(TAG, "We can't be in doze mode as we are pre-Android M"); 40 | } 41 | return false; 42 | } 43 | public boolean isInFullDozeMode(Context context) { 44 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 45 | PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); 46 | 47 | if (pm == null) { 48 | LogManager.d(TAG, "Can't get PowerManager to check doze mode."); 49 | return false; 50 | } 51 | 52 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 53 | LogManager.d(TAG, "Full Doze mode? pm.isDeviceIdleMode()="+pm.isDeviceIdleMode()); 54 | if (pm.isDeviceIdleMode()) { 55 | return true; 56 | } 57 | } 58 | 59 | LogManager.d(TAG, "Doze mode? pm.isPowerSaveMode()="+pm.isPowerSaveMode()); 60 | return pm.isPowerSaveMode(); 61 | } 62 | else { 63 | LogManager.d(TAG, "We can't be in doze mode as we are pre-Android M"); 64 | } 65 | return false; 66 | } 67 | 68 | public void registerDozeCallbacks(Context context, BroadcastReceiver receiver) { 69 | IntentFilter filter = new IntentFilter(); 70 | filter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED); 71 | context.registerReceiver(receiver, filter); 72 | 73 | filter = new IntentFilter(); 74 | String action = getLightIdleModeChangeAction(); 75 | filter.addAction(action); 76 | context.registerReceiver(receiver, filter); 77 | } 78 | 79 | public String getLightIdleModeChangeAction() { 80 | String action = "android.os.action.LIGHT_DEVICE_IDLE_MODE_CHANGE"; 81 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 82 | try { 83 | Object reflectionAction =PowerManager.class.getField("ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED").get(null); 84 | if (reflectionAction != null && reflectionAction instanceof String) { 85 | action = (String) reflectionAction; 86 | } 87 | } catch (Exception e) { 88 | LogManager.d(TAG, "Cannot get LIGHT_DEVICE_IDLE_MODE_CHANGE action: " + e.toString(), e); 89 | } 90 | } 91 | return action; 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /lib/src/main/java/org/altbeacon/beacon/distance/DistanceConfigFetcher.java: -------------------------------------------------------------------------------- 1 | package org.altbeacon.beacon.distance; 2 | 3 | import org.altbeacon.beacon.logging.LogManager; 4 | 5 | import java.io.BufferedReader; 6 | import java.io.FileNotFoundException; 7 | import java.io.InputStreamReader; 8 | import java.net.HttpURLConnection; 9 | import java.net.URL; 10 | 11 | /** 12 | * Created by dyoung on 12/11/13. 13 | * 14 | * @exclude 15 | */ 16 | public class DistanceConfigFetcher { 17 | private static final String TAG = "DistanceConfigFetcher"; 18 | protected String mResponse; 19 | protected Exception mException; 20 | private int mResponseCode = -1; 21 | private String mUrlString; 22 | private String mUserAgentString; 23 | 24 | public DistanceConfigFetcher(String urlString, String userAgentString) { 25 | this.mUrlString = urlString; 26 | this.mUserAgentString = userAgentString; 27 | } 28 | 29 | 30 | public int getResponseCode() { 31 | return mResponseCode; 32 | } 33 | 34 | public String getResponseString() { 35 | return mResponse; 36 | } 37 | 38 | public Exception getException() { 39 | return mException; 40 | } 41 | 42 | public void request() { 43 | mResponse = null; 44 | String currentUrlString = mUrlString; 45 | int requestCount = 0; 46 | StringBuilder responseBuilder = new StringBuilder(); 47 | URL url; 48 | HttpURLConnection conn = null; 49 | do { 50 | if (requestCount != 0) { 51 | LogManager.d(TAG, "Following redirect from %s to %s", 52 | mUrlString, conn.getHeaderField("Location")); 53 | currentUrlString = conn.getHeaderField("Location"); 54 | } 55 | requestCount++; 56 | mResponseCode = -1; 57 | url = null; 58 | try { 59 | url = new URL(currentUrlString); 60 | } catch (Exception e) { 61 | LogManager.e(TAG, "Can't construct URL from: %s", mUrlString); 62 | mException = e; 63 | 64 | } 65 | if (url == null) { 66 | LogManager.d(TAG, "URL is null. Cannot make request"); 67 | } else { 68 | try { 69 | conn = (HttpURLConnection) url.openConnection(); 70 | conn.addRequestProperty("User-Agent", mUserAgentString); 71 | mResponseCode = conn.getResponseCode(); 72 | LogManager.d(TAG, "response code is %s", conn.getResponseCode()); 73 | } catch (SecurityException e1) { 74 | LogManager.w(e1, TAG, "Can't reach sever. Have you added android.permission.INTERNET to your manifest?"); 75 | mException = e1; 76 | } catch (FileNotFoundException e2) { 77 | LogManager.w(e2, TAG, "No data exists at \"+urlString"); 78 | mException = e2; 79 | } catch (java.io.IOException e3) { 80 | LogManager.w(e3, TAG, "Can't reach server"); 81 | mException = e3; 82 | } 83 | } 84 | } 85 | while (requestCount < 10 && 86 | (mResponseCode == HttpURLConnection.HTTP_MOVED_TEMP 87 | || mResponseCode == HttpURLConnection.HTTP_MOVED_PERM 88 | || mResponseCode == HttpURLConnection.HTTP_SEE_OTHER)); 89 | 90 | if (mException == null) { 91 | try { 92 | BufferedReader in = new BufferedReader( 93 | new InputStreamReader( 94 | conn.getInputStream()) 95 | ); 96 | String inputLine; 97 | while ((inputLine = in.readLine()) != null) 98 | responseBuilder.append(inputLine); 99 | in.close(); 100 | mResponse = responseBuilder.toString(); 101 | } catch (Exception e) { 102 | mException = e; 103 | LogManager.w(e, TAG, "error reading beacon data"); 104 | } 105 | } 106 | 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /lib/src/test/java/org/altbeacon/beacon/logging/LogManagerTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Radius Networks, Inc. 3 | * Copyright 2015 Andrew Reitz 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.altbeacon.beacon.logging; 18 | 19 | import org.junit.Before; 20 | import org.junit.Test; 21 | import org.mockito.Mock; 22 | import org.mockito.MockitoAnnotations; 23 | 24 | import static org.mockito.Mockito.verify; 25 | 26 | /** 27 | * Tests for LogManager, ensuring correct delegation and expectations are met. 28 | * 29 | * @author Andrew Reitz 30 | */ 31 | public class LogManagerTest { 32 | 33 | @Mock 34 | Logger logger; 35 | 36 | @Before 37 | public void setup() { 38 | MockitoAnnotations.initMocks(this); 39 | LogManager.setLogger(logger); 40 | } 41 | 42 | @Test(expected = NullPointerException.class) 43 | public void canNotSetNullLogger() { 44 | LogManager.setLogger(null); 45 | } 46 | 47 | @Test 48 | public void verbose() { 49 | String tag = "TestTag"; 50 | String message = "Test message"; 51 | 52 | LogManager.v(tag, message); 53 | 54 | verify(logger).v(tag, message); 55 | } 56 | 57 | @Test 58 | public void verboseWithThrowable() { 59 | String tag = "TestTag"; 60 | String message = "Test message"; 61 | Throwable t = new Throwable(); 62 | 63 | LogManager.v(t, tag, message); 64 | 65 | verify(logger).v(t, tag, message); 66 | } 67 | 68 | @Test 69 | public void debug() { 70 | String tag = "TestTag"; 71 | String message = "Test message"; 72 | 73 | LogManager.d(tag, message); 74 | 75 | verify(logger).d(tag, message); 76 | } 77 | 78 | @Test 79 | public void debugWithThrowable() { 80 | String tag = "TestTag"; 81 | String message = "Test message"; 82 | Throwable t = new Throwable(); 83 | 84 | LogManager.d(t, tag, message); 85 | 86 | verify(logger).d(t, tag, message); 87 | } 88 | 89 | @Test 90 | public void info() { 91 | String tag = "TestTag"; 92 | String message = "Test message"; 93 | 94 | LogManager.i(tag, message); 95 | 96 | verify(logger).i(tag, message); 97 | } 98 | 99 | @Test 100 | public void infoWithThrowable() { 101 | String tag = "TestTag"; 102 | String message = "Test message"; 103 | Throwable t = new Throwable(); 104 | 105 | LogManager.i(t, tag, message); 106 | 107 | verify(logger).i(t, tag, message); 108 | } 109 | 110 | @Test 111 | public void warning() { 112 | String tag = "TestTag"; 113 | String message = "Test message"; 114 | 115 | LogManager.w(tag, message); 116 | 117 | verify(logger).w(tag, message); 118 | } 119 | 120 | @Test 121 | public void warningWithThrowable() { 122 | String tag = "TestTag"; 123 | String message = "Test message"; 124 | Throwable t = new Throwable(); 125 | 126 | LogManager.w(t, tag, message); 127 | 128 | verify(logger).w(t, tag, message); 129 | } 130 | 131 | @Test 132 | public void error() { 133 | String tag = "TestTag"; 134 | String message = "Test message"; 135 | 136 | LogManager.e(tag, message); 137 | 138 | verify(logger).e(tag, message); 139 | } 140 | 141 | @Test 142 | public void errorWithThrowable() { 143 | String tag = "TestTag"; 144 | String message = "Test message"; 145 | Throwable t = new Throwable(); 146 | 147 | LogManager.e(t, tag, message); 148 | 149 | verify(logger).e(t, tag, message); 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /gradle/oldpackage.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'idea' 2 | 3 | idea { 4 | module { 5 | testOutputDir = file('build/test-classes/debug') 6 | } 7 | } 8 | 9 | task renameAarForRelease(type: Copy, dependsOn: build) { 10 | description = "Rename the aar for easy release publishing" 11 | 12 | from "$buildDir/outputs/aar/" //${project.name}-release.aar 13 | into "$buildDir/outputs/aar/" //${project.name}-${project.version}.aar" 14 | include "${project.name}-release.aar" 15 | rename { String fileName -> 16 | fileName = "${project.name}-${project.version}.aar" 17 | } 18 | } 19 | 20 | task distribution(dependsOn: [build, clean, renameAarForRelease]) { 21 | doLast { 22 | println "Building with version=$version" 23 | } 24 | } 25 | 26 | task release(dependsOn: 'distribution') { 27 | doLast { 28 | println('Doing release build') 29 | } 30 | } 31 | 32 | android.libraryVariants.all { variant -> 33 | 34 | task("generate${variant.name}Javadoc", type: Javadoc) { 35 | title = "Android Beacon Library $version API" 36 | description "Generates Javadoc for $variant.name." 37 | source = variant.javaCompile.source 38 | ext.androidJar = 39 | "${android.sdkDirectory}/platforms/${android.compileSdkVersion}/android.jar" 40 | //Refer to https://stackoverflow.com/a/50833438/4068957 41 | doFirst { classpath = files(variant.javaCompile.classpath.files, ext.androidJar)} 42 | options.linksOffline "http://d.android.com/reference/", "${android.sdkDirectory}/docs/reference" 43 | exclude '**/BuildConfig.java' 44 | exclude '**/R.java' 45 | } 46 | } 47 | 48 | build.mustRunAfter clean 49 | 50 | apply from: '../gradle/credentials.gradle' 51 | // custom tasks for creating source/javadoc jars 52 | task androidJavadocsJar(type: Jar) { 53 | from "$buildDir/libs/$project.name-$project.version-javadoc.jar" 54 | } 55 | 56 | task androidSourcesJar(type: Jar) { 57 | from android.sourceSets.main.java.srcDirs 58 | } 59 | // configuration of the Maven artifacts 60 | apply plugin: 'maven-publish' 61 | 62 | // add javadoc/source jar tasks as artifacts 63 | artifacts { 64 | archives androidSourcesJar, androidJavadocsJar 65 | } 66 | 67 | publishing { 68 | publications { 69 | dist(MavenPublication) { 70 | groupId project.group 71 | artifactId project.name 72 | version project.version 73 | artifact "${project.buildDir}/outputs/aar/${project.name}-release.aar" 74 | artifact androidJavadocsJar { 75 | classifier 'javadoc' 76 | } 77 | artifact androidSourcesJar { 78 | classifier 'source' 79 | } 80 | 81 | pom.withXml { 82 | def Node root = asNode() 83 | root.appendNode('name', project.name) 84 | root.appendNode('description', project.project_description) 85 | root.appendNode('url', project.project_url) 86 | 87 | def issues = root.appendNode('issueManagement') 88 | issues.appendNode('system', 'github') 89 | issues.appendNode('url', project.project_issues_url) 90 | 91 | def scm = root.appendNode('scm') 92 | scm.appendNode('url', project.project_scm) 93 | scm.appendNode('connection', project.project_connection) 94 | scm.appendNode('developerConnection', project.project_dev_connection) 95 | 96 | def license = root.appendNode('licenses').appendNode('license') 97 | license.appendNode('name', 'The Apache Software License, Version 2.0') 98 | license.appendNode('url', 'http://www.apache.org/licenses/LICENSE-2.0.txt') 99 | license.appendNode('distribution', 'repo') 100 | 101 | def dev = root.appendNode('developers').appendNode('developer') 102 | dev.appendNode('id', project.project_vendor) 103 | dev.appendNode('name', project.project_vendor_name) 104 | dev.appendNode('organization', 'AltBeacon') 105 | dev.appendNode('organizationUrl', 'altbeacon.org') 106 | } 107 | } 108 | } 109 | } 110 | 111 | 112 | 113 | 114 | -------------------------------------------------------------------------------- /lib/src/main/java/org/altbeacon/beacon/AltBeaconParser.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Radius Networks, Inc. 3 | * http://www.radiusnetworks.com 4 | * 5 | * @author David G. Young 6 | * 7 | * Licensed to the Apache Software Foundation (ASF) under one 8 | * or more contributor license agreements. See the NOTICE file 9 | * distributed with this work for additional information 10 | * regarding copyright ownership. The ASF licenses this file 11 | * to you under the Apache License, Version 2.0 (the 12 | * "License"); you may not use this file except in compliance 13 | * with the License. You may obtain a copy of the License at 14 | * 15 | * http://www.apache.org/licenses/LICENSE-2.0 16 | * 17 | * Unless required by applicable law or agreed to in writing, 18 | * software distributed under the License is distributed on an 19 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 20 | * KIND, either express or implied. See the License for the 21 | * specific language governing permissions and limitations 22 | * under the License. 23 | */ 24 | package org.altbeacon.beacon; 25 | 26 | import android.bluetooth.BluetoothDevice; 27 | import android.util.Log; 28 | 29 | /** 30 | * A specific beacon parser designed to parse only AltBeacons from raw BLE packets detected by 31 | * Android. By default, this is the only BeaconParser that is used by the library. 32 | * Additional BeaconParser instances can get created and registered with the library. 33 | * {@link BeaconParser See BeaconParser for more information.} 34 | */ 35 | public class AltBeaconParser extends BeaconParser { 36 | public static final String TAG = "AltBeaconParser"; 37 | 38 | /** 39 | * Constructs an AltBeacon Parser and sets its layout 40 | */ 41 | public AltBeaconParser() { 42 | super(); 43 | // Radius networks and other manufacturers seen in AltBeacons 44 | // Note: Other manufacturer codes that have been seen in the wild with AltBeacons are: 45 | // 0x004c, 0x00e0 46 | // We are not adding these here because there is no indication they are widely used 47 | // for production purposes. We need to keep the hardware assist list short in order to 48 | // save slots. If you are a manufacturer of AltBeacons and want you company code added to 49 | // this list, please open an issue on the Github project for this library. If a beacon 50 | // manufacturer code not in this list is used for AltBeacons, phones using Andoroid 5.x+ 51 | // detection APIs will not be able to detect the beacon in the background. 52 | mHardwareAssistManufacturers = new int[]{0x0118}; 53 | this.setBeaconLayout(BeaconParser.ALTBEACON_LAYOUT); 54 | this.mIdentifier = "altbeacon"; 55 | } 56 | /** 57 | * Construct an AltBeacon from a Bluetooth LE packet collected by Android's Bluetooth APIs, 58 | * including the raw Bluetooth device info 59 | * 60 | * timestampMs excluded for backward compatibility with older api consumers. 61 | * 62 | * @Deprecated New implementations should not use this method. It is not intended as a public 63 | * API and is subject to change or removal in the future. 64 | * 65 | * @param scanData The actual packet bytes 66 | * @param rssi The measured signal strength of the packet 67 | * @param device The Bluetooth device that was detected 68 | * @return An instance of a Beacon 69 | */ 70 | @Deprecated 71 | @Override 72 | public Beacon fromScanData(byte[] scanData, int rssi, BluetoothDevice device) { 73 | return fromScanData(scanData, rssi, device, System.currentTimeMillis(), new AltBeacon()); 74 | } 75 | 76 | /** 77 | * Construct an AltBeacon from a Bluetooth LE packet collected by Android's Bluetooth APIs, 78 | * including the raw Bluetooth device info 79 | * 80 | * @param scanData The actual packet bytes 81 | * @param rssi The measured signal strength of the packet 82 | * @param device The Bluetooth device that was detected 83 | * @param timestampMs The timestamp in milliseconds of the scan execution 84 | * @return An instance of a Beacon 85 | */ 86 | @Override 87 | public Beacon fromScanData(byte[] scanData, int rssi, BluetoothDevice device, long timestampMs) { 88 | return fromScanData(scanData, rssi, device, timestampMs, new AltBeacon()); 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /lib/src/test/java/org/altbeacon/beacon/service/MonitoringStatusTest.java: -------------------------------------------------------------------------------- 1 | package org.altbeacon.beacon.service; 2 | 3 | import android.content.Context; 4 | 5 | import org.altbeacon.beacon.BeaconManager; 6 | import org.altbeacon.beacon.Region; 7 | import org.altbeacon.beacon.logging.LogManager; 8 | import org.altbeacon.beacon.logging.Loggers; 9 | import org.junit.Before; 10 | import org.junit.Test; 11 | import org.junit.runner.RunWith; 12 | import org.robolectric.RobolectricTestRunner; 13 | import org.robolectric.RuntimeEnvironment; 14 | import org.robolectric.annotation.Config; 15 | 16 | import java.util.Collection; 17 | 18 | import static org.junit.Assert.assertEquals; 19 | 20 | /** 21 | * Created by dyoung on 7/1/16. 22 | */ 23 | @RunWith(RobolectricTestRunner.class) 24 | @Config(sdk = 28) 25 | public class MonitoringStatusTest { 26 | private static final String TAG = MonitoringStatusTest.class.getSimpleName(); 27 | @Before 28 | public void before() { 29 | org.robolectric.shadows.ShadowLog.stream = System.err; 30 | LogManager.setLogger(Loggers.verboseLogger()); 31 | LogManager.setVerboseLoggingEnabled(true); 32 | BeaconManager.setManifestCheckingDisabled(true); 33 | Context context = RuntimeEnvironment.application; 34 | new MonitoringStatus(context).clear(); 35 | } 36 | 37 | @Test 38 | public void savesStatusOfUpTo50RegionsTest() throws Exception { 39 | Context context = RuntimeEnvironment.application; 40 | MonitoringStatus monitoringStatus = new MonitoringStatus(context); 41 | for (int i = 0; i < 50; i++) { 42 | Region region = new Region(""+i, null, null, null); 43 | monitoringStatus.addRegion(region, null); 44 | } 45 | monitoringStatus.saveMonitoringStatusIfOn(); 46 | MonitoringStatus monitoringStatus2 = new MonitoringStatus(context); 47 | assertEquals("restored regions should be same number as saved", 50, monitoringStatus2.regions().size()); 48 | } 49 | 50 | @Test 51 | public void clearsStatusOfOver50RegionsTest() throws Exception { 52 | Context context = RuntimeEnvironment.application; 53 | MonitoringStatus monitoringStatus = new MonitoringStatus(context); 54 | for (int i = 0; i < 51; i++) { 55 | Region region = new Region(""+i, null, null, null); 56 | monitoringStatus.addRegion(region, null); 57 | } 58 | monitoringStatus.saveMonitoringStatusIfOn(); 59 | MonitoringStatus monitoringStatus2 = new MonitoringStatus(context); 60 | assertEquals("restored regions should be none", 0, monitoringStatus2.regions().size()); 61 | } 62 | 63 | @Test 64 | public void refusesToRestoreRegionsIfTooMuchTimeHasPassedSinceSavingTest() throws Exception { 65 | Context context = RuntimeEnvironment.application; 66 | MonitoringStatus monitoringStatus = new MonitoringStatus(context); 67 | for (int i = 0; i < 50; i++) { 68 | Region region = new Region(""+i, null, null, null); 69 | monitoringStatus.addRegion(region, null); 70 | } 71 | monitoringStatus.saveMonitoringStatusIfOn(); 72 | // Set update time to one hour ago 73 | monitoringStatus.updateMonitoringStatusTime(System.currentTimeMillis() - 1000*3600l); 74 | MonitoringStatus monitoringStatus2 = new MonitoringStatus(context); 75 | assertEquals("restored regions should be none", 0, monitoringStatus2.regions().size()); 76 | } 77 | 78 | @Test 79 | public void allowsAccessToRegionsAfterRestore() throws Exception { 80 | Context context = RuntimeEnvironment.application; 81 | BeaconManager beaconManager = BeaconManager.getInstanceForApplication(context); 82 | MonitoringStatus monitoringStatus = MonitoringStatus.getInstanceForApplication(context); 83 | for (int i = 0; i < 50; i++) { 84 | Region region = new Region(""+i, null, null, null); 85 | monitoringStatus.addRegion(region, null); 86 | } 87 | monitoringStatus.saveMonitoringStatusIfOn(); 88 | monitoringStatus.restoreMonitoringStatus(); 89 | Collection restoredRegions = monitoringStatus.regions(); 90 | assertEquals("tracked regions should be restored", 50, restoredRegions.size()); 91 | Collection regions = beaconManager.getMonitoredRegions(); 92 | assertEquals("beaconManager should not return regions it did not register", 0, regions.size()); 93 | } 94 | 95 | 96 | } 97 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Android Beacon Library 2 | ======================= 3 | 4 | [![Build Status](https://circleci.com/gh/AltBeacon/android-beacon-library.png?circle-token=4e11fb0dccaa8b98bc67fdbe38b179e4a7d07c27)](https://circleci.com/gh/AltBeacon/android-beacon-library) 5 | 6 | An Android library providing APIs to interact with beacons. Please visit the 7 | [project website](http://altbeacon.github.io/android-beacon-library/) for how to use this library. 8 | 9 | **IMPORTANT: By default, this library will only detect beacons meeting the AltBeacon specification.** 10 | 11 | If you want this library to work with proprietary or custom beacons, see the [BeaconParser](http://altbeacon.github.io/android-beacon-library/javadoc/org/altbeacon/beacon/BeaconParser.html) class. 12 | 13 | ## What does this library do? 14 | 15 | It allows Android devices to use beacons much like iOS devices do. An app can request to get notifications when one 16 | or more beacons appear or disappear. An app can also request to get a ranging update from one or more beacons 17 | at a frequency of approximately 1Hz. 18 | 19 | ## Documentation 20 | 21 | The [project website](http://altbeacon.github.io/android-beacon-library/) has [full documentation](http://altbeacon.github.io/android-beacon-library/documentation.html) 22 | 23 | ## Downloads 24 | 25 | ### Binary 26 | 27 | You may [download binary releases here.](http://altbeacon.github.io/android-beacon-library/download.html) 28 | 29 | ### Maven 30 | 31 | Add Maven Central to your build file's list of repositories. 32 | 33 | ```groovy 34 | repositories { 35 | mavenCentral() 36 | } 37 | ``` 38 | 39 | to use the Maven Central Repository 40 | 41 | ```groovy 42 | dependencies { 43 | ... 44 | implementation 'org.altbeacon:android-beacon-library:${altbeacon.version}' 45 | ... 46 | } 47 | ``` 48 | 49 | replacing `${altbeacon.version}` with the version you wish to use. 50 | 51 | ## How to build this Library 52 | 53 | This project uses an AndroidStudio/gradle build system and is known working with Android Studio Flamingo | 2022.2.1 Patch 1 and Gradle 8.0 54 | 55 | Key Gradle build targets: 56 | 57 | ./gradlew test # run unit tests. To see results: `open lib/build/reports/tests/testDebugUnitTest/index.html` 58 | ./gradlew build # development build 59 | ./gradlew release # release build 60 | 61 | ## License 62 | 63 | Licensed under the Apache License, Version 2.0 (the "License"); 64 | you may not use this file except in compliance with the License. 65 | You may obtain a copy of the License at 66 | 67 | http://www.apache.org/licenses/LICENSE-2.0 68 | 69 | Unless required by applicable law or agreed to in writing, software 70 | distributed under the License is distributed on an "AS IS" BASIS, 71 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 72 | See the License for the specific language governing permissions and 73 | limitations under the License. 74 | 75 | This software is available under the Apache License 2.0, allowing you to use the library in your applications. 76 | 77 | If you want to help with the open source project, contact david@radiusnetworks.com 78 | 79 | ## Publishing to Maven 80 | 81 | The following instructions are for project administrators. 82 | 83 | 1. Prerequisites: https://getstream.io/blog/publishing-libraries-to-mavencentral-2021/ 84 | 85 | 2. Configure your ~/.gradle/gradle.properties with: 86 | 87 | signing.keyId= 88 | signing.password= 89 | signing.secretKeyRingFile= 90 | signing.password= 91 | ossrhUsername= 92 | ossrhPassword= 93 | 94 | 3. Run the build and upload 95 | 96 | git tag 97 | git push --tags 98 | ./gradlew release 99 | ./gradlew mavenPublish # Wait 10 mins before using the next command 100 | ./gradlew closeAndReleaseRepository 101 | 102 | 4. Keep checking for a half hour or so at https://repo1.maven.org/maven2/org/altbeacon/android-beacon-library/ to see that the new release shows up. 103 | 104 | Note: you must have Java 17 to build the projecdt. If that is not the version on the path, and you have Android Studio installed, you may be able to add it to the path with: 105 | 106 | `export PATH=/Applications/Android\ Studio.app/Contents/jbr/Contents/Home/bin:$PATH` 107 | -------------------------------------------------------------------------------- /lib/src/main/java/org/altbeacon/beacon/startup/StartupBroadcastReceiver.java: -------------------------------------------------------------------------------- 1 | package org.altbeacon.beacon.startup; 2 | 3 | import android.bluetooth.le.BluetoothLeScanner; 4 | import android.bluetooth.le.ScanCallback; 5 | import android.bluetooth.le.ScanResult; 6 | import android.bluetooth.le.ScanSettings; 7 | import android.content.BroadcastReceiver; 8 | import android.content.Context; 9 | import android.content.Intent; 10 | import android.os.Build; 11 | import android.os.Bundle; 12 | import android.os.Parcel; 13 | import android.os.Parcelable; 14 | 15 | import org.altbeacon.beacon.logging.LogManager; 16 | import org.altbeacon.beacon.BeaconManager; 17 | import org.altbeacon.beacon.service.ScanJob; 18 | import org.altbeacon.beacon.service.ScanJobScheduler; 19 | 20 | import java.util.ArrayList; 21 | 22 | public class StartupBroadcastReceiver extends BroadcastReceiver 23 | { 24 | private static final String TAG = "StartupBroadcastReceiver"; 25 | 26 | @Override 27 | public void onReceive(Context context, Intent intent) { 28 | LogManager.d(TAG, "onReceive called in startup broadcast receiver"); 29 | if (Build.VERSION.SDK_INT < 18) { 30 | LogManager.w(TAG, "Not starting up beacon service because we do not have API version 18 (Android 4.3). We have: %s", Build.VERSION.SDK_INT); 31 | return; 32 | } 33 | if (intent != null && intent.getAction() != null && intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) { 34 | LogManager.i(TAG, "Android Beacon Library restarted via ACTION_BOOT_COMPLETED"); 35 | BeaconManager beaconManager = BeaconManager.getInstanceForApplication(context.getApplicationContext()); 36 | // As of Android 14, the OS fails BOOT foreground service start when initiated from 37 | // Application.onCreate. It is only allowed after the onReceive is called for the 38 | // BOOT_COMPLETED event, which happens immediately after Application.onCreate This 39 | // retry will fix the failure that happened just milliseconds earlier. 40 | if (beaconManager.foregroundServiceStartFailed()) { 41 | LogManager.i(TAG, "Foreground service startup failure detected. We will retry starting now that we have received a BOOT_COMPLETED action."); 42 | beaconManager.retryForegroundServiceScanning(); 43 | } 44 | } 45 | 46 | BeaconManager beaconManager = BeaconManager.getInstanceForApplication(context.getApplicationContext()); 47 | if (beaconManager.isAnyConsumerBound() || beaconManager.getScheduledScanJobsEnabled() || beaconManager.getIntentScanStrategyCoordinator() != null) { 48 | int bleCallbackType = intent.getIntExtra(BluetoothLeScanner.EXTRA_CALLBACK_TYPE, -1); // e.g. ScanSettings.CALLBACK_TYPE_FIRST_MATCH 49 | if (bleCallbackType != -1) { 50 | LogManager.d(TAG, "Passive background scan callback type: "+bleCallbackType); 51 | LogManager.d(TAG, "got Android background scan via intent"); 52 | int errorCode = intent.getIntExtra(BluetoothLeScanner.EXTRA_ERROR_CODE, -1); // e.g. ScanCallback.SCAN_FAILED_INTERNAL_ERROR 53 | if (errorCode != -1) { 54 | LogManager.w(TAG, "Passive background scan failed. Code; "+errorCode); 55 | } 56 | ArrayList scanResults = intent.getParcelableArrayListExtra(BluetoothLeScanner.EXTRA_LIST_SCAN_RESULT); 57 | 58 | if (beaconManager.getIntentScanStrategyCoordinator() != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 59 | beaconManager.getIntentScanStrategyCoordinator().processScanResults(scanResults); 60 | } 61 | else if (beaconManager.getScheduledScanJobsEnabled() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 62 | ScanJobScheduler.getInstance().scheduleAfterBackgroundWakeup(context, scanResults); 63 | } 64 | } 65 | else if (intent.getBooleanExtra("wakeup", false)) { 66 | LogManager.d(TAG, "got wake up intent"); 67 | } 68 | else { 69 | LogManager.d(TAG, "Already started. Ignoring intent: %s of type: %s", intent, 70 | intent.getStringExtra("wakeup")); 71 | } 72 | } 73 | else { 74 | LogManager.d(TAG, "No consumers are bound. Ignoring broadcast receiver."); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /lib/src/test/java/org/altbeacon/beacon/distance/ModelSpecificDistanceCalculatorTest.java: -------------------------------------------------------------------------------- 1 | package org.altbeacon.beacon.distance; 2 | 3 | import android.content.Context; 4 | 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | import org.robolectric.RobolectricTestRunner; 8 | import org.robolectric.annotation.Config; 9 | import org.robolectric.RuntimeEnvironment; 10 | 11 | import static org.junit.Assert.assertEquals; 12 | 13 | 14 | @Config(sdk = 28) 15 | @RunWith(RobolectricTestRunner.class) 16 | /* 17 | HOW TO SEE DEBUG LINES FROM YOUR UNIT TESTS: 18 | 19 | 1. set a line like this at the start of your test: 20 | org.robolectric.shadows.ShadowLog.stream = System.err; 21 | 2. run the tests from the command line 22 | 3. Look at the test report file in your web browser, e.g. 23 | file:///Users/dyoung/workspace/AndroidProximityLibrary/build/reports/tests/index.html 24 | 4. Expand the System.err section 25 | /** 26 | * Created by dyoung on 8/28/14. 27 | */ 28 | public class ModelSpecificDistanceCalculatorTest { 29 | @Test 30 | public void testCalculatesDistance() { 31 | org.robolectric.shadows.ShadowLog.stream = System.err; 32 | 33 | ModelSpecificDistanceCalculator distanceCalculator = new ModelSpecificDistanceCalculator(null, null); 34 | Double distance = distanceCalculator.calculateDistance(-59, -59); 35 | assertEquals("Distance should be 1.0 for same power and rssi", 1.0, distance, 0.1); 36 | } 37 | 38 | @Test 39 | public void testSelectsDefaultModel() { 40 | org.robolectric.shadows.ShadowLog.stream = System.err; 41 | 42 | ModelSpecificDistanceCalculator distanceCalculator = new ModelSpecificDistanceCalculator(null, null); 43 | assertEquals("Default model should be Nexus 5", "Nexus 5", distanceCalculator.getModel().getModel()); 44 | } 45 | 46 | @Test 47 | public void testSelectsNexus4OnExactMatch() { 48 | org.robolectric.shadows.ShadowLog.stream = System.err; 49 | AndroidModel model = new AndroidModel("4.4.2", "KOT49H","Nexus 4","LGE"); 50 | 51 | ModelSpecificDistanceCalculator distanceCalculator = new ModelSpecificDistanceCalculator(null, null, model); 52 | assertEquals("should be Nexus 4", "Nexus 4", distanceCalculator.getModel().getModel()); 53 | } 54 | 55 | @Test 56 | public void testCalculatesDistanceForMotoXPro() { 57 | final Context applicationContext = RuntimeEnvironment.application; 58 | org.robolectric.shadows.ShadowLog.stream = System.err; 59 | 60 | final AndroidModel model = new AndroidModel("5.0.2", "LXG22.67-7.1", "Moto X Pro", "XT1115"); 61 | ModelSpecificDistanceCalculator distanceCalculator = new ModelSpecificDistanceCalculator(applicationContext, null, model); 62 | assertEquals("should be Moto X Pro", "Moto X Pro", distanceCalculator.getModel().getModel()); 63 | Double distance = distanceCalculator.calculateDistance(-49, -58); 64 | assertEquals("Distance should be as predicted by coefficients at 3 meters", 2.661125466, distance, 0.1); 65 | } 66 | 67 | @Test 68 | public void testConcurrentModificationException() { 69 | org.robolectric.shadows.ShadowLog.stream = System.err; 70 | 71 | final Context applicationContext = RuntimeEnvironment.application; 72 | 73 | final AndroidModel model = new AndroidModel("4.4.2", "KOT49H", "Nexus 4", "LGE"); 74 | final String modelMapJson = 75 | "{\"models\":[ \"coefficient1\": 0.89976,\"coefficient2\": 7.7095,\"coefficient3\": 0.111," + 76 | "\"version\":\"4.4.2\",\"build_number\":\"KOT49H\",\"model\":\"Nexus 4\"," + 77 | "\"manufacturer\":\"LGE\"},{\"coefficient1\": 0.42093,\"coefficient2\": 6.9476," + 78 | "\"coefficient3\": 0.54992,\"version\":\"4.4.2\",\"build_number\":\"LPV79\"," + 79 | "\"model\":\"Nexus 5\",\"manufacturer\":\"LGE\",\"default\": true}]}"; 80 | final ModelSpecificDistanceCalculator distanceCalculator = 81 | new ModelSpecificDistanceCalculator(applicationContext, null, model); 82 | 83 | Runnable runnable2 = new Runnable() { 84 | @Override 85 | public void run() { 86 | try { 87 | while (true) { 88 | distanceCalculator.buildModelMapWithLock(modelMapJson); 89 | } 90 | } catch (Exception ex) { 91 | ex.printStackTrace(); 92 | } 93 | } 94 | }; 95 | 96 | Thread thread2 = new Thread(runnable2); 97 | thread2.start(); 98 | 99 | int i = 0; 100 | while (++i < 1000 && thread2.getState() != Thread.State.TERMINATED) { 101 | distanceCalculator.findCalculatorForModelWithLock(model); 102 | } 103 | 104 | thread2.interrupt(); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /lib/src/test/java/org/altbeacon/beacon/service/RangingDataTest.java: -------------------------------------------------------------------------------- 1 | package org.altbeacon.beacon.service; 2 | 3 | import android.content.Context; 4 | 5 | import org.altbeacon.beacon.BeaconManager; 6 | import org.altbeacon.beacon.Identifier; 7 | import org.altbeacon.beacon.Beacon; 8 | import org.altbeacon.beacon.Region; 9 | import org.altbeacon.beacon.logging.LogManager; 10 | import org.altbeacon.beacon.logging.Loggers; 11 | import org.junit.Before; 12 | import org.junit.Test; 13 | import org.junit.runner.RunWith; 14 | import org.robolectric.RobolectricTestRunner; 15 | import org.robolectric.annotation.Config; 16 | import org.robolectric.RuntimeEnvironment; 17 | 18 | import java.util.ArrayList; 19 | 20 | import android.os.Bundle; 21 | 22 | import static org.junit.Assert.assertEquals; 23 | 24 | @RunWith(RobolectricTestRunner.class) 25 | @Config(sdk = 28) 26 | public class RangingDataTest { 27 | @Before 28 | public void before() { 29 | org.robolectric.shadows.ShadowLog.stream = System.err; 30 | LogManager.setLogger(Loggers.verboseLogger()); 31 | LogManager.setVerboseLoggingEnabled(true); 32 | BeaconManager.setsManifestCheckingDisabled(true); 33 | } 34 | 35 | @Test 36 | public void testSerialization() throws Exception { 37 | Context context = RuntimeEnvironment.application; 38 | ArrayList identifiers = new ArrayList(); 39 | identifiers.add(Identifier.parse("2f234454-cf6d-4a0f-adf2-f4911ba9ffa6")); 40 | identifiers.add(Identifier.parse("1")); 41 | identifiers.add(Identifier.parse("2")); 42 | Region region = new Region("testRegion", identifiers); 43 | ArrayList beacons = new ArrayList(); 44 | Beacon beacon = new Beacon.Builder().setIdentifiers(identifiers) 45 | .setRssi(-1) 46 | .setRunningAverageRssi(-2) 47 | .setTxPower(-50) 48 | .setBluetoothAddress("01:02:03:04:05:06") 49 | .build(); 50 | beacon.setRssiMeasurementCount(1); 51 | beacon.setPacketCount(2); 52 | for (int i=0; i < 10; i++) { 53 | beacons.add(beacon); 54 | } 55 | RangingData data = new RangingData(beacons, region); 56 | Bundle bundle = data.toBundle(); 57 | RangingData data2 = RangingData.fromBundle(bundle); 58 | assertEquals("beacon count shouild be restored", 10, data2.getBeacons().size()); 59 | assertEquals("region identifier 1 shouild be restored", "2f234454-cf6d-4a0f-adf2-f4911ba9ffa6", data2.getRegion().getId1().toString()); 60 | Beacon restoredBeacon = data2.getBeacons().iterator().next(); 61 | assertEquals("beacon identifier 1 shouild be restored", "2f234454-cf6d-4a0f-adf2-f4911ba9ffa6", restoredBeacon.getId1().toString()); 62 | assertEquals("RSSI is restored", -1, restoredBeacon.getRssi()); 63 | assertEquals("Average RSSI is restored", -2.0, restoredBeacon.getRunningAverageRssi(), 0.0); 64 | assertEquals("TXPower is restored", -50, restoredBeacon.getTxPower()); 65 | assertEquals("Measurement count is restored", 1, restoredBeacon.getMeasurementCount()); 66 | assertEquals("Packet count is restored", 2, restoredBeacon.getPacketCount()); 67 | } 68 | 69 | @Test 70 | // On MacBookPro 2.5 GHz Core I7, 10000 serialization/deserialiation cycles of RangingData took 22ms 71 | public void testSerializationBenchmark() throws Exception { 72 | Context context = RuntimeEnvironment.application; 73 | ArrayList identifiers = new ArrayList(); 74 | identifiers.add(Identifier.parse("2f234454-cf6d-4a0f-adf2-f4911ba9ffa6")); 75 | identifiers.add(Identifier.parse("1")); 76 | identifiers.add(Identifier.parse("2")); 77 | Region region = new Region("testRegion", identifiers); 78 | ArrayList beacons = new ArrayList(); 79 | Beacon beacon = new Beacon.Builder().setIdentifiers(identifiers).setRssi(-1).setRunningAverageRssi(-2).setTxPower(-50).setBluetoothAddress("01:02:03:04:05:06").build(); 80 | for (int i=0; i < 10; i++) { 81 | beacons.add(beacon); 82 | } 83 | RangingData data = new RangingData(beacons, region); 84 | long time1 = System.currentTimeMillis(); 85 | for (int i=0; i< 10000; i++) { 86 | Bundle bundle = data.toBundle(); 87 | RangingData data2 = RangingData.fromBundle(bundle); 88 | } 89 | long time2 = System.currentTimeMillis(); 90 | System.out.println("*** Ranging Data Serialization benchmark: "+(time2-time1)); 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /lib/src/test/java/org/altbeacon/beacon/AltBeaconParserTest.java: -------------------------------------------------------------------------------- 1 | package org.altbeacon.beacon; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.assertNotNull; 5 | import static org.junit.Assert.assertNull; 6 | 7 | import org.altbeacon.beacon.logging.LogManager; 8 | import org.altbeacon.beacon.logging.Loggers; 9 | import org.robolectric.RobolectricTestRunner; 10 | 11 | import org.junit.runner.RunWith; 12 | import org.junit.Test; 13 | import org.robolectric.annotation.Config; 14 | 15 | @Config(sdk = 28) 16 | @RunWith(RobolectricTestRunner.class) 17 | /* 18 | HOW TO SEE DEBUG LINES FROM YOUR UNIT TESTS: 19 | 1. set a line like this at the start of your test: 20 | org.robolectric.shadows.ShadowLog.stream = System.err; 21 | 2. run the tests from the command line 22 | 3. Look at the test report file in your web browser, e.g. 23 | file:///Users/dyoung/workspace/AndroidProximityLibrary/build/reports/tests/index.html 24 | 4. Expand the System.err section 25 | */ 26 | public class AltBeaconParserTest { 27 | 28 | public static byte[] hexStringToByteArray(String s) { 29 | int len = s.length(); 30 | byte[] data = new byte[len / 2]; 31 | for (int i = 0; i < len; i += 2) { 32 | data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) 33 | + Character.digit(s.charAt(i+1), 16)); 34 | } 35 | return data; 36 | } 37 | @Test 38 | public void testRecognizeBeacon() { 39 | BeaconManager.setDebug(true); 40 | byte[] bytes = hexStringToByteArray("02011a1bff1801beac2f234454cf6d4a0fadf2f4911ba9ffa600010002c50900"); 41 | AltBeaconParser parser = new AltBeaconParser(); 42 | Beacon beacon = parser.fromScanData(bytes, -55, null, 123456L); 43 | assertEquals ("Beacon should have one data field", 1, beacon.getDataFields().size()); 44 | assertEquals("manData should be parsed", 9, ((AltBeacon) beacon).getMfgReserved()); 45 | } 46 | 47 | @Test 48 | public void testDetectsDaveMHardwareBeacon() { 49 | org.robolectric.shadows.ShadowLog.stream = System.err; 50 | byte[] bytes = hexStringToByteArray("02011a1bff1801beac2f234454cf6d4a0fadf2f4911ba9ffa600050003be020e09526164426561636f6e20555342020a0300000000000000000000000000"); 51 | AltBeaconParser parser = new AltBeaconParser(); 52 | Beacon beacon = parser.fromScanData(bytes, -55, null, 123456L); 53 | assertNotNull("Beacon should be not null if parsed successfully", beacon); 54 | } 55 | @Test 56 | public void testDetectsAlternateBeconType() { 57 | org.robolectric.shadows.ShadowLog.stream = System.err; 58 | byte[] bytes = hexStringToByteArray("02011a1bff1801aabb2f234454cf6d4a0fadf2f4911ba9ffa600010002c50900"); 59 | AltBeaconParser parser = new AltBeaconParser(); 60 | parser.setMatchingBeaconTypeCode(0xaabbl); 61 | Beacon beacon = parser.fromScanData(bytes, -55, null, 123456L); 62 | assertNotNull("Beacon should be not null if parsed successfully", beacon); 63 | } 64 | @Test 65 | public void testParseWrongFormatReturnsNothing() { 66 | BeaconManager.setDebug(true); 67 | org.robolectric.shadows.ShadowLog.stream = System.err; 68 | LogManager.d("XXX", "testParseWrongFormatReturnsNothing start"); 69 | byte[] bytes = hexStringToByteArray("02011a1aff1801ffff2f234454cf6d4a0fadf2f4911ba9ffa600010002c509"); 70 | AltBeaconParser parser = new AltBeaconParser(); 71 | Beacon beacon = parser.fromScanData(bytes, -55, null, 123456L); 72 | LogManager.d("XXX", "testParseWrongFormatReturnsNothing end"); 73 | assertNull("Beacon should be null if not parsed successfully", beacon); 74 | } 75 | 76 | @Test 77 | public void testParsesBeaconMissingDataField() { 78 | BeaconManager.setDebug(true); 79 | org.robolectric.shadows.ShadowLog.stream = System.err; 80 | byte[] bytes = hexStringToByteArray("02011a1aff1801beac2f234454cf6d4a0fadf2f4911ba9ffa600010002c5000000"); 81 | AltBeaconParser parser = new AltBeaconParser(); 82 | Beacon beacon = parser.fromScanData(bytes, -55, null, 123456L); 83 | assertEquals("mRssi should be as passed in", -55, beacon.getRssi()); 84 | assertEquals("uuid should be parsed", "2f234454-cf6d-4a0f-adf2-f4911ba9ffa6", beacon.getIdentifier(0).toString()); 85 | assertEquals("id2 should be parsed", "1", beacon.getIdentifier(1).toString()); 86 | assertEquals("id3 should be parsed", "2", beacon.getIdentifier(2).toString()); 87 | assertEquals("txPower should be parsed", -59, beacon.getTxPower()); 88 | assertEquals("manufacturer should be parsed", 0x118 ,beacon.getManufacturer()); 89 | assertEquals("missing data field zero should be zero", new Long(0l), beacon.getDataFields().get(0)); 90 | } 91 | } -------------------------------------------------------------------------------- /lib/src/main/java/org/altbeacon/beacon/IntentHandler.java: -------------------------------------------------------------------------------- 1 | package org.altbeacon.beacon; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | 6 | import org.altbeacon.beacon.logging.LogManager; 7 | import org.altbeacon.beacon.service.MonitoringData; 8 | import org.altbeacon.beacon.service.MonitoringStatus; 9 | import org.altbeacon.beacon.service.RangingData; 10 | 11 | import java.util.Set; 12 | 13 | /** 14 | * Converts internal Intents for ranging/monitoring to notifier callbacks. 15 | * These may be direct method calls from BeaconLocalBroadcastProcessor or 16 | * global broadcast intents from BeaconIntentProcessor 17 | * 18 | * Internal library class. Do not use directly from outside the library 19 | * 20 | * @hide 21 | * Created by dyoung on 7/20/17. 22 | */ 23 | 24 | public class IntentHandler { 25 | private static final String TAG = IntentHandler.class.getSimpleName(); 26 | public void convertIntentsToCallbacks(Context context, Intent intent) { 27 | MonitoringData monitoringData = null; 28 | RangingData rangingData = null; 29 | 30 | if (intent != null && intent.getExtras() != null) { 31 | if (intent.getExtras().getBundle("monitoringData") != null) { 32 | monitoringData = MonitoringData.fromBundle(intent.getExtras().getBundle("monitoringData")); 33 | } 34 | if (intent.getExtras().getBundle("rangingData") != null) { 35 | rangingData = RangingData.fromBundle(intent.getExtras().getBundle("rangingData")); 36 | } 37 | } 38 | 39 | if (rangingData != null) { 40 | LogManager.d(TAG, "got ranging data"); 41 | if (rangingData.getBeacons() == null) { 42 | LogManager.w(TAG, "Ranging data has a null beacons collection"); 43 | } 44 | Set notifiers = BeaconManager.getInstanceForApplication(context).getRangingNotifiers(); 45 | java.util.Collection beacons = rangingData.getBeacons(); 46 | if (notifiers != null) { 47 | for(RangeNotifier notifier : notifiers){ 48 | notifier.didRangeBeaconsInRegion(beacons, rangingData.getRegion()); 49 | } 50 | } 51 | else { 52 | LogManager.d(TAG, "but ranging notifier is null, so we're dropping it."); 53 | } 54 | RangeNotifier dataNotifier = BeaconManager.getInstanceForApplication(context).getDataRequestNotifier(); 55 | if (dataNotifier != null) { 56 | dataNotifier.didRangeBeaconsInRegion(beacons, rangingData.getRegion()); 57 | } 58 | if (BeaconManager.getInstanceForApplication(context).isRegionViewModelInitialized(rangingData.getRegion())) { 59 | RegionViewModel regionViewModel = BeaconManager.getInstanceForApplication(context).getRegionViewModel(rangingData.getRegion()); 60 | regionViewModel.getRangedBeacons().postValue(rangingData.getBeacons()); 61 | } 62 | } 63 | 64 | if (monitoringData != null) { 65 | LogManager.d(TAG, "got monitoring data"); 66 | Set notifiers = BeaconManager.getInstanceForApplication(context).getMonitoringNotifiers(); 67 | Region region = monitoringData.getRegion(); 68 | Integer state = monitoringData.isInside() ? MonitorNotifier.INSIDE : 69 | MonitorNotifier.OUTSIDE; 70 | if (notifiers != null) { 71 | for(MonitorNotifier notifier : notifiers) { 72 | LogManager.d(TAG, "Calling monitoring notifier: %s", notifier); 73 | notifier.didDetermineStateForRegion(state, region); 74 | // In case the beacon scanner is running in a separate process, the monitoring 75 | // status in this process will not have been updated yet as a result of this 76 | // region state change. We make a call here to keep it in sync. 77 | MonitoringStatus.getInstanceForApplication(context).updateLocalState(region, state); 78 | if (monitoringData.isInside()) { 79 | notifier.didEnterRegion(monitoringData.getRegion()); 80 | } else { 81 | notifier.didExitRegion(monitoringData.getRegion()); 82 | } 83 | } 84 | } 85 | if (BeaconManager.getInstanceForApplication(context).isRegionViewModelInitialized(monitoringData.getRegion())) { 86 | RegionViewModel regionViewModel = BeaconManager.getInstanceForApplication(context).getRegionViewModel(monitoringData.getRegion()); 87 | regionViewModel.getRegionState().postValue(state); 88 | } 89 | 90 | } 91 | 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /lib/src/main/java/org/altbeacon/beacon/service/RangeState.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Radius Networks, Inc. 3 | * http://www.radiusnetworks.com 4 | * 5 | * @author David G. Young 6 | * 7 | * Licensed to the Apache Software Foundation (ASF) under one 8 | * or more contributor license agreements. See the NOTICE file 9 | * distributed with this work for additional information 10 | * regarding copyright ownership. The ASF licenses this file 11 | * to you under the Apache License, Version 2.0 (the 12 | * "License"); you may not use this file except in compliance 13 | * with the License. You may obtain a copy of the License at 14 | * 15 | * http://www.apache.org/licenses/LICENSE-2.0 16 | * 17 | * Unless required by applicable law or agreed to in writing, 18 | * software distributed under the License is distributed on an 19 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 20 | * KIND, either express or implied. See the License for the 21 | * specific language governing permissions and limitations 22 | * under the License. 23 | */ 24 | package org.altbeacon.beacon.service; 25 | 26 | import org.altbeacon.beacon.Beacon; 27 | import org.altbeacon.beacon.logging.LogManager; 28 | 29 | import java.io.Serializable; 30 | import java.util.ArrayList; 31 | import java.util.Collection; 32 | import java.util.HashMap; 33 | import java.util.Map; 34 | 35 | public class RangeState implements Serializable { 36 | private static final String TAG = "RangeState"; 37 | private Callback mCallback; 38 | private Map mRangedBeacons = new HashMap(); 39 | private static boolean sUseTrackingCache = false; 40 | 41 | public RangeState(Callback c) { 42 | mCallback = c; 43 | } 44 | 45 | public Callback getCallback() { 46 | return mCallback; 47 | } 48 | 49 | public void addBeacon(Beacon beacon) { 50 | RangedBeacon rangedBeacon = mRangedBeacons.get(beacon); 51 | if (rangedBeacon != null) { 52 | if (LogManager.isVerboseLoggingEnabled()) { 53 | LogManager.d(TAG, "adding %s to existing range for: %s", beacon, rangedBeacon); 54 | } 55 | rangedBeacon.updateBeacon(beacon); 56 | } 57 | else { 58 | if (LogManager.isVerboseLoggingEnabled()) { 59 | LogManager.d(TAG, "adding %s to new rangedBeacon", beacon); 60 | } 61 | mRangedBeacons.put(beacon, new RangedBeacon(beacon)); 62 | } 63 | } 64 | 65 | public int count() { 66 | synchronized (mRangedBeacons) { 67 | return mRangedBeacons.size(); 68 | } 69 | } 70 | 71 | // returns a list of beacons that are tracked, and then removes any from the list that should not 72 | // be there for the next cycle 73 | public synchronized Collection finalizeBeacons() { 74 | Map newRangedBeacons = new HashMap(); 75 | ArrayList finalizedBeacons = new ArrayList(); 76 | 77 | synchronized (mRangedBeacons) { 78 | for (Beacon beacon : mRangedBeacons.keySet()) { 79 | RangedBeacon rangedBeacon = mRangedBeacons.get(beacon); 80 | if (rangedBeacon != null) { 81 | if (rangedBeacon.isTracked()) { 82 | rangedBeacon.commitMeasurements(); // calculates accuracy 83 | if (!rangedBeacon.noMeasurementsAvailable()) { 84 | finalizedBeacons.add(rangedBeacon.getBeacon()); 85 | } 86 | } 87 | // If we still have useful measurements, keep it around but mark it as not 88 | // tracked anymore so we don't pass it on as visible unless it is seen again 89 | if (!rangedBeacon.noMeasurementsAvailable() == true) { 90 | //if TrackingCache is enabled, allow beacon to not receive 91 | //measurements for a certain amount of time 92 | if (!sUseTrackingCache || rangedBeacon.isExpired()) 93 | rangedBeacon.setTracked(false); 94 | newRangedBeacons.put(beacon, rangedBeacon); 95 | } 96 | else { 97 | LogManager.d(TAG, "Dumping beacon from RangeState because it has no recent measurements."); 98 | } 99 | } 100 | } 101 | mRangedBeacons = newRangedBeacons; 102 | } 103 | 104 | return finalizedBeacons; 105 | } 106 | 107 | public static void setUseTrackingCache(boolean useTrackingCache) { 108 | RangeState.sUseTrackingCache = useTrackingCache; 109 | } 110 | 111 | public static boolean getUseTrackingCache() { 112 | return sUseTrackingCache; 113 | } 114 | 115 | } 116 | -------------------------------------------------------------------------------- /lib/src/main/java/org/altbeacon/beacon/AltBeacon.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Radius Networks, Inc. 3 | * http://www.radiusnetworks.com 4 | * 5 | * @author David G. Young 6 | * 7 | * Licensed to the Apache Software Foundation (ASF) under one 8 | * or more contributor license agreements. See the NOTICE file 9 | * distributed with this work for additional information 10 | * regarding copyright ownership. The ASF licenses this file 11 | * to you under the Apache License, Version 2.0 (the 12 | * "License"); you may not use this file except in compliance 13 | * with the License. You may obtain a copy of the License at 14 | * 15 | * http://www.apache.org/licenses/LICENSE-2.0 16 | * 17 | * Unless required by applicable law or agreed to in writing, 18 | * software distributed under the License is distributed on an 19 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 20 | * KIND, either express or implied. See the License for the 21 | * specific language governing permissions and limitations 22 | * under the License. 23 | */ 24 | package org.altbeacon.beacon; 25 | 26 | import android.os.Parcel; 27 | import android.os.Parcelable; 28 | import android.util.Log; 29 | 30 | import java.util.ArrayList; 31 | 32 | /** 33 | *

The AltBeacon class represents a single hardware AltBeacon detected by 34 | * an Android device. It is more specific than the Beacon class in that it provides 35 | * access to the #mfgReserved field.

36 | * 37 | *

An AltBeacon is identified by a unique three part identifier. The first 38 | * identifier Id1 is normally used across an organization, the second identifier Id2 is used to 39 | * group beacons and the third identifier Id3 is used to uniquely identify a specific beacon (in 40 | * combination with the other two identifiers.) 41 | * 42 | * @author David G. Young 43 | */ 44 | public class AltBeacon extends Beacon { 45 | private static final String TAG = "AltBeacon"; 46 | 47 | /** 48 | * Required for making object Parcelable. If you override this class, you must provide an 49 | * equivalent version of this method. 50 | */ 51 | public static final Parcelable.Creator CREATOR 52 | = new Parcelable.Creator() { 53 | public AltBeacon createFromParcel(Parcel in) { 54 | return new AltBeacon(in); 55 | } 56 | 57 | public AltBeacon[] newArray(int size) { 58 | return new AltBeacon[size]; 59 | } 60 | }; 61 | 62 | /** 63 | * Copy constructor from base class 64 | * @param beacon 65 | */ 66 | protected AltBeacon(Beacon beacon) { 67 | super(beacon); 68 | } 69 | 70 | /** 71 | * @see AltBeacon.Builder to make AltBeacon instances 72 | */ 73 | protected AltBeacon() { 74 | super(); 75 | } 76 | 77 | /** 78 | * Required for making object Parcelable 79 | **/ 80 | protected AltBeacon(Parcel in) { 81 | super(in); 82 | } 83 | 84 | /** 85 | * Returns a field with a value from 0-255 that can be used for the purposes specified by the 86 | * manufacturer. The manufacturer specifications for the beacon should be checked before using 87 | * this field, and the manufacturer should be checked against the Beacon#mManufacturer 88 | * field 89 | * @return mfgReserved 90 | */ 91 | public int getMfgReserved() { 92 | return mDataFields.get(0).intValue(); 93 | } 94 | 95 | /** 96 | * Required for making object Parcelable 97 | * @return 98 | */ 99 | @Override 100 | public int describeContents() { 101 | return 0; 102 | } 103 | 104 | /** 105 | * Required for making object Parcelable 106 | **/ 107 | @Override 108 | public void writeToParcel(Parcel out, int flags) { 109 | super.writeToParcel(out, flags); 110 | } 111 | 112 | 113 | /** 114 | * Builder class for AltBeacon objects. Provides a convenient way to set the various fields of a 115 | * Beacon 116 | * 117 | *

Example: 118 | * 119 | *

120 |      * Beacon beacon = new Beacon.Builder()
121 |      *         .setId1("2F234454-CF6D-4A0F-ADF2-F4911BA9FFA6")
122 |      *         .setId2("1")
123 |      *         .setId3("2")
124 |      *         .setMfgReserved(3);
125 |      *         .build();
126 |      * 
127 | */ 128 | public static class Builder extends Beacon.Builder { 129 | @Override 130 | public Beacon build() { 131 | return new AltBeacon(super.build()); 132 | } 133 | public Builder setMfgReserved(int mfgReserved) { 134 | if (mBeacon.mDataFields.size() != 0) { 135 | mBeacon.mDataFields = new ArrayList(); 136 | } 137 | mBeacon.mDataFields.add((long)mfgReserved); 138 | return this; 139 | } 140 | } 141 | 142 | } 143 | -------------------------------------------------------------------------------- /lib/src/test/java/org/altbeacon/beacon/logging/VerboseAndroidLoggerTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Radius Networks, Inc. 3 | * Copyright 2015 Andrew Reitz 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.altbeacon.beacon.logging; 18 | 19 | import org.junit.Test; 20 | import org.junit.runner.RunWith; 21 | import org.robolectric.RobolectricTestRunner; 22 | import org.robolectric.annotation.Config; 23 | import org.robolectric.shadows.ShadowLog; 24 | 25 | import java.util.List; 26 | 27 | import static android.util.Log.DEBUG; 28 | import static android.util.Log.ERROR; 29 | import static android.util.Log.INFO; 30 | import static android.util.Log.VERBOSE; 31 | import static android.util.Log.WARN; 32 | import static junit.framework.Assert.assertEquals; 33 | 34 | /** 35 | * Ensure the verbose logger logs correctly. 36 | * 37 | * @author Andrew Reitz 38 | */ 39 | @Config(sdk = 28) 40 | @RunWith(RobolectricTestRunner.class) 41 | public class VerboseAndroidLoggerTest { 42 | private String tag = getClass().getName(); 43 | private Logger logger = new VerboseAndroidLogger(); 44 | 45 | @Test 46 | public void verboseLoggedCorrectly() { 47 | String expectedMessage = "Hello World"; 48 | 49 | logger.v(tag, "Hello %s", "World"); 50 | 51 | assertLogged(VERBOSE, tag, expectedMessage, null); 52 | } 53 | 54 | @Test 55 | public void verboseWithThrowableLoggedCorrectly() { 56 | String expectedMessage = "Hello World"; 57 | Throwable t = new Throwable("Test Throwable"); 58 | 59 | logger.v(t, tag, "Hello %s", "World"); 60 | 61 | assertLogged(VERBOSE, tag, expectedMessage, t); 62 | } 63 | 64 | @Test 65 | public void debugLoggedCorrectly() { 66 | String expectedMessage = "Hello World"; 67 | 68 | logger.d(tag, "Hello %s", "World"); 69 | 70 | assertLogged(DEBUG, tag, expectedMessage, null); 71 | } 72 | 73 | @Test 74 | public void debugWithThrowableLoggedCorrectly() { 75 | String expectedMessage = "Hello World"; 76 | Throwable t = new Throwable("Test Throwable"); 77 | 78 | logger.d(t, tag, "Hello %s", "World"); 79 | 80 | assertLogged(DEBUG, tag, expectedMessage, t); 81 | } 82 | 83 | @Test 84 | public void infoLoggedCorrectly() { 85 | String expectedMessage = "Hello World"; 86 | 87 | logger.v(tag, "Hello %s", "World"); 88 | 89 | assertLogged(VERBOSE, tag, expectedMessage, null); 90 | } 91 | 92 | @Test 93 | public void infoWithThrowableLoggedCorrectly() { 94 | String expectedMessage = "Hello World"; 95 | Throwable t = new Throwable("Test Throwable"); 96 | 97 | logger.i(t, tag, "Hello %s", "World"); 98 | 99 | assertLogged(INFO, tag, expectedMessage, t); 100 | } 101 | 102 | @Test 103 | public void warningLoggedCorrectly() { 104 | String expectedMessage = "Hello World"; 105 | 106 | logger.w(tag, "Hello %s", "World"); 107 | 108 | assertLogged(WARN, tag, expectedMessage, null); 109 | } 110 | 111 | @Test 112 | public void warningWithThrowableLoggedCorrectly() { 113 | String expectedMessage = "Hello World"; 114 | Throwable t = new Throwable("Test Throwable"); 115 | 116 | logger.w(t, tag, "Hello %s", "World"); 117 | 118 | assertLogged(WARN, tag, expectedMessage, t); 119 | } 120 | 121 | @Test 122 | public void errorLoggedCorrectly() { 123 | String expectedMessage = "Hello World"; 124 | 125 | logger.e(tag, "Hello %s", "World"); 126 | 127 | assertLogged(ERROR, tag, expectedMessage, null); 128 | } 129 | 130 | @Test 131 | public void errorWithThrowableLoggedCorrectly() { 132 | String expectedMessage = "Hello World"; 133 | Throwable t = new Throwable("Test Throwable"); 134 | 135 | logger.e(t, tag, "Hello %s", "World"); 136 | 137 | assertLogged(ERROR, tag, expectedMessage, t); 138 | } 139 | 140 | private void assertLogged(int type, String tag, String msg, Throwable throwable) { 141 | List logs = ShadowLog.getLogs(); 142 | ShadowLog.LogItem lastLog = logs.get(logs.size() - 1); 143 | assertEquals(type, lastLog.type); 144 | assertEquals(msg, lastLog.msg); 145 | assertEquals(tag, lastLog.tag); 146 | assertEquals(throwable, lastLog.throwable); 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /lib/src/test/java/org/altbeacon/beacon/logging/WarningAndroidLoggerTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Radius Networks, Inc. 3 | * Copyright 2015 Andrew Reitz 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.altbeacon.beacon.logging; 18 | 19 | import android.util.Log; 20 | 21 | import org.junit.Test; 22 | import org.junit.runner.RunWith; 23 | import org.robolectric.RobolectricTestRunner; 24 | import org.robolectric.annotation.Config; 25 | import org.robolectric.shadows.ShadowLog; 26 | 27 | import java.util.List; 28 | 29 | import static android.util.Log.ERROR; 30 | import static android.util.Log.WARN; 31 | import static junit.framework.Assert.assertEquals; 32 | import static org.hamcrest.Matchers.isOneOf; 33 | import static org.junit.Assert.assertThat; 34 | 35 | /** 36 | * Ensure the warning logger logs correctly. 37 | * 38 | * @author Andrew Reitz 39 | */ 40 | @Config(sdk = 28) 41 | @RunWith(RobolectricTestRunner.class) 42 | public class WarningAndroidLoggerTest { 43 | private String tag = getClass().getName(); 44 | private Logger logger = new WarningAndroidLogger(); 45 | 46 | @Test 47 | public void verboseNotLogged() { 48 | logger.v(tag, "Hello %s", "World"); 49 | 50 | assertNotLogged(); 51 | } 52 | 53 | @Test 54 | public void verboseWithThrowableNotLogged() { 55 | Throwable t = new Throwable("Test Throwable"); 56 | 57 | logger.v(t, tag, "Hello %s", "World"); 58 | 59 | assertNotLogged(); 60 | } 61 | 62 | @Test 63 | public void debugNotLogged() { 64 | String expectedTag = "TestTag"; 65 | 66 | logger.d(expectedTag, "Hello %s", "World"); 67 | 68 | assertNotLogged(); 69 | } 70 | 71 | @Test 72 | public void debugWithThrowableNotLogged() { 73 | Throwable t = new Throwable("Test Throwable"); 74 | 75 | logger.d(t, tag, "Hello %s", "World"); 76 | 77 | assertNotLogged(); 78 | } 79 | 80 | @Test 81 | public void infoNotLogged() { 82 | String expectedTag = "TestTag"; 83 | 84 | logger.v(expectedTag, "Hello %s", "World"); 85 | 86 | assertNotLogged(); 87 | } 88 | 89 | @Test 90 | public void infoWithThrowableNotLogged() { 91 | Throwable t = new Throwable("Test Throwable"); 92 | 93 | logger.i(t, tag, "Hello %s", "World"); 94 | 95 | assertNotLogged(); 96 | } 97 | 98 | @Test 99 | public void warningLoggedCorrectly() { 100 | String expectedMessage = "Hello World"; 101 | 102 | logger.w(tag, "Hello %s", "World"); 103 | 104 | assertLogged(WARN, tag, expectedMessage, null); 105 | } 106 | 107 | @Test 108 | public void warningWithThrowableLoggedCorrectly() { 109 | String expectedMessage = "Hello World"; 110 | Throwable t = new Throwable("Test Throwable"); 111 | 112 | logger.w(t, tag, "Hello %s", "World"); 113 | 114 | assertLogged(WARN, tag, expectedMessage, t); 115 | } 116 | 117 | @Test 118 | public void errorLoggedCorrectly() { 119 | String expectedMessage = "Hello World"; 120 | 121 | logger.e(tag, "Hello %s", "World"); 122 | 123 | assertLogged(ERROR, tag, expectedMessage, null); 124 | } 125 | 126 | @Test 127 | public void errorWithThrowableLoggedCorrectly() { 128 | String expectedMessage = "Hello World"; 129 | Throwable t = new Throwable("Test Throwable"); 130 | 131 | logger.e(t, tag, "Hello %s", "World"); 132 | 133 | assertLogged(ERROR, tag, expectedMessage, t); 134 | } 135 | 136 | private void assertLogged(int type, String tag, String msg, Throwable throwable) { 137 | List logs = ShadowLog.getLogs(); 138 | ShadowLog.LogItem lastLog = logs.get(logs.size() - 1); 139 | assertEquals(type, lastLog.type); 140 | assertEquals(msg, lastLog.msg); 141 | assertEquals(tag, lastLog.tag); 142 | assertEquals(throwable, lastLog.throwable); 143 | } 144 | 145 | private void assertNotLogged() { 146 | final List logs = ShadowLog.getLogs(); 147 | for (ShadowLog.LogItem log : logs) { 148 | //INFO level log was introduced by ASOP 149 | //https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/content/res/AssetManager.java 150 | assertThat(log.type, isOneOf(Log.INFO, Log.WARN, Log.ERROR)); 151 | } 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /lib/src/main/java/org/altbeacon/beacon/service/RangedBeacon.java: -------------------------------------------------------------------------------- 1 | package org.altbeacon.beacon.service; 2 | 3 | import android.os.SystemClock; 4 | 5 | import org.altbeacon.beacon.Beacon; 6 | import org.altbeacon.beacon.BeaconManager; 7 | import org.altbeacon.beacon.logging.LogManager; 8 | 9 | import java.io.Serializable; 10 | import java.lang.reflect.Constructor; 11 | 12 | public class RangedBeacon implements Serializable { 13 | 14 | private static final String TAG = "RangedBeacon"; 15 | public static final long DEFAULT_MAX_TRACKING_AGE = 5000; /* 5 Seconds */ 16 | public static long maxTrackingAge = DEFAULT_MAX_TRACKING_AGE; /* 5 Seconds */ 17 | //kept here for backward compatibility 18 | public static final long DEFAULT_SAMPLE_EXPIRATION_MILLISECONDS = 20000; /* 20 seconds */ 19 | private static long sampleExpirationMilliseconds = DEFAULT_SAMPLE_EXPIRATION_MILLISECONDS; 20 | private boolean mTracked = true; 21 | protected long lastTrackedTimeMillis = 0; 22 | Beacon mBeacon; 23 | protected transient RssiFilter mFilter = null; 24 | private int packetCount = 0; 25 | private long firstCycleDetectionTimestamp = 0; 26 | private long lastCycleDetectionTimestamp = 0; 27 | 28 | public RangedBeacon(Beacon beacon) { 29 | updateBeacon(beacon); 30 | } 31 | 32 | public void updateBeacon(Beacon beacon) { 33 | packetCount += 1; 34 | mBeacon = beacon; 35 | if(firstCycleDetectionTimestamp == 0) { 36 | firstCycleDetectionTimestamp = beacon.getFirstCycleDetectionTimestamp(); 37 | } 38 | lastCycleDetectionTimestamp = beacon.getLastCycleDetectionTimestamp(); 39 | addMeasurement(mBeacon.getRssi()); 40 | } 41 | 42 | public boolean isTracked() { 43 | return mTracked; 44 | } 45 | 46 | public void setTracked(boolean tracked) { 47 | mTracked = tracked; 48 | } 49 | 50 | public Beacon getBeacon() { 51 | return mBeacon; 52 | } 53 | 54 | // Done at the end of each cycle before data are sent to the client 55 | public void commitMeasurements() { 56 | if (!getFilter().noMeasurementsAvailable()) { 57 | double runningAverage = getFilter().calculateRssi(); 58 | mBeacon.setRunningAverageRssi(runningAverage); 59 | mBeacon.setRssiMeasurementCount(getFilter().getMeasurementCount()); 60 | LogManager.d(TAG, "calculated new runningAverageRssi: %s", runningAverage); 61 | } 62 | else { 63 | LogManager.d(TAG, "No measurements available to calculate running average"); 64 | } 65 | mBeacon.setPacketCount(packetCount); 66 | mBeacon.setFirstCycleDetectionTimestamp(firstCycleDetectionTimestamp); 67 | mBeacon.setLastCycleDetectionTimestamp(lastCycleDetectionTimestamp); 68 | packetCount = 0; 69 | firstCycleDetectionTimestamp = 0L; 70 | lastCycleDetectionTimestamp = 0L; 71 | } 72 | 73 | public void addMeasurement(Integer rssi) { 74 | // Filter out unreasonable values per 75 | // http://stackoverflow.com/questions/30118991/rssi-returned-by-altbeacon-library-127-messes-up-distance 76 | if (rssi != 127) { 77 | mTracked = true; 78 | lastTrackedTimeMillis = SystemClock.elapsedRealtime(); 79 | getFilter().addMeasurement(rssi); 80 | } 81 | } 82 | 83 | //kept here for backward compatibility 84 | public static void setSampleExpirationMilliseconds(long milliseconds) { 85 | sampleExpirationMilliseconds = milliseconds; 86 | RunningAverageRssiFilter.setSampleExpirationMilliseconds(sampleExpirationMilliseconds); 87 | } 88 | 89 | public static void setMaxTrackinAge(int maxTrackinAge) { 90 | RangedBeacon.maxTrackingAge = maxTrackinAge; 91 | } 92 | 93 | public boolean noMeasurementsAvailable() { 94 | return getFilter().noMeasurementsAvailable(); 95 | } 96 | 97 | public long getTrackingAge() { 98 | return SystemClock.elapsedRealtime() - lastTrackedTimeMillis; 99 | } 100 | 101 | public boolean isExpired() { 102 | return getTrackingAge() > maxTrackingAge; 103 | } 104 | 105 | private RssiFilter getFilter() { 106 | if (mFilter == null) { 107 | // Use custom RSSI filter 108 | if (BeaconManager.getRssiFilterImplClass() != null) { 109 | try { 110 | mFilter = (RssiFilter) BeaconManager.getRssiFilterImplClass().newInstance(); 111 | } catch (Exception e) { 112 | LogManager.e(TAG, "Failed with exception %s", e.toString()); 113 | LogManager.e(TAG, "Could not construct class %s", BeaconManager.getRssiFilterImplClass().getName()); 114 | LogManager.e(TAG, "Will default to RunningAverageRssiFilter"); 115 | } 116 | } 117 | if (mFilter == null) { 118 | // Use default rssi filter 119 | mFilter = new RunningAverageRssiFilter(); 120 | } 121 | } 122 | return mFilter; 123 | } 124 | } 125 | --------------------------------------------------------------------------------