Nathan F.
Nathan F.

Reputation: 3469

Programatically setting Always-On VPN, "Admin does not own the profile"

I'm trying to figure out how to configure my VPN application to toggle the Always-On flag from within the application with a toggle.

I'm aware of

DevicePolicyManager#setAlwaysOnVpnPackage

However, it's not very clear how to use this function. I have tried the following:

Admin.java

public class Admin extends DeviceAdminReceiver {
    @Override
    public void onEnabled(@NonNull Context context, @NonNull Intent intent) {
        super.onEnabled(context, intent);
    }
}

AdvancedSettings.java

public class AdvancedSettings extends AppCompatActivity 
        implements View.OnClickListener {

    private ComponentName componentName;
    private DevicePolicyManager devicePolicyManager;
    private boolean alwaysOnConfiguredValue;

    private static final int ALWAYS_ON_REQUEST_CODE = 11;

    @Override
    public void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        setContentView(R.layout.settings_advanced);

        Button button = findViewById(R.id.toggleAlwaysOnButton);
        button.setOnClickListener(this);

        devicePolicyManager = (DevicePolicyManager) this
                .getSystemService(Context.DEVICE_POLICY_SERVICE);
        componentName = new ComponentName(
                this.getApplicationContext(), Admin.class);
    }

    @Override
    public void onClick(View v) {
        if (v.getId() == R.id.toggleAlwaysOnButton) {
            this.setAlwaysOn(true);
        }
    }

    /**
     * Handle the Activity Result.
     */
    @Override
    protected void onActivityResult(
        int requestCode, int resultCode, @Nullable Intent data
    ) {
        if (requestCode == ALWAYS_ON_REQUEST_CODE) {
            if (resultCode == Activity.RESULT_OK) {
                finalizeAlwaysOnToggle();
            } else {
                Log.w(
                    "Invalid result code " + resultCode
                );
            }
        }

        super.onActivityResult(requestCode, resultCode, data);
    }

    /**
     * Start the process of enabling "Always On" for the VPN.
     *
     * @param boolean value
     */
    private void setAlwaysOn(boolean value) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            alwaysOnConfiguredValue = value;

            if (devicePolicyManager.isAdminActive(componentName)) {
                finalizeAlwaysOnToggle();
                return;
            }

            requestAdminAccess();
        } else {
            Toas.makeText(this, "Not supported", Toast.LENGTH_LONG).show();
        }
    }

    /**
     * Request Admin Access for this application 
     * if it has not already been done.
     */
    private void requestAdminAccess() {
        Intent intent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN);
        intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, componentName);
        intent.putExtra(
            DevicePolicyManager.EXTRA_ADD_EXPLANATION,
            "This is required to modify the Always-On Feature from within the Test Application."
        );
        this.startActivityForResult(intent, ALWAYS_ON_REQUEST_CODE);
    }

    /**
     * Finalize setting the always on toggle after the Admin Access 
     * has been granted for this application.
     */
    private void finalizeAlwaysOnToggle() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            try {
                if (devicePolicyManager.isAdminActive(componentName)) {
                    devicePolicyManager.setAlwaysOnVpnPackage(
                        componentName, (alwaysOnConfiguredValue) ? "com.myapp" : null, true
                    );
                } else {
                    Log.e(
                        "Device Policy Manager Admin is not yet active while " + 
                        "trying to finalize changes to AlwaysOnToggle."
                    );
                }
            } catch (PackageManager.NameNotFoundException e) {
                Log.e("Unable to set always on vpn due to NameNotFound Exception.", e);
            }
        }
    }
}

It processes the request for adding the Device Admin just fine, however after that has completed, when it runs finalizeAlwaysOnToggle(), during the call to devicePolicyManager.setAlwaysOnVpnPackage I receive the following error:

E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.myapp, PID: 30778
    java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=11, result=-1, data=null} to activity {com.myapp/com.myapp.ui.settings.AdvancedSettings}: java.lang.SecurityException: Admin ComponentInfo{com.myapp/com.myapp.provider.Admin} does not own the profile

Upvotes: 3

Views: 2109

Answers (1)

BBB
BBB

Reputation: 342

You have to differentiate between "Device Admin", "Device Owner" and "Profile Owner". As it is stated in the docs you need to be one of the latter twos to be able to call setAlwaysOnVpnPackage:

Called by a device or profile owner to configure an always-on VPN connection through a specific application for the current user. This connection is automatically granted and persisted after a reboot.

(https://developer.android.com/reference/android/app/admin/DevicePolicyManager.html#setAlwaysOnVpnPackage(android.content.ComponentName,%2520java.lang.String,%2520boolean))

Upvotes: 2

Related Questions