Skip to content

Instantly share code, notes, and snippets.

@bashenk
Last active December 9, 2025 14:48
Show Gist options
  • Select an option

  • Save bashenk/58c6dd883b177ee6e6ed1c533f3e8066 to your computer and use it in GitHub Desktop.

Select an option

Save bashenk/58c6dd883b177ee6e6ed1c533f3e8066 to your computer and use it in GitHub Desktop.
Creating a QR Code for Android Device Enrollment

Creating a QR Code for Android Device Enrollment

Android Enterprise Documentation: Create a QR code

Always required

Required if a DPC isn't already installed on the device

Recommended if the device isn't already connected to Wi-Fi

Optional


EMM Provisioning

Android Zero-Touch Enrollment EMM Provisioning Guide

👍 EMM Recommended

Use the following intent extras to set up your DPC

👎 EMM Not recommended

Don't include the following extras that you might use in other enrollment methods


Additional references

@bashenk
Copy link
Author

bashenk commented Mar 12, 2024

@rekire I updated the gist to include checksum calculation for the v2 signing scheme, which has a small chance of being one of the issues you could've encountered. To be clear, I was unable to get your sha256sum version to produce the proper checksum, but I also don't have a spare device to test with a factory reset these days, so I was just checking it against one of my known-working QR codes.

@robin-thoni
Copy link

Great quick guide, thanks!

I had to patch your command to get the signature checksum, though:

apksigner verify --print-certs com.afwsamples.testdpc_9.0.12.apk | grep 'Signer #1' | sed "/s*SHA-256/{s/.*SHA-256 digest:\s*//p};d" | xxd -r -p | openssl base64 | tr -- '+/' '-_' | tr -d '\n'

