androidandroid-12device-policy-managerdevice-owner

Device owner QR stopped working on Android12 devices, getting Can't set up device message


For some reason, the QR code for installing our app as a device owner stopped working on Android12 devices (the same QR codeworks perfectly on previous Android versions).

The error message we are getting is:

Can't set up device
Contact your IT admin for help

This is the JSON of the QR code

{
  "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION": "https://someurlthatworkforsure",
  "android.app.extra.PROVISIONING_SKIP_ENCRYPTION": true,
  "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM": "validCheckSumThatWasTestedAndDoesWorkOnAndroidPriodTo12",
  "android.app.extra.PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME": "com.brand.name/com.brand.some.SomeClass"
}

I have done some googling but haven't found whats wrong with our QR code,

I have also tried setting PROVISIONING_SKIP_ENCRYPTION to false and removing it, the result is the same.

The CHECKSUM is valid, if I mess with it throws a different error...

Thanks in advance.


Solution

  • Well, I eventually solved it in the following way

    You must add two more activities that will handle the new flow In both activities, you will have to set results and finish the activity

    setResult(RESULT_OK, intent);
    finish();
    

    Note that I noticed that the new approach worked 100% on Android 12 (v31) but sometimes failed on Android 11 v30, so I made this solution conditional, its being enabled on Android 12+, by checking the following boolean provision_mode_compliance_enabled, which is stored in XML resource files

    ProvisioningModeActivity.java

    package com.my.pkg;
    
    import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE;
    
    import android.content.Intent;
    import android.os.Build;
    import android.os.Bundle;
    import android.os.PersistableBundle;
    
    import androidx.appcompat.app.AppCompatActivity;
    import com.my.pkg.R;
    
    import java.util.List;
    
    public class ProvisioningModeActivity extends AppCompatActivity {
    
        private String EXTRA_PROVISIONING_ALLOWED_PROVISIONING_MODES = "android.app.extra.PROVISIONING_ALLOWED_PROVISIONING_MODES";
        private int PROVISIONING_MODE_FULLY_MANAGED_DEVICE = 1;
        private int PROVISIONING_MODE_MANAGED_PROFILE = 2;
        private String EXTRA_PROVISIONING_MODE = "android.app.extra.PROVISIONING_MODE";
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_get_provisioning_mode);
    
            Intent intent = getIntent();
            int provisioningMode = PROVISIONING_MODE_FULLY_MANAGED_DEVICE;
            List<Integer> allowedProvisioningModes = intent.getIntegerArrayListExtra(EXTRA_PROVISIONING_ALLOWED_PROVISIONING_MODES);
    
            if (allowedProvisioningModes != null) {
                if (allowedProvisioningModes.contains(PROVISIONING_MODE_FULLY_MANAGED_DEVICE)) {
                    provisioningMode = PROVISIONING_MODE_FULLY_MANAGED_DEVICE;
                } else if (allowedProvisioningModes.contains(PROVISIONING_MODE_MANAGED_PROFILE)) {
                    provisioningMode = PROVISIONING_MODE_MANAGED_PROFILE;
                }
            }
            //grab the extras (might contain some needed values from QR code) and pass to AdminPolicyComplianceActivity
            PersistableBundle extras = intent.getParcelableExtra(EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE);
            Intent resultIntent = getIntent();
    
            if (extras != null) {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                    resultIntent.putExtra(EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE, extras);
                }
            }
            resultIntent.putExtra(EXTRA_PROVISIONING_MODE, provisioningMode);
    
            setResult(RESULT_OK, resultIntent);
            finish();
        }
    }
    

    and

    AdminPolicyComplianceActivity.java

    package com.my.pkg;
    
    
    import android.content.Intent;
    import android.os.Bundle;
    import com.my.pkg.R;
    import androidx.appcompat.app.AppCompatActivity;
    
    
    
    public class AdminPolicyComplianceActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_policy_compliance);
    
            Intent intent = getIntent();
    
            setResult(RESULT_OK, intent);
            finish();
        }
    }
    
    

    Manifest entries: notice that these activities are enabled conditionaly (continue reading till the end of the answer)

    <activity
        android:name="com.communitake.android.lib.deviceadmin.AdminPolicyComplianceActivity"
        android:exported="true"
        android:enabled="@bool/provision_mode_compliance_enabled"
        android:screenOrientation="portrait"
        android:theme="@style/Theme.AppCompat"
        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>
    
    <activity
        android:name="com.communitake.android.lib.deviceadmin.ProvisioningModeActivity"
        android:screenOrientation="portrait"
        android:exported="true"
        android:enabled="@bool/provision_mode_compliance_enabled"
        android:theme="@style/Theme.AppCompat"
        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_get_provisioning_mode.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".GetProvisioningModeActivity">
    
        <Button
            android:id="@+id/get_provisioning_data"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Get Provisioning Data"/>
    
    </LinearLayout>
    

    activity_policy_compliance.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".PolicyComplianceActivity">
    
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Policy Compliance Screen"/>
    
    </LinearLayout>
    

    Last thing is adding bools.xml to the values and values-v31 folders, with the following content, note that the new flow better be enabled on Android 12 and higher

    values folder

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <bool name="provision_mode_compliance_enabled">false</bool>
    </resources>
    

    values-v31 folder

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <bool name="provision_mode_compliance_enabled">true</bool>
    </resources>