Swiss army knife for identifying and fingerprinting Android devices.

Overview

FingerprintJS

Latest release Build status Android minAPI status

Discord server

PlaygroundApp

fingerprint android

Lightweight library for device identification and fingerprinting.

Fully written in Kotlin. 100% Crash-free.

Creates a device identifier from all available platform signals.

The identifier is fully stateless and will remain the same after reinstalling or clearing application data.

Table of Contents

  1. Quick start
  2. Usage
  3. Advanced usage
  4. Playground App

Quick start

Add repository

Add these lines to your build.gradle.

allprojects {	
  repositories {
  ...
  maven { url 'https://jitpack.io' }	
}}

Add dependency

Add these lines to build.gradle of a module.

This library depends on kotlin-stdlib.

If your application is written in Java, add kotlin-stdlib dependency first (it's lightweight and has excellent backward and forward compatibility).

dependencies {
  // Add this line only if you use this library with Java
  implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"

  implementation "com.github.fingerprintjs:fingerprint-android:1.2"
}

deviceId vs fingerprint

The library operates with two entities.

  1. deviceId - is a unique device identifier.

Can be used by developers to identify devices to deliver personalized content, detect suspicious activity, and perform fraud detection. Internally it will use Google Service Framework ID if it's available and ANDROID_ID, if GSF ID is not available. This identifier is stable, i.e. it will remain the same even after reinstalling your app. But it will be different after factory reset of the device.

  1. fingerprint is a digital device fingerprint. It works by combining all available device signals and attributes into a single identifier. There is a probability that two identical devices will have the same fingerprint.

Which one should I use?

deviceId is guaranteed to be unique and should be your first choice for device identification. This identifier can be spoofed though and shouldn't be used in security-focused or fraud detection scenarios.

fingerprint is much harder to spoof and is a safer choice in security-focused use cases.

Usage

Kotlin

// Initialization
 val fingerprinter = FingerprinterFactory
		.getInstance(applicationContext, Configuration(version = 3))


// Usage
fingerprinter.getFingerprint { fingerprintResult ->
  val fingerprint = fingerprintResult.fingerprint
}

fingerprinter.getDeviceId { result ->
  val deviceId = result.deviceId
}

Java

// Initialization
Fingerprinter fingerprinter = FingerprinterFactory
				.getInstance(getApplicationContext(), new Configuration(3));


// Usage
fingerprinter.getFingerprint(new Function1<FingerprintResult, Unit>() {
        @Override
        public Unit invoke(FingerprintResult fingerprintResult) {
        	String fingerprint = fingerprintResult.getFingerprint();
        	    return null;
            }
        });
        
fingerprinter.getDeviceId(new Function1<DeviceIdResult, Unit>() {
            @Override
            public Unit invoke(DeviceIdResult deviceIdResult) {
            	String deviceId = deviceIdResult.getDeviceId();
                return null;
            }
        });

getFingerprint and getDeviceId methods execute on a separate thread. Keep this in mind when using results on the main thread.

Also the results are cached, so subsequent calls will be faster.

Versioning

fingerprint is versioned incrementatlly; the version should be set explicitly to avoid unexpected fingerprint changes when updating the library.

The version is set while the initialization of the library with Configuration class.

val fingerprinter = FingerprinterFactory
		.getInstance(applicationContext, Configuration(version = 3))

Advanced usage

Reference for Kotlin is provided below. Java reference.

The full public API of the library is following:

interface Fingerprinter {
  fun getDeviceId(listener: (DeviceIdResult) -> (Unit))
  fun getFingerprint(listener: (FingerprintResult) -> (Unit))
  fun getFingerprint(stabilityLevel: StabilityLevel, listener: (FingerprintResult) -> (Unit))
}

interface FingerprintResult {
  val fingerprint: String
  fun <T> getSignalProvider(clazz: Class<T>): T?
}

data class DeviceIdResult(
  val deviceId: String,
  val gsfId: String?,
  val androidId: String,
  val mediaDrmId: String?
)

If you are using RxJava or Kotlin Coroutines - use the extensions.

Increasing the uniqueness of fingerprints

There is a probability that two different devices will have the same fingerprint value. There is also a probability that the same device will have different fingerprint values in different moments of time due to system upgrades or updated settings (although this should be rare).

By default the library calculates a fingerprint with the best combination of stability and uniqueness. This stability level is called StabilityMode.OPTIMAL. There are two other stability modes for fingerprinting: Stable and Unique. Stable is when you prefer a more stable, but potentially a less unique fingerprint. Unique is when you want a very unique, but potentially a less stable fingerprint.

Use them as shown below:

fingerprinter.getFingerprint(StabilityMode.STABLE) { fingerprintResult ->
  val stableFingerprint = fingerprintResult.fingerprint
}

fingerprinter.getFingerprint(StabilityMode.OPTIMAL) { fingerprintResult ->
  val optimalFingerprint = fingerprintResult.fingerprint
}

fingerprinter.getFingerprint(StabilityMode.UNIQUE) { fingerprintResult ->
  val uniqueFingerprint = fingerprintResult.fingerprint
}

Raw data access

If you need access to raw data from signal providers, you can get it as shown below:

fingerprinter.getFingerprint { fingerprintResult ->

  val hardwareSignalProvider = fingerprintResult
  			.getSignalProvider(HardwareSignalGroupProvider::class.java)

  val hardwareFingerprint = hardwareSignalProvider.fingerprint()

  val cpuInfo = hardwareSignalProvider.rawData().procCpuInfo()
}

Change hash function

The library uses Murmur3 hash (64x128) which is fast and optimal for most cases.

If this hash function does not work for you, you can change it to a different one.

To do it, implement your own hasher, and pass it to Configuration class as shown below:

val hasher = object : Hasher {
  override fun hash(data: String): String {
    // Implement your own hashing logic, e.g. call SHA256 here
  }
}

val fingerprinter = FingerprinterFactory.getInstance(
  applicationContext,
  Configuration(version = 1, hasher = hasher)

)

Backward compatibility

If you want to get a newer version of fingerprint, but also want to keep the old one for backward compatibility, you can get them both as shown below:

val v1Fingerprinter = FingerprinterFactory
		.getInstance(applicationContext, Configuration(version = 1))

val v2Fingerprinter = FingerprinterFactory
		.getInstance(applicationContext, Configuration(version = 2))


v1Fingerprinter.getFingerprint { fingerprintResult ->
  val v1Fingerprint = fingerprintResult.fingerprint
}

v2Fingerprinter.getFingerprint { fingerprintResult ->
  val v2Fingerprint = fingerprintResult.fingerprint
}

Playground App

Try all the library features in the Playground App.

PlaygroundApp

Android API support

fingerprint-android supports API versions from 21 (Android 5.0) and higher.

Contributing

Feel free to ask questions and request features. Just create an issue with a clear explanation of what you'd like to have in the library. For code contributions, please see the contributing guideline.

Testimonials

Just tested on HUAWEI Y6p, with factory reset, we can retrieve the same result with StabilityLevel.STABLE setting.

Thank you

GitHub user

License

This library is MIT licensed. Copyright FingerprintJS, Inc. 2020-2021.

Comments
  • Add multi-user and 'Instant App' use-case to the stability table

    Add multi-user and 'Instant App' use-case to the stability table

    I tried the Playground app with different 'Users' on my device (https://source.android.com/devices/tech/admin/multi-user) and discovered that the GSF ID is not stable. ~The ID that stayed the same is the Media DRM ID, thats why i introduced stabilityVersion 4 that uses the DRM ID as the default id, if available.~

    When fingerprintjs is run within an Instant App the GSF ID and Installed Apps fingerprint is not available at all. All other IDs and fingerprints are different from a regular app.

    I ~also~ add this information to the stability documentation ~and updated the README.~

    opened by AndreasBoehm 9
  •  java.lang.VerifyError: Verifier rejected class

    java.lang.VerifyError: Verifier rejected class

    I don't want to use callback, for that I am using suspendCancellableCoroutine, Below function, I have used to get deviceId

            val fingerprinted = FingerprinterFactory
               .getInstance(activity, Configuration(version = 1))
    
           val deviceId = suspendCancellableCoroutine<String> {
               try {
                   fingerprinted.getDeviceId { result ->
                       val deviceId = result.deviceId
                       it.resume(deviceId)
                   }
               }catch (e: Exception){
                   it.resumeWithException(e)
               }
               it.cancel()
           }
    

    I am always getting an error like below

    Process: com.account, PID: 19905
        java.lang.VerifyError: Verifier rejected class com.account.DeviceManager: java.lang.Object com.account.DeviceManager.getDeviceId(android.content.Context, kotlin.coroutines.Continuation) failed to verify: java.lang.Object com.account.DeviceManager.getDeviceId(android.content.Context, kotlin.coroutines.Continuation): [0x1A] register v1 has type Reference: android.content.Context but expected Precise Reference: com.account.DeviceManager (declaration of 'com.account.DeviceManager' appears in /data/app/com.account-cYswagkBsh_1A8mtjV9kYg==/base.apk!classes4.dex)
        
    
    opened by siddhpuraamitr 9
  • Make the library compatible with Instant Apps

    Make the library compatible with Instant Apps

    Due to some restrictions applied to Instant Apps the fingerprint library crashes when the instance is created.

    Adjustments:

    • check if externalStorageDir is readable before trying to access it, would crash otherwise
    • made DevicePolicyManager and KeyguardManager optional, would crash otherwise
    • remove unnecessary build version code check
    opened by AndreasBoehm 7
  • CpuInfoProvider might return different values

    CpuInfoProvider might return different values

    Hello,

    I am trying to figure it out what is happening with different fingerprints on different running. The main issue is that I found in CpuInfoProvider that reading /proc/cpuinfo multiple times, returns different values of BogoMIPS, therefore it will return different fingerprints.

    For exemple reading /proc/cpuinfo at one run: 2021-08-31 00:59:15.463 15908-15938/eco.eep I/System.out: HERE: processor : 0 2021-08-31 00:59:15.465 15908-15938/eco.eep I/System.out: HERE: BogoMIPS : 33.17 2021-08-31 00:59:15.475 15908-15938/eco.eep I/System.out: HERE: processor : 1 2021-08-31 00:59:15.478 15908-15938/eco.eep I/System.out: HERE: BogoMIPS : 33.17 2021-08-31 00:59:15.482 15908-15938/eco.eep I/System.out: HERE: processor : 2 2021-08-31 00:59:15.483 15908-15938/eco.eep I/System.out: HERE: BogoMIPS : 26.08 2021-08-31 00:59:15.491 15908-15938/eco.eep I/System.out: HERE: processor : 3 2021-08-31 00:59:15.494 15908-15938/eco.eep I/System.out: HERE: BogoMIPS : 26.08

    at second run: 2021-08-31 00:59:30.990 15994-16031/eco.eep I/System.out: HERE: c3e0428bf5fbd27f15932e800e87922a 2021-08-31 00:59:31.002 15994-16031/eco.eep I/System.out: HERE: processor : 0 2021-08-31 00:59:31.004 15994-16031/eco.eep I/System.out: HERE: BogoMIPS : 33.17 2021-08-31 00:59:31.009 15994-16031/eco.eep I/System.out: HERE: processor : 1 2021-08-31 00:59:31.010 15994-16031/eco.eep I/System.out: HERE: BogoMIPS : 33.17 2021-08-31 00:59:31.014 15994-16031/eco.eep I/System.out: HERE: processor : 2 2021-08-31 00:59:31.015 15994-16031/eco.eep I/System.out: HERE: BogoMIPS : 33.17 2021-08-31 00:59:31.021 15994-16031/eco.eep I/System.out: HERE: processor : 3 2021-08-31 00:59:31.024 15994-16031/eco.eep I/System.out: HERE: BogoMIPS : 33.17

    at 3rd run: 2021-08-31 00:59:52.482 16083-16120/eco.eep I/System.out: HERE: processor : 0 2021-08-31 00:59:52.484 16083-16120/eco.eep I/System.out: HERE: BogoMIPS : 33.17 2021-08-31 00:59:52.488 16083-16120/eco.eep I/System.out: HERE: processor : 1 2021-08-31 00:59:52.489 16083-16120/eco.eep I/System.out: HERE: BogoMIPS : 26.08 2021-08-31 00:59:52.493 16083-16120/eco.eep I/System.out: HERE: processor : 2 2021-08-31 00:59:52.495 16083-16120/eco.eep I/System.out: HERE: BogoMIPS : 26.08 2021-08-31 00:59:52.500 16083-16120/eco.eep I/System.out: HERE: processor : 3 2021-08-31 00:59:52.503 16083-16120/eco.eep I/System.out: HERE: BogoMIPS : 26.08

    Therefore the BogoMIPS in my case can be either 33.17 or 26.08, therefore the output of fingerprint will differ. Am I doing something wrong?

    opened by NeacsuCristian 5
  • How do you access a list of installed apps?

    How do you access a list of installed apps?

    Since Android 11, apps need the QUERY_ALL_PACKAGES permission to get a list of installed apps on the device. However, the playground app does not use this permission, but it still can get a list of my installed apps. How is this possible?

    opened by flexagoon 3
  • how getting fingerprint device id in java ?

    how getting fingerprint device id in java ?

    i tried to follow the documentation

    // Initialization
    Fingerprinter fingerprinter = FingerprinterFactory
    				.getInstance(getApplicationContext(), new Configuration(4));
    
    
    // Usage
    fingerprinter.getFingerprint(new Function1<FingerprintResult, Unit>() {
            @Override
            public Unit invoke(FingerprintResult fingerprintResult) {
            	String fingerprint = fingerprintResult.getFingerprint();
            	    return null;
                }
            });
    

    but what is function1 and unit ? unit refer to kotlin unit and i try to test in java

    opened by AhmedHumk 3
  • Fingerprint changes after device reboot

    Fingerprint changes after device reboot

    I have almost every reboot change all 3 types of fingerprint (STABLE, OPTIMAL, UNIQUE) Perhaps your library is bound to the mac address how to make fingerprint immutable?

    opened by aleksandrmozgovoi 3
  • Android TV support

    Android TV support

    On android tv devices i receive

    com.test.app I/ServiceManager: Waiting for service 'media.camera' on '/dev/binder'... com.test.app I/ServiceManager: Waiting for service 'media.camera' on '/dev/binder'... com.test.app I/ServiceManager: Waiting for service 'media.camera' on '/dev/binder'... com.test.app I/ServiceManager: Waiting for service 'media.camera' on '/dev/binder'... com.test.app W/ServiceManager: Service media.camera didn't start. Returning NULL com.test.app W/CameraBase: CameraService not published, waiting... com.test.app I/ServiceManager: Waiting for service 'media.camera' on '/dev/binder'... com.test.app I/ServiceManager: Waiting for service 'media.camera' on '/dev/binder'... com.test.app I/ServiceManager: Waiting for service 'media.camera' on '/dev/binder'...

    and app freeze how to fix ?

    bug 
    opened by AgnitumuS 3
  • Fingerpint result is not being cached contrary to what doc says

    Fingerpint result is not being cached contrary to what doc says

    Just some self-explanatory code:

        fun okCase(fingerprinter: Fingerprinter) {
            fingerprinter.getDeviceId(
                listener = { deviceId1 ->
                    fingerprinter.getDeviceId(
                        listener = { deviceId2 ->
                            // The following check returns true which is ok because the result
                            // is cached internally.
    
                            // Still have concerns whether we really need this though.
                            deviceId1 === deviceId2
                        }
                    )
                }
            )
        }
    
        fun bugCase(fingerprinter: Fingerprinter) {
            fingerprinter.getFingerprint(
                stabilityLevel = StabilityLevel.OPTIMAL,
                listener = { fingerprintResult1 ->
                    fingerprinter.getFingerprint(
                        stabilityLevel = StabilityLevel.OPTIMAL,
                        listener = { fingerprintResult2 ->
                            // Returns false which is not expected.
                            fingerprintResult1 === fingerprintResult2
                        }
                    )
                }
            )
        }
    
    opened by Sergey-Makarov 2
  • Cannot add this library to my project ? Failed to resolve: com.github.fingerprintjs:fingerprint-android:1.2

    Cannot add this library to my project ? Failed to resolve: com.github.fingerprintjs:fingerprint-android:1.2

    Hi ,

    My project is using java code. When I add this library to my project, I get the error as below :

    Failed to resolve: com.github.fingerprintjs:fingerprint-android:1.2
    Show in Project Structure dialog
    Affected Modules: MyProject
    

    Here is my build.gradle:

    buildscript {
    
    
        ext {
            kotlin_version = '1.3.72'
        }
        repositories {
            google()
            jcenter()
            maven { url 'https://maven.google.com' }
            maven { url 'https://jitpack.io' }
            mavenCentral()
        }
    
        dependencies {
            classpath 'com.android.tools.build:gradle:3.4.1'
            classpath 'com.google.gms:google-services:4.3.3'
            classpath 'com.google.firebase:firebase-crashlytics-gradle:2.4.1'
            classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    
        }
    }
    
    allprojects {
        repositories {
            google()
            jcenter()
            mavenCentral()
        }
    }
    
    task clean(type: Delete) {
        delete rootProject.buildDir
    }
    

    and here is my build app gradle:

    
    android {
        compileSdkVersion PROP_COMPILE_SDK_VERSION.toInteger() //31
        buildToolsVersion PROP_BUILD_TOOLS_VERSION
          defaultConfig {
            applicationId "com.my.app"
            minSdkVersion PROP_MIN_SDK_VERSION//16
            targetSdkVersion PROP_TARGET_SDK_VERSION//31
            versionCode 33
            versionName "3.3"
            multiDexEnabled true
    //.....
         }
    //.....
    }
    dependencies {
        implementation fileTree(include: ['*.jar'], dir: 'libs')
        implementation project(':libcocos2dx')
    
        implementation 'androidx.appcompat:appcompat:1.0.2'
        implementation 'com.google.firebase:firebase-ads:18.3.0'
        implementation 'com.google.firebase:firebase-analytics:17.2.2'
        implementation 'com.google.firebase:firebase-auth:19.2.0'
        implementation 'com.google.firebase:firebase-database:19.2.1'
        implementation 'com.google.android.gms:play-services-measurement:17.0.0'
        implementation 'com.google.android.gms:play-services-measurement-sdk:17.0.0'
        implementation 'com.google.android.gms:play-services-ads:19.0.1'
        implementation 'com.facebook.android:facebook-login:[9,10)'
        implementation 'com.anjlab.android.iab.v3:library:1.0.44'
        implementation 'com.google.firebase:firebase-crashlytics-ndk:17.4.0'
        implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
        implementation 'androidx.legacy:legacy-support-v4:1.0.0'
    
        // Add this line only if you use this library with Java
        implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    
        implementation "com.github.fingerprintjs:fingerprint-android:1.2"
    }
    
    apply plugin: 'com.google.gms.google-services'
    apply plugin: 'com.google.firebase.crashlytics'
    apply plugin: 'kotlin-android'
    
    

    Could anyone help me resolve this issue?

    opened by thienphuoc 2
  • Code example version seems arbitrary to me

    Code example version seems arbitrary to me

    When I'm reading the code example in the readme:

    // Initialization
     val fingerprinter = FingerprinterFactory
    		.getInstance(applicationContext, Configuration(version = 3))
    

    This part version = 3 seems random and arbitrary to me. Why is it 3? What if we create a constant Version.Latest, which will be 3 now, but will change with every major release? @Alexey-Verkhovsky

    opened by Valve 2
  • Publish on Maven Central

    Publish on Maven Central

    Firstly thanks for this library!

    For the last 24+ hours jitpack went down, which broke the build pipeline for apps using fingerprintjs-android.

    Have you considered publishing on a more reputable repository such as Maven Central, instead of just jitpack?

    opened by kimrtaylor 1
Releases(2.0.0)
Owner
FingerprintJS
Fraud detection API for the Internet
FingerprintJS
Get a unique ID for Android devices without any permissions.

Java and Kotlin Android library. Uniquely identify an Android device without any permissions and API restrictions. The recommended approach using DRM API.

Eyosiyas Bereketab 9 Aug 25, 2022
gRPC and protocol buffers for Android, Kotlin, and Java.

Wire “A man got to have a code!” - Omar Little See the project website for documentation and APIs. As our teams and programs grow, the variety and vol

Square 3.9k Dec 31, 2022
General purpose utilities and hash functions for Android and Java (aka java-common)

Essentials Essentials are a collection of general-purpose classes we found useful in many occasions. Beats standard Java API performance, e.g. LongHas

Markus Junginger 1.4k Dec 29, 2022
Access and process various types of personal data in Android with a set of easy, uniform, and privacy-friendly APIs.

PrivacyStreams PrivacyStreams is an Android library for easy and privacy-friendly personal data access and processing. It offers a functional programm

null 269 Dec 1, 2022
A simple and easy to use stopwatch and timer library for android

TimeIt Now with Timer support! A simple and easy to use stopwatch and timer library for android Introduction A stopwatch can be a very important widge

Yashovardhan Dhanania 35 Dec 10, 2022
Trail is a simple logging system for Java and Android. Create logs using the same API and the library will detect automatically in which platform the code is running.

Trail Trail is a simple logging system for Java and Android. Create logs using the same API and the library will detect automatically in which platfor

Mauricio Togneri 13 Aug 29, 2022
General purpose utilities and hash functions for Android and Java (aka java-common)

Essentials Essentials are a collection of general-purpose classes we found useful in many occasions. Beats standard Java API performance, e.g. LongHas

Markus Junginger 1.4k Dec 29, 2022
DiskCache - Simple and readable disk cache for kotlin and android applications

DiskCache Simple and readable disk cache for kotlin and android applications (with journaled lru strategy) This is a simple lru disk cache, based on t

Giovanni Corte 14 Dec 2, 2022
A library for fast and safe delivery of parameters for Activities and Fragments.

MorbidMask - 吸血面具 Read this in other languages: 中文, English, Change Log A library for fast and safe delivery of parameters for Activities and Fragment

Season 67 Mar 29, 2022
Matches incoming and/or outgoing text messages against set rules and sends them over to webhook.

Textmatic If you ever wanted a tool to simply push the SMS (or text messages) from your phone to somewhere remote, this is it. This app matches all in

Float 2 Jan 7, 2022
Command framework built around Kord, built to be robust and scalable, following Kord's convention and design patterns.

Command framework built around Kord, built to be robust and scalable, following Kord's convention and design patterns.

ZeroTwo Bot 4 Jun 15, 2022
a simple cache for android and java

ASimpleCache ASimpleCache 是一个为android制定的 轻量级的 开源缓存框架。轻量到只有一个java文件(由十几个类精简而来)。 1、它可以缓存什么东西? 普通的字符串、JsonObject、JsonArray、Bitmap、Drawable、序列化的java对象,和 b

Michael Yang 3.7k Dec 14, 2022
A lightning fast, transactional, file-based FIFO for Android and Java.

Tape by Square, Inc. Tape is a collection of queue-related classes for Android and Java. QueueFile is a lightning-fast, transactional, file-based FIFO

Square 2.4k Dec 30, 2022
UPnP/DLNA library for Java and Android

Cling EOL: This project is no longer actively maintained, code may be outdated. If you are interested in maintaining and developing this project, comm

4th Line 1.6k Jan 4, 2023
WebSocket & WAMP in Java for Android and Java 8

Autobahn|Java Client library providing WAMP on Java 8 (Netty) and Android, plus (secure) WebSocket for Android. Autobahn|Java is a subproject of the A

Crossbar.io 1.5k Dec 9, 2022
Collection of source codes, utilities, templates and snippets for Android development.

Android Templates and Utilities [DEPRECATED] Android Templates and Utilities are deprecated. I started with this project in 2012. Android ecosystem ha

Petr Nohejl 1.1k Nov 30, 2022
A Virtual Machine For Assessing Android applications, Reverse Engineering and Malware Analysis

Androl4b AndroL4b is an android security virtual machine based on ubuntu-mate includes the collection of latest framework, tutorials and labs from dif

null 1k Dec 27, 2022
Android library for viewing, editing and sharing in app databases.

DbInspector DbInspector provides a simple way to view the contents of the in-app database for debugging purposes. There is no need to pull the databas

Infinum 924 Jan 4, 2023
A beautiful set of predefined colors and a set of color methods to make your Android development life easier.

Colours is a port of the Colours Library for iOS made by my good friend Ben Gordon. You can find that project here. Installation Maven Central Colours

Matthew York 634 Dec 28, 2022