(Added the grep part`)

Since apksigner is outputting multiple hashes:

Signer #1 certificate DN: CN=testdpc, OU=Android, O=Google Inc., L=Mountain View, ST=California, C=US
Signer #1 certificate SHA-256 digest: 8090f6630b4e8962479123249087cb4658feaae36a1b57dbeafd74d109b333dc
Signer #1 certificate SHA-1 digest: 9476412b9e9d0fbcfb68f82d9a17c5a4859f70c6
Signer #1 certificate MD5 digest: 3f9b85d5b13dfb38c01a771ef60fa4b8
Source Stamp Signer certificate DN: CN=Android, OU=Android, O=Google Inc., L=Mountain View, ST=California, C=US
Source Stamp Signer certificate SHA-256 digest: 3257d599a49d2c961a471ca9843f59d341a405884583fc087df4237b733bbd6d
Source Stamp Signer certificate SHA-1 digest: b1af3a0bf998aeede1a8716a539e5a59da1d86d6
Source Stamp Signer certificate MD5 digest: 577b8a9fbc7e308321aec6411169d2fb

@bashenk
Copy link
Author

bashenk commented Oct 28, 2024

@robin-thoni Thanks for the update, including the example output! That modification could be easily added to the sed command, rather than creating a whole separate pipe for it. E.g., '/Signer #1 certificate SHA-256/{s/.*SHA-256 digest:\s*//;q};d'. I'll go ahead and update the gist with the new command.

@AhmadRaza159
Copy link

AhmadRaza159 commented Dec 8, 2024

@bashenk where to run these commands (keytool -printcert -jarfile "apkfile.apk" | sed '/\s*SHA256/{s/.SHA256:\s//;q};d' | xxd -r -p | openssl base64 | tr -- '+/' '-_' | tr -d '\n'), keytool is not recognized in command line

@bashenk
Copy link
Author

bashenk commented Dec 8, 2024

@AhmadRaza159 keytool is installed with the Java JRE/JDK, though you may need to update your PATH variable to include the bin folder in the Java directory (It can be installed either through your IDE or via Oracle JDK, OpenJDK, or choose your own alternative). Though, there's no need to use keytool anymore, because the first command (using apksigner) can handle V1 and V2 signed APKs.
The remainder of the commands should be preinstalled in any Linux operating environment, so you could either use a computer running a Linux distro, use WSL, or you could probably get it to work using Git for Windows if you include %ProgramFiles%\Git\usr\bin in your PATH (though if you choose the latter, I'd recommend reading up about what built-in Windows commands it ends up overriding). And also, if you're running it in Windows, you'll need to replace the single quotes (') with double quotes (") to get it to work on the command line, or it might work as-is in PowerShell if you have everything else right.

@AhmadRaza159 I've now added native PowerShell and PowerShell Core alternatives to the gist, though you'll still need apksigner or keytool.

@shaikh-kamran
Copy link

How we decide if we have to use PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM or PROVISIONING_DEVICE_ADMIN_SIGNATURE_CHECKSUM?

@robin-thoni
Copy link

How we decide if we have to use PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM or PROVISIONING_DEVICE_ADMIN_SIGNATURE_CHECKSUM?

PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM identifies a particular package build

PROVISIONING_DEVICE_ADMIN_SIGNATURE_CHECKSUM identifies the publisher

The later allows to install an updated version of the package without requiring regenerating the QR code. The first one ensures you're installing a trusted and/or well tested version of the package.

@shaikh-kamran
Copy link

Okay so i can use any one, both will work in any android version?

@robin-thoni
Copy link

EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM was added in API 21 (Android 5.0), while EXTRA_PROVISIONING_DEVICE_ADMIN_SIGNATURE_CHECKSUM was added in API 23 (Android 6.0)

@MichMich
Copy link

Thanks for this super complete piece information. Much appreciated. Can anyone confirm if this method should allow me to generate a QR to install an APK as “Device Owner” without the use of an MDM or Android Enterprise?

@robin-thoni
Copy link

Yes, you'll be able to install a Device Owner app using this method. You can try it out using this Google sample app.

@MichMich
Copy link

Thanks for the rapid response! I really appreciate it.

@MichaelTeeuw
Copy link

The Sample app seems to work. Good starter point to figure out why I run into issues with my app. Again: thanks for your help!

@MichaelTeeuw
Copy link

I apologies for using this Gist for asking this question, but does anybody happen to have a super barebones DPC kotlin application I can take a look at? I've been working on my own but unfortunately I'm unable to correctly use it using the QR method. After scanning the code, the app is being downloaded, android displays “Device belongs to your organization”, but then it fails. Debugging by fetching the logs is a PITA, but it looks like Android is trying to fall back on the (non existent) CloudDPC app in stead of using my app as the DPC.

Unable to start service Intent { cmp=com.android.managedprovisioning/com.google.android.apps.work.clouddpc.base.managedprovisioning.provisioning.ProvisioningService } U=0: not found

I've got the feeling my app is not correctly registering itself as a DPC app. Any help is highly appreciated!

@MichaelTeeuw
Copy link

Finally found it! For anyone running into the same issue; you need the following additional activities (next to your DeviceAdminReceiver):

        <activity
            android:name=".GetProvisioningModeActivity"
            android:exported="true"
            android:permission="android.permission.BIND_DEVICE_ADMIN">
            <intent-filter>
                <action android:name="android.app.action.GET_PROVISIONING_MODE" />
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
        </activity>

        <activity
            android:name=".PolicyComplianceActivity"
            android:exported="true"
            android:permission="android.permission.BIND_DEVICE_ADMIN">
            <intent-filter>
                <action android:name="android.app.action.ADMIN_POLICY_COMPLIANCE" />
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
        </activity>
class PolicyComplianceActivity : Activity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // Do any required setup
        val intent = Intent(this, MainActivity::class.java)
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
        startActivity(intent)
        finish()
    }
}
class GetProvisioningModeActivity : Activity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val result = Intent().apply {
            putExtra(
                DevicePolicyManager.EXTRA_PROVISIONING_MODE,
                DevicePolicyManager.PROVISIONING_MODE_FULLY_MANAGED_DEVICE
            )
        }

        setResult(Activity.RESULT_OK, result)
        finish()
    }
}

@Nickztar
Copy link

Hi! This has been super helpful. But I am struggling to read the android.app.extra.PROVISIONING_ADMIN_EXTRAS_BUNDLE, anyone have any examples or tips on how to get the values out of this? It seems that is supposed to be read from GetProvisioning or PolicyCompliance but am unable to. Any help would be really appricated

@saifikram969
Copy link

saifikram969 commented Dec 9, 2025

WhatsApp Image 2025-12-09 at 14 10 27_fbe34e5b

@robin-thoni @bashenk
Device Policy Controller (DPC) QR Provisioning Issue
I am developing a custom Device Policy Controller and attempting to enroll it through QR code provisioning. Below is a detailed description of my implementation and the issue I am facing.
Current DPC Implementation
My DPC application includes the following components and functionality:

Device admin receiver extending DeviceAdminReceiver class (MyDeviceAdminReceiver)
Policy management through DevicePolicyManager API
User interface controls for:

Camera enable/disable functionality
Device lock capability

Remote command execution via Firebase for camera control, device lock, and location tracking
All features function correctly when the app is installed manually and device owner mode is activated through ADB
Successful ADB Provisioning
The following ADB command successfully establishes device owner mode:
adb shell dpm set-device-owner com.example.testemmjc/.MyDeviceAdminReceiver

After executing this command, all policy enforcement features (camera disable/enable, device lock, location tracking) work as intended, confirming that the DPC implementation itself is functional.
Problem Statement
QR code provisioning consistently fails on a factory-reset Samsung device. The error message displayed is:
"Couldn't set up device. Contact your IT admin."
QR Code Configuration
The JSON configuration being used for QR provisioning is:

{
  "android.app.extra.PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME": "com.example.testemmjc/.MyDeviceAdminReceiver",
  "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION": "https://maharanidevi.com/dpc.apk",
  "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM": "8ldAbIR-Baxlqwv-yUShimRmGyZR8Mqj4vQbI1mFmKo"
}

Verification Steps Completed
I have confirmed the following:

APK is properly signed using release keystore
SHA-256 checksum is correctly calculated and encoded in base64url format
Package name in JSON matches the manifest declaration (com.example.testemmjc)
APK download URL is publicly accessible and serves the correct file
Device has been factory reset with no Google accounts configured
Manual installation combined with ADB device owner activation works without issues
The provisioning process fails during the initial QR scan phase, before the APK installation begins.
Request for Assistance
I am seeking guidance on potential issues related to:

Samsung-specific provisioning requirements or limitations
Differences between APK checksum and signature checksum verification
Server configuration or HTTP headers required for APK hosting
Additional manifest permissions or metadata required specifically for QR provisioning
Android version-specific restrictions (testing on Android 11 or later)
I have followed the official Android Enterprise provisioning documentation, but the QR provisioning method continues to fail while ADB provisioning succeeds with the same DPC application. Any insights into what might be preventing successful QR provisioning would be helpful.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment