Reputation: 3469
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
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.
Upvotes: 2