From 986c9461ac1138e10688066f5afd4ce04e37ffb4 Mon Sep 17 00:00:00 2001
From: Charitha Goonetilleke <charitha@entgra.io>
Date: Fri, 26 Apr 2019 02:44:43 +0000
Subject: [PATCH 1/3] Add capability to store device type specific platform
 configurations for any device type

---
 .../android/mocks/DeviceManagementProviderServiceMock.java   | 3 ++-
 .../configuration.hbs                                        | 5 +++++
 .../public/js/platform-configuration.js                      | 2 +-
 .../android/mocks/DeviceManagementProviderServiceMock.java   | 3 ++-
 .../configuration.hbs                                        | 5 +++++
 .../public/js/platform-configuration.js                      | 2 +-
 pom.xml                                                      | 2 +-
 7 files changed, 17 insertions(+), 5 deletions(-)

diff --git a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/test/java/org/wso2/carbon/mdm/services/android/mocks/DeviceManagementProviderServiceMock.java b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/test/java/org/wso2/carbon/mdm/services/android/mocks/DeviceManagementProviderServiceMock.java
index 38f2a774a..9ed1b6f0e 100644
--- a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/test/java/org/wso2/carbon/mdm/services/android/mocks/DeviceManagementProviderServiceMock.java
+++ b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/test/java/org/wso2/carbon/mdm/services/android/mocks/DeviceManagementProviderServiceMock.java
@@ -21,6 +21,7 @@ package org.wso2.carbon.mdm.services.android.mocks;
 import org.wso2.carbon.device.mgt.common.Device;
 import org.wso2.carbon.device.mgt.common.DeviceIdentifier;
 import org.wso2.carbon.device.mgt.common.DeviceManagementException;
+import org.wso2.carbon.device.mgt.common.DeviceTypeNotFoundException;
 import org.wso2.carbon.device.mgt.common.EnrolmentInfo;
 import org.wso2.carbon.device.mgt.common.FeatureManager;
 import org.wso2.carbon.device.mgt.common.InvalidDeviceException;
@@ -289,7 +290,7 @@ public class DeviceManagementProviderServiceMock implements DeviceManagementProv
     }
 
     @Override
-    public FeatureManager getFeatureManager(String s) throws DeviceManagementException {
+    public FeatureManager getFeatureManager(String s) throws DeviceTypeNotFoundException {
         return null;
     }
 
diff --git a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.platform.configuration/configuration.hbs b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.platform.configuration/configuration.hbs
index ec06c25f6..0b4931295 100644
--- a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.platform.configuration/configuration.hbs
+++ b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.platform.configuration/configuration.hbs
@@ -17,6 +17,11 @@
 }}
 <!-- android -->
 <div class="panel panel-default">
+    <div class="panel-heading" role="tab">
+        <h2 class="sub-title panel-title">
+            Platform Configuration - Windows
+        </h2>
+    </div>
     <div id="android-config-body" class="panel-collapse panel-body" role="tabpanel">
         <div id="android-config-error-msg" class="alert alert-danger hidden" role="alert">
             <i class="icon fw fw-error"></i><span></span>
diff --git a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.platform.configuration/public/js/platform-configuration.js b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.platform.configuration/public/js/platform-configuration.js
index e204ef96a..8e26eb51c 100644
--- a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.platform.configuration/public/js/platform-configuration.js
+++ b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.platform.configuration/public/js/platform-configuration.js
@@ -92,7 +92,7 @@ var kioskConfigs = {
 $(document).ready(function () {
     $("#fcm-inputs").hide();
     tinymce.init({
-        selector: "textarea",
+        selector: "#android-eula",
         height:500,
         theme: "modern",
         plugins: [
diff --git a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.v09.api/src/test/java/org/wso2/carbon/mdm/services/android/mocks/DeviceManagementProviderServiceMock.java b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.v09.api/src/test/java/org/wso2/carbon/mdm/services/android/mocks/DeviceManagementProviderServiceMock.java
index 38f2a774a..9ed1b6f0e 100644
--- a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.v09.api/src/test/java/org/wso2/carbon/mdm/services/android/mocks/DeviceManagementProviderServiceMock.java
+++ b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.v09.api/src/test/java/org/wso2/carbon/mdm/services/android/mocks/DeviceManagementProviderServiceMock.java
@@ -21,6 +21,7 @@ package org.wso2.carbon.mdm.services.android.mocks;
 import org.wso2.carbon.device.mgt.common.Device;
 import org.wso2.carbon.device.mgt.common.DeviceIdentifier;
 import org.wso2.carbon.device.mgt.common.DeviceManagementException;
+import org.wso2.carbon.device.mgt.common.DeviceTypeNotFoundException;
 import org.wso2.carbon.device.mgt.common.EnrolmentInfo;
 import org.wso2.carbon.device.mgt.common.FeatureManager;
 import org.wso2.carbon.device.mgt.common.InvalidDeviceException;
@@ -289,7 +290,7 @@ public class DeviceManagementProviderServiceMock implements DeviceManagementProv
     }
 
     @Override
-    public FeatureManager getFeatureManager(String s) throws DeviceManagementException {
+    public FeatureManager getFeatureManager(String s) throws DeviceTypeNotFoundException {
         return null;
     }
 
diff --git a/components/mobile-plugins/windows-plugin/org.wso2.carbon.device.mgt.mobile.windows.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.windows.platform.configuration/configuration.hbs b/components/mobile-plugins/windows-plugin/org.wso2.carbon.device.mgt.mobile.windows.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.windows.platform.configuration/configuration.hbs
index fe54ae367..07fe26ebb 100644
--- a/components/mobile-plugins/windows-plugin/org.wso2.carbon.device.mgt.mobile.windows.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.windows.platform.configuration/configuration.hbs
+++ b/components/mobile-plugins/windows-plugin/org.wso2.carbon.device.mgt.mobile.windows.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.windows.platform.configuration/configuration.hbs
@@ -17,6 +17,11 @@
 }}
 <!-- windows -->
 <div class="panel panel-default">
+    <div class="panel-heading" role="tab">
+        <h2 class="sub-title panel-title">
+            Platform Configuration - Windows
+        </h2>
+    </div>
     <div id="windows-config-body" class="panel-collapse panel-body" role="tabpanel">
         <div id="windows-config-error-msg" class="alert alert-danger hidden" role="alert">
             <i class="icon fw fw-error"></i><span></span>
diff --git a/components/mobile-plugins/windows-plugin/org.wso2.carbon.device.mgt.mobile.windows.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.windows.platform.configuration/public/js/platform-configuration.js b/components/mobile-plugins/windows-plugin/org.wso2.carbon.device.mgt.mobile.windows.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.windows.platform.configuration/public/js/platform-configuration.js
index 89f825439..32b42eac4 100644
--- a/components/mobile-plugins/windows-plugin/org.wso2.carbon.device.mgt.mobile.windows.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.windows.platform.configuration/public/js/platform-configuration.js
+++ b/components/mobile-plugins/windows-plugin/org.wso2.carbon.device.mgt.mobile.windows.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.windows.platform.configuration/public/js/platform-configuration.js
@@ -72,7 +72,7 @@ function promptErrorPolicyPlatform(errorMsg) {
 
 $(document).ready(function () {
     tinymce.init({
-        selector: "textarea",
+        selector: "#windows-eula",
         height:500,
         theme: "modern",
         plugins: [
diff --git a/pom.xml b/pom.xml
index b8f16407c..5e6e11e8d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1230,7 +1230,7 @@
         <javax.ws.rs.version>1.1.1</javax.ws.rs.version>
 
         <!-- Carbon Device Management -->
-        <carbon.devicemgt.version>3.2.4</carbon.devicemgt.version>
+        <carbon.devicemgt.version>3.2.5-SNAPSHOT</carbon.devicemgt.version>
         <carbon.devicemgt.version.range>[3.1.0, 4.0.0)</carbon.devicemgt.version.range>
 
         <!-- Carbon App Management -->

From b6c41638787bd1e8cdb92d355223496b6e158d52 Mon Sep 17 00:00:00 2001
From: sandaru daminda <sandarudr@gmail.com>
Date: Tue, 14 May 2019 03:21:19 +0000
Subject: [PATCH 2/3] Add remote configuration feature to android device mgt
 plugin

Application list response sends "SAME_APPLICATION_LIST" flag if app list is not changed. This is not being handled in the server side properly.
---
 .../android/bean/ApplicationRestriction.java  | 54 +++++++++++++++
 .../ApplicationRestrictionBeanWrapper.java    | 55 +++++++++++++++
 .../DeviceManagementAdminService.java         | 68 +++++++++++++++++++
 .../DeviceManagementAdminServiceImpl.java     | 56 ++++++++++++---
 .../android/util/AndroidAPIUtils.java         | 35 ++++++++++
 .../android/util/AndroidConstants.java        |  1 +
 .../public/js/operation-bar.js                | 12 +++-
 .../private/config.json                       | 21 +++++-
 .../main/resources/devicetypes/android.xml    |  7 ++
 9 files changed, 299 insertions(+), 10 deletions(-)
 create mode 100644 components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/bean/ApplicationRestriction.java
 create mode 100644 components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/bean/wrapper/ApplicationRestrictionBeanWrapper.java

diff --git a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/bean/ApplicationRestriction.java b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/bean/ApplicationRestriction.java
new file mode 100644
index 000000000..b3773376d
--- /dev/null
+++ b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/bean/ApplicationRestriction.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2019, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved.
+ *
+ * WSO2 Inc. licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.wso2.carbon.mdm.services.android.bean;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.io.Serializable;
+
+/**
+ * This class represents the information of sending application config operation.
+ */
+@ApiModel(value = "ApplicationRestriction",
+        description = "Details related to application config passed to device.")
+public class ApplicationRestriction extends AndroidOperation implements Serializable {
+    private static final long serialVersionUID = 1995401458L;
+
+    @ApiModelProperty(name = "appIdentifier", value = "The application identifier to be sent.", required = true)
+    private String appIdentifier;
+
+    @ApiModelProperty(name = "restrictionPayload", value = "The restriction payload to be sent.", required = true)
+    private String restrictionPayload;
+
+    public String getAppIdentifier() {
+        return appIdentifier;
+    }
+
+    public void setAppIdentifier(String appIdentifier) {
+        this.appIdentifier = appIdentifier;
+    }
+
+    public String getRestrictionPayload() {
+        return restrictionPayload;
+    }
+
+    public void setRestrictionPayload(String restrictionPayload) {
+        this.restrictionPayload = restrictionPayload;
+    }
+}
\ No newline at end of file
diff --git a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/bean/wrapper/ApplicationRestrictionBeanWrapper.java b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/bean/wrapper/ApplicationRestrictionBeanWrapper.java
new file mode 100644
index 000000000..147a577da
--- /dev/null
+++ b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/bean/wrapper/ApplicationRestrictionBeanWrapper.java
@@ -0,0 +1,55 @@
+
+/*
+ * Copyright (c) 2019, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved.
+ *
+ * WSO2 Inc. licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.wso2.carbon.mdm.services.android.bean.wrapper;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import org.wso2.carbon.mdm.services.android.bean.ApplicationRestriction;
+
+import java.util.List;
+
+/**
+ * This class is used to wrap the Notification bean with devices.
+ */
+@ApiModel(value = "ApplicationRestrictionBeanWrapper",
+        description = "Mapping between application restriction operation and device list to be applied.")
+public class ApplicationRestrictionBeanWrapper {
+
+    @ApiModelProperty(name = "deviceIDs", value = "List of device Ids", required = true)
+    private List<String> deviceIDs;
+    @ApiModelProperty(name = "operation", value = "The information of application restriction operation",
+            required = true)
+    private ApplicationRestriction operation;
+
+    public List<String> getDeviceIDs() {
+        return deviceIDs;
+    }
+
+    public void setDeviceIDs(List<String> deviceIDs) {
+        this.deviceIDs = deviceIDs;
+    }
+
+    public ApplicationRestriction getOperation() {
+        return operation;
+    }
+
+    public void setOperation(ApplicationRestriction operation) {
+        this.operation = operation;
+    }
+}
\ No newline at end of file
diff --git a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/DeviceManagementAdminService.java b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/DeviceManagementAdminService.java
index 998503b13..beebf032c 100644
--- a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/DeviceManagementAdminService.java
+++ b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/DeviceManagementAdminService.java
@@ -245,6 +245,12 @@ import java.util.List;
                         description = "Transferring a file to android devices",
                         key = "perm:android:file-transfer",
                         permissions = {"/device-mgt/devices/owning-device/operations/android/file-transfer"}
+                ),
+                @Scope(
+                        name = "Send app restrictions",
+                        description = "Send app restrictions to an application in the device",
+                        key = "perm:android:send-app-restrictions",
+                        permissions = {"/device-mgt/devices/owning-device/operations/android/send-app-conf"}
                 )
         }
 )
@@ -1856,6 +1862,68 @@ public interface DeviceManagementAdminService {
                     value = "The properties to set the web clip.",
                     required = true)
                     WebClipBeanWrapper webClipBeanWrapper);
+    @POST
+    @Path("/send-app-conf")
+    @ApiOperation(
+            consumes = MediaType.APPLICATION_JSON,
+            httpMethod = "POST",
+            value = "Sending an app restrictions to Android Devices",
+            notes = "Send application restrictions to Android devices.",
+            response = Activity.class,
+            tags = "Android Device Management Administrative Service",
+            extensions = {
+                    @Extension(properties = {
+                            @ExtensionProperty(name = AndroidConstants.SCOPE,
+                                    value = "perm:android:send-app-restrictions")
+                    })
+            }
+    )
+    @ApiResponses(value = {
+            @ApiResponse(
+                    code = 201,
+                    message = "Created. \n Successfully sent the application configuration.",
+                    response = Activity.class,
+                    responseHeaders = {
+                            @ResponseHeader(
+                                    name = "Content-Location",
+                                    description = "URL of the activity instance that refers to the scheduled operation."),
+                            @ResponseHeader(
+                                    name = "Content-Type",
+                                    description = "Content type of the body"),
+                            @ResponseHeader(
+                                    name = "ETag",
+                                    description = "Entity Tag of the response resource.\n" +
+                                            "Used by caches, or in conditional requests."),
+                            @ResponseHeader(
+                                    name = "Last-Modified",
+                                    description = "Date and time the resource was last modified.\n" +
+                                            "Used by caches, or in conditional requests.")}),
+            @ApiResponse(
+                    code = 303,
+                    message = "See Other. \n The source can be retrieved from the URL specified in the location header.",
+                    responseHeaders = {
+                            @ResponseHeader(
+                                    name = "Content-Location",
+                                    description = "The Source URL of the document.")}),
+            @ApiResponse(
+                    code = 400,
+                    message = "Bad Request. \n Invalid request or validation error."),
+            @ApiResponse(
+                    code = 415,
+                    message = "Unsupported media type. \n The format of the requested entity was not supported."),
+            @ApiResponse(
+                    code = 500,
+                    message = "Internal Server Error. \n " +
+                            "Server error occurred while adding a new send application config operation.")
+    })
+    Response sendApplicationConfiguration(
+            @ApiParam(
+                    name = "notification",
+                    value = "The properties required to send application restrictions. Provide the restriction you " +
+                            "wish to send and the ID of the Android device. Multiple device IDs can be added by using" +
+                            " comma separated values.",
+                    required = true)
+                    ApplicationRestrictionBeanWrapper applicationRestrictionBeanWrapper);
 
     @POST
     @Path("/configure-global-proxy")
diff --git a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/impl/DeviceManagementAdminServiceImpl.java b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/impl/DeviceManagementAdminServiceImpl.java
index 0941a66a9..efddc8764 100644
--- a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/impl/DeviceManagementAdminServiceImpl.java
+++ b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/services/impl/DeviceManagementAdminServiceImpl.java
@@ -37,7 +37,7 @@ package org.wso2.carbon.mdm.services.android.services.impl;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
-import org.json.JSONException;
+import org.wso2.carbon.device.mgt.common.DeviceManagementException;
 import org.wso2.carbon.device.mgt.common.InvalidDeviceException;
 import org.wso2.carbon.device.mgt.common.operation.mgt.Activity;
 import org.wso2.carbon.device.mgt.common.operation.mgt.Operation;
@@ -45,6 +45,7 @@ import org.wso2.carbon.device.mgt.common.operation.mgt.OperationManagementExcept
 import org.wso2.carbon.device.mgt.core.operation.mgt.CommandOperation;
 import org.wso2.carbon.device.mgt.core.operation.mgt.ProfileOperation;
 import org.wso2.carbon.mdm.services.android.bean.ApplicationInstallation;
+import org.wso2.carbon.mdm.services.android.bean.ApplicationRestriction;
 import org.wso2.carbon.mdm.services.android.bean.ApplicationUninstallation;
 import org.wso2.carbon.mdm.services.android.bean.ApplicationUpdate;
 import org.wso2.carbon.mdm.services.android.bean.BlacklistApplications;
@@ -63,6 +64,7 @@ import org.wso2.carbon.mdm.services.android.bean.WebClip;
 import org.wso2.carbon.mdm.services.android.bean.Wifi;
 import org.wso2.carbon.mdm.services.android.bean.WipeData;
 import org.wso2.carbon.mdm.services.android.bean.wrapper.ApplicationInstallationBeanWrapper;
+import org.wso2.carbon.mdm.services.android.bean.wrapper.ApplicationRestrictionBeanWrapper;
 import org.wso2.carbon.mdm.services.android.bean.wrapper.ApplicationUninstallationBeanWrapper;
 import org.wso2.carbon.mdm.services.android.bean.wrapper.ApplicationUpdateBeanWrapper;
 import org.wso2.carbon.mdm.services.android.bean.wrapper.BlacklistApplicationsBeanWrapper;
@@ -82,6 +84,7 @@ import org.wso2.carbon.mdm.services.android.bean.wrapper.WipeDataBeanWrapper;
 import org.wso2.carbon.mdm.services.android.exception.BadRequestException;
 import org.wso2.carbon.mdm.services.android.exception.UnexpectedServerErrorException;
 import org.wso2.carbon.mdm.services.android.services.DeviceManagementAdminService;
+import org.wso2.carbon.mdm.services.android.util.AndroidAPIUtils;
 import org.wso2.carbon.mdm.services.android.util.AndroidConstants;
 import org.wso2.carbon.mdm.services.android.util.AndroidDeviceUtils;
 
@@ -528,7 +531,8 @@ public class DeviceManagementAdminServiceImpl implements DeviceManagementAdminSe
     @POST
     @Path("/install-application")
     @Override
-    public Response installApplication(ApplicationInstallationBeanWrapper applicationInstallationBeanWrapper) {
+    public Response installApplication(
+            ApplicationInstallationBeanWrapper applicationInstallationBeanWrapper) {
         if (log.isDebugEnabled()) {
             log.debug("Invoking 'InstallApplication' operation");
         }
@@ -554,11 +558,6 @@ public class DeviceManagementAdminServiceImpl implements DeviceManagementAdminSe
             Activity activity = AndroidDeviceUtils
                     .getOperationResponse(applicationInstallationBeanWrapper.getDeviceIDs(), operation);
             return Response.status(Response.Status.CREATED).entity(activity).build();
-        } catch (JSONException e) {
-            String errorMessage = "Invalid payload for the operation.";
-            log.error(errorMessage);
-            throw new BadRequestException(
-                    new ErrorResponse.ErrorResponseBuilder().setCode(400l).setMessage(errorMessage).build());
         } catch (InvalidDeviceException e) {
             String errorMessage = "Invalid Device Identifiers found.";
             log.error(errorMessage, e);
@@ -616,7 +615,8 @@ public class DeviceManagementAdminServiceImpl implements DeviceManagementAdminSe
     @POST
     @Path("/uninstall-application")
     @Override
-    public Response uninstallApplication(ApplicationUninstallationBeanWrapper applicationUninstallationBeanWrapper) {
+    public Response uninstallApplication(
+            ApplicationUninstallationBeanWrapper applicationUninstallationBeanWrapper) {
         if (log.isDebugEnabled()) {
             log.debug("Invoking 'UninstallApplication' operation");
         }
@@ -1022,6 +1022,46 @@ public class DeviceManagementAdminServiceImpl implements DeviceManagementAdminSe
                     new ErrorResponse.ErrorResponseBuilder().setCode(500L).setMessage(errorMessage).build());
         }
     }
+    @POST
+    @Path("/send-app-conf")
+    @Override
+    public Response sendApplicationConfiguration(
+            ApplicationRestrictionBeanWrapper applicationRestrictionBeanWrapper) {
+        if (log.isDebugEnabled()) {
+            log.debug("Invoking 'send application configuration' operation");
+        }
+
+        try {
+            if (applicationRestrictionBeanWrapper == null || applicationRestrictionBeanWrapper.getOperation() == null) {
+                String errorMessage = "The payload of the application configuration operation is incorrect";
+                log.error(errorMessage);
+                throw new BadRequestException(
+                        new ErrorResponse.ErrorResponseBuilder().setCode(400l).setMessage(errorMessage).build());
+            }
+            ApplicationRestriction applicationRestriction = applicationRestrictionBeanWrapper.getOperation();
+            ProfileOperation operation = new ProfileOperation();
+            operation.setCode(AndroidConstants.OperationCodes.REMOTE_APP_CONFIG);
+            operation.setType(Operation.Type.PROFILE);
+            operation.setPayLoad(applicationRestriction.toJSON());
+            return (Response) AndroidAPIUtils.getOperationResponse(applicationRestrictionBeanWrapper.getDeviceIDs(),
+                    operation);
+        } catch (InvalidDeviceException e) {
+            String errorMessage = "Invalid Device Identifiers found.";
+            log.error(errorMessage, e);
+            throw new BadRequestException(
+                    new ErrorResponse.ErrorResponseBuilder().setCode(400l).setMessage(errorMessage).build());
+        } catch (OperationManagementException e) {
+            String errorMessage = "Issue in retrieving operation management service instance";
+            log.error(errorMessage, e);
+            throw new UnexpectedServerErrorException(
+                    new ErrorResponse.ErrorResponseBuilder().setCode(500l).setMessage(errorMessage).build());
+        } catch (DeviceManagementException e) {
+            String errorMessage = "Issue in retrieving device management service instance";
+            log.error(errorMessage, e);
+            throw new UnexpectedServerErrorException(
+                    new ErrorResponse.ErrorResponseBuilder().setCode(500l).setMessage(errorMessage).build());
+        }
+    }
 
     private static void validateApplicationUrl(String apkUrl) {
         try {
diff --git a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/util/AndroidAPIUtils.java b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/util/AndroidAPIUtils.java
index f8c37fe5f..318c23d83 100644
--- a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/util/AndroidAPIUtils.java
+++ b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/util/AndroidAPIUtils.java
@@ -23,12 +23,26 @@ import org.apache.commons.logging.LogFactory;
 import org.wso2.carbon.analytics.api.AnalyticsDataAPI;
 import org.wso2.carbon.context.PrivilegedCarbonContext;
 import org.wso2.carbon.device.mgt.analytics.data.publisher.service.EventsPublisherService;
+import org.wso2.carbon.device.mgt.common.DeviceIdentifier;
+import org.wso2.carbon.device.mgt.common.DeviceManagementConstants;
+import org.wso2.carbon.device.mgt.common.DeviceManagementException;
+import org.wso2.carbon.device.mgt.common.InvalidDeviceException;
 import org.wso2.carbon.device.mgt.common.notification.mgt.NotificationManagementService;
+import org.wso2.carbon.device.mgt.common.operation.mgt.Activity;
+import org.wso2.carbon.device.mgt.common.operation.mgt.Operation;
+import org.wso2.carbon.device.mgt.common.operation.mgt.OperationManagementException;
 import org.wso2.carbon.device.mgt.core.app.mgt.ApplicationManagementProviderService;
 import org.wso2.carbon.device.mgt.core.device.details.mgt.DeviceInformationManager;
 import org.wso2.carbon.device.mgt.core.service.DeviceManagementProviderService;
+import org.wso2.carbon.mdm.services.android.bean.ErrorResponse;
+import org.wso2.carbon.mdm.services.android.exception.BadRequestException;
 import org.wso2.carbon.policy.mgt.core.PolicyManagerService;
 
+
+import javax.ws.rs.core.Response;
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * AndroidAPIUtil class provides utility functions used by Android REST-API classes.
  */
@@ -135,4 +149,25 @@ public class AndroidAPIUtils {
         return analyticsDataAPI;
     }
 
+    public static Response getOperationResponse(List<String> deviceIDs, Operation operation)
+            throws DeviceManagementException, OperationManagementException, InvalidDeviceException {
+        if (deviceIDs == null || deviceIDs.size() == 0) {
+            String errorMessage = "Device identifier list is empty";
+            log.error(errorMessage);
+            throw new BadRequestException(
+                    new ErrorResponse.ErrorResponseBuilder().setCode(400l).setMessage(errorMessage).build());
+        }
+        DeviceIdentifier deviceIdentifier;
+        List<DeviceIdentifier> deviceIdentifiers = new ArrayList<>();
+        for (String deviceId : deviceIDs) {
+            deviceIdentifier = new DeviceIdentifier();
+            deviceIdentifier.setId(deviceId);
+            deviceIdentifier.setType(AndroidConstants.DEVICE_TYPE_ANDROID);
+            deviceIdentifiers.add(deviceIdentifier);
+        }
+        Activity activity = getDeviceManagementService().addOperation(
+                DeviceManagementConstants.MobileDeviceTypes.MOBILE_DEVICE_TYPE_ANDROID, operation, deviceIdentifiers);
+        return Response.status(Response.Status.CREATED).entity(activity).build();
+    }
+
 }
diff --git a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/util/AndroidConstants.java b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/util/AndroidConstants.java
index 96842c956..9db77c7f4 100644
--- a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/util/AndroidConstants.java
+++ b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.api/src/main/java/org/wso2/carbon/mdm/services/android/util/AndroidConstants.java
@@ -140,6 +140,7 @@ public final class AndroidConstants {
         public static final String WORK_PROFILE = "WORK_PROFILE";
         public static final String NOTIFIER_FREQUENCY = "NOTIFIER_FREQUENCY";
         public static final String GLOBAL_PROXY = "SET_GLOBAL_PROXY";
+        public static final String REMOTE_APP_CONFIG = "REMOTE_APP_CONFIG";
     }
 
     public final class StatusCodes {
diff --git a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.operation-bar/public/js/operation-bar.js b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.operation-bar/public/js/operation-bar.js
index 854a2eaee..c6e811fae 100644
--- a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.operation-bar/public/js/operation-bar.js
+++ b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.operation-bar/public/js/operation-bar.js
@@ -491,6 +491,15 @@ var generatePayload = function (operationCode, operationData, deviceList) {
                 }
             };
             break;
+        case androidOperationConstants["APP_RESTRICTION_OPERATION_CODE"]:
+            operationType = operationTypeConstants["PROFILE"];
+            payload = {
+                 "operation": {
+                 "appIdentifier": operationData["app-id"],
+                 "restrictionPayload": operationData["payload"]
+                 }
+            };
+            break;
         default:
             // If the operation is neither of above, it is a command operation
             operationType = operationTypeConstants["COMMAND"];
@@ -554,5 +563,6 @@ var androidOperationConstants = {
     "APPLICATION_OPERATION_CODE": "APP-RESTRICTION",
     "SYSTEM_UPDATE_POLICY_CODE": "SYSTEM_UPDATE_POLICY",
     "KIOSK_APPS_CODE": "KIOSK_APPS",
-    "FILE_TRANSFER": "FILE_TRANSFER"
+    "FILE_TRANSFER": "FILE_TRANSFER",
+    "APP_RESTRICTION_OPERATION_CODE": "REMOTE_APP_CONFIG"
 };
diff --git a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.type-view/private/config.json b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.type-view/private/config.json
index 1f8a82010..82394b258 100644
--- a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.type-view/private/config.json
+++ b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.type-view/private/config.json
@@ -36,7 +36,8 @@
             "perm:android:unlock-devices",
             "perm:android:control-camera",
             "perm:android:reboot",
-            "perm:android:logcat"
+            "perm:android:logcat",
+            "perm:android:send-app-restrictions"
         ],
         "features": {
             "DEVICE_RING": {
@@ -214,6 +215,24 @@
                     }
                 ],
                 "permission": "/device-mgt/devices/owning-device/operations/android/wipe"
+            },
+            "REMOTE_APP_CONFIG": {
+                "icon": "fw-lock",
+                "formParams": [
+                    {
+                        "type": "text",
+                        "id": "app-id",
+                        "optional": false,
+                        "label": "Application identifier"
+                    },
+                    {
+                        "type": "text",
+                        "id": "payload",
+                        "optional": false,
+                        "label": "Application restriction payload"
+                    }
+                ],
+                "permission": "/device-mgt/devices/owning-device/operations/android/send-app-conf"
             }
         }
     }
diff --git a/features/mobile-plugins-feature/android-plugin-feature/org.wso2.carbon.device.mgt.mobile.android.feature/src/main/resources/devicetypes/android.xml b/features/mobile-plugins-feature/android-plugin-feature/org.wso2.carbon.device.mgt.mobile.android.feature/src/main/resources/devicetypes/android.xml
index 30fc71bd6..27e8e19a2 100644
--- a/features/mobile-plugins-feature/android-plugin-feature/org.wso2.carbon.device.mgt.mobile.android.feature/src/main/resources/devicetypes/android.xml
+++ b/features/mobile-plugins-feature/android-plugin-feature/org.wso2.carbon.device.mgt.mobile.android.feature/src/main/resources/devicetypes/android.xml
@@ -347,6 +347,13 @@
             <Name>Unlock the device</Name>
             <Description>Unlock the device</Description>
         </Feature>
+        <Feature code="REMOTE_APP_CONFIG">
+            <Name>Send app restriction</Name>
+            <Description>Send remote configurations to app</Description>
+            <Operation context="/api/device-mgt/android/v1.0/admin/devices/send-app-conf" method="POST"
+                       type="application/json">
+            </Operation>
+        </Feature>
     </Features>
     <TaskConfiguration>
         <Enable>true</Enable>

From 019502c58460468ab403cbf7eb44f9883f353d43 Mon Sep 17 00:00:00 2001
From: Milan Perera <milanharindu.ucsc@gmail.com>
Date: Fri, 17 May 2019 11:33:40 +0530
Subject: [PATCH 3/3] Add always on VPN policy

---
 .../public/js/operation-bar.js                |  3 +-
 .../public/js/operation-mod.js                |  8 ++-
 .../public/js/android-policy-edit.js          | 38 +++++++++---
 .../public/templates/android-policy-edit.hbs  | 56 +++++++++++++++++
 .../public/templates/android-policy-view.hbs  | 58 ++++++++++++++++-
 .../public/js/android-policy-operations.js    | 36 ++++++++---
 .../templates/android-policy-operations.hbs   | 62 ++++++++++++++++++-
 7 files changed, 237 insertions(+), 24 deletions(-)

diff --git a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.operation-bar/public/js/operation-bar.js b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.operation-bar/public/js/operation-bar.js
index c6e811fae..56fc63621 100644
--- a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.operation-bar/public/js/operation-bar.js
+++ b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.operation-bar/public/js/operation-bar.js
@@ -418,7 +418,8 @@ var generatePayload = function (operationCode, operationData, deviceList) {
             payload = {
                 "operation": {
                     "type": operationData["type"],
-                    "openvpn_config": operationData["openvpn_config"]
+                    "openvpn_config": operationData["openvpn_config"],
+                    "packageName": operationData["packageName"]
                 }
             };
             break;
diff --git a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.operation-mod/public/js/operation-mod.js b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.operation-mod/public/js/operation-mod.js
index fe820ecc3..866e7efd1 100644
--- a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.operation-mod/public/js/operation-mod.js
+++ b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.operation-mod/public/js/operation-mod.js
@@ -167,7 +167,8 @@ var androidOperationModule = function () {
             case androidOperationConstants["VPN_OPERATION_CODE"]:
                 payload = {
                     "type": operationPayload["type"],
-                    "openvpn_config": operationPayload["openvpn_config"]
+                    "openvpn_config": operationPayload["openvpn_config"],
+                    "packageName": operationPayload["packageName"]
                 };
                 break;
             case androidOperationConstants["APPLICATION_OPERATION_CODE"]:
@@ -425,7 +426,8 @@ var androidOperationModule = function () {
                 payload = {
                     "operation": {
                         "type": operationData["type"],
-                        "openvpn_config": operationData["openvpn_config"]
+                        "openvpn_config": operationData["openvpn_config"],
+                        "packageName": operationData["packageName"]
                     }
                 };
                 break;
@@ -1213,4 +1215,4 @@ var androidOperationModule = function () {
     };
 
     return publicMethods;
-}();
\ No newline at end of file
+}();
diff --git a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.policy-edit/public/js/android-policy-edit.js b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.policy-edit/public/js/android-policy-edit.js
index f00f61f30..f5b6fc4f4 100644
--- a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.policy-edit/public/js/android-policy-edit.js
+++ b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.policy-edit/public/js/android-policy-edit.js
@@ -590,14 +590,34 @@ var validatePolicyProfile = function () {
             // initializing continueToCheckNextInputs to true
             continueToCheckNextInputs = true;
 
-            var openvpnConfig = $("#openvpn-config").val();
-            if (!openvpnConfig || openvpnConfig === '') {
-                validationStatus = {
-                    "error": true,
-                    "subErrorMsg": "ovpn config required. You cannot proceed.",
-                    "erroneousFeature": operation
-                };
-                continueToCheckNextInputs = false;
+            let openVpnConfigEnabled = document.getElementById('vpn-body').classList.contains('in');
+            if (openVpnConfigEnabled) {
+                var openvpnConfig = $("textarea#openvpn-config").val();
+                if (!openvpnConfig || openvpnConfig === '') {
+                    validationStatus = {
+                        "error": true,
+                        "subErrorMsg": "ovpn config file is required. You cannot proceed.",
+                        "erroneousFeature": operation
+                    };
+                    continueToCheckNextInputs = false;
+                }
+            }
+
+            if (validationStatus) {
+                validationStatusArray.push(validationStatus);
+            }
+
+            let alwaysOnVpnEnabled = document.getElementById('always-on-vpn-body').classList.contains('in');
+            if (alwaysOnVpnEnabled) {
+                var alwaysOnConfig = $("input#vpn-client-app").val();
+                if (!alwaysOnConfig || alwaysOnConfig === '') {
+                    validationStatus = {
+                        "error": true,
+                        "subErrorMsg": "Add a valid package name for the VPN client",
+                        "erroneousFeature": "always-on-vpn"
+                    };
+                    continueToCheckNextInputs = false;
+                }
             }
 
             // at-last, if the value of continueToCheckNextInputs is still true
@@ -1317,4 +1337,4 @@ $(document).ready(function () {
             $(this).removeClass("child-input");
         });
     });
-});
\ No newline at end of file
+});
diff --git a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.policy-edit/public/templates/android-policy-edit.hbs b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.policy-edit/public/templates/android-policy-edit.hbs
index c2e2cb227..d8b15d0d6 100644
--- a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.policy-edit/public/templates/android-policy-edit.hbs
+++ b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.policy-edit/public/templates/android-policy-edit.hbs
@@ -1246,6 +1246,62 @@
                         <input id="vpn-type" class="form-control operationDataKeys" type="hidden" data-key="type" value="OpenVPN" />
                     </div>
                 </div>
+
+                <!-- Always on VPN connection settings -->
+
+                <div id="always-on-vpn-heading" class="panel-heading" role="tab">
+                    <h2 class="sub-title panel-title">
+                        Always On VPN Settings
+                        <label class="wr-input-control switch" data-toggle="collapse" data-target="#always-on-vpn-body">
+                            <input type="checkbox"/>
+                            <span class="helper"></span>
+                            <span class="text"></span>
+                        </label>
+                    </h2>
+                    <div class="panel-title-description">
+                        Configure an always-on VPN connection through a specific VPN client application
+                    </div>
+                </div>
+
+                <div id="always-on-vpn-body" class="panel-collapse panel-body collapse" role="tabpanel"
+                     aria-labelledby="vpn-body">
+                    <hr/>
+
+                    <div class="always-on-vpn-message">
+                        <ul class="message message-info">
+                            <i class="icon fw fw-info"></i>
+                            <a id="always-on-vpn-status-msg"> Below configurations are valid only when the Agent is
+                                <b>work-profile owner</b> or <b>device owner</b>.</a>
+                        </ul>
+                    </div>
+                    <br/>
+
+                    <div class="wr-input-control">
+                        <label class="wr-input-label" for="vpn-client-app">
+                            VPN Client Application Package Name*
+                            <span class="helper" title="Package name of the VPN client application to be configured.">
+                                <span class="wr-help-tip glyphicon glyphicon-question-sign"></span>
+                            </span>
+                        </label>
+                        <input id="vpn-client-app" type="text" class="form-control operationDataKeys"
+                               data-key="packageName" maxlength="200" placeholder="[ Should be a valid package name ]"/>
+                    </div>
+
+
+                    <!--<div class="wr-input-control">-->
+                        <!--<label class="wr-input-control checkbox">-->
+                            <!--<input id="vpn-lock-down" type="checkbox" class="operationDataKeys"-->
+                                   <!--data-key="lockDownEnable" checked="checked"/>-->
+                            <!--<span class="helper"-->
+                                  <!--title="True to disallow networking when the VPN is not connected or false otherwise">-->
+                                <!--Lock Down VPN-->
+                                <!--<span class="wr-help-tip glyphicon glyphicon-question-sign"></span>-->
+                            <!--</span>-->
+                        <!--</label>-->
+                    <!--</div>-->
+                </div>
+                <!-- Always on VPN connection settings -->
+
             </div>
         </div>
         <!-- /VPN -->
diff --git a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.policy-view/public/templates/android-policy-view.hbs b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.policy-view/public/templates/android-policy-view.hbs
index 8d8f78d00..1649a0009 100644
--- a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.policy-view/public/templates/android-policy-view.hbs
+++ b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.policy-view/public/templates/android-policy-view.hbs
@@ -2142,6 +2142,62 @@
                                data-key="openvpn_config" style="height: 400px;" disabled></textarea>
                     </div>
                 </div>
+
+                <!-- Always on VPN connection settings -->
+
+                <div id="always-on-vpn-heading" class="panel-heading" role="tab">
+                    <h2 class="sub-title panel-title">
+                        Always On VPN Settings
+                        <label class="wr-input-control switch" data-toggle="collapse" data-target="#always-on-vpn-body">
+                            <input type="checkbox"/>
+                            <span class="helper"></span>
+                            <span class="text"></span>
+                        </label>
+                    </h2>
+                    <div class="panel-title-description">
+                        Configure an always-on VPN connection through a specific VPN client application
+                    </div>
+                </div>
+
+                <div id="always-on-vpn-body" class="panel-collapse panel-body collapse" role="tabpanel"
+                     aria-labelledby="vpn-body">
+                    <hr/>
+
+                    <div class="always-on-vpn-message">
+                        <ul class="message message-info">
+                            <i class="icon fw fw-info"></i>
+                            <a id="always-on-vpn-status-msg"> Below configurations are valid only when the Agent is
+                                <b>work-profile owner</b> or <b>device owner</b>.</a>
+                        </ul>
+                    </div>
+                    <br/>
+
+                    <div class="wr-input-control">
+                        <label class="wr-input-label" for="vpn-client-app">
+                            VPN Client Application Package Name
+                            <span class="helper" title="Package name of the VPN client application to be configured.">
+                                <span class="wr-help-tip glyphicon glyphicon-question-sign"></span>
+                            </span>
+                        </label>
+                        <input id="vpn-client-app" type="text" class="form-control operationDataKeys"
+                               data-key="packageName" maxlength="200" placeholder="[ Should be a valid package name ]"/>
+                    </div>
+
+
+                    <!--<div class="wr-input-control">-->
+                        <!--<label class="wr-input-control checkbox">-->
+                            <!--<input id="vpn-lock-down" type="checkbox" class="operationDataKeys"-->
+                                   <!--data-key="lockDownEnable" checked="checked"/>-->
+                            <!--<span class="helper"-->
+                                  <!--title="True to disallow networking when the VPN is not connected or false otherwise">-->
+                                <!--Lock Down VPN-->
+                                <!--<span class="wr-help-tip glyphicon glyphicon-question-sign"></span>-->
+                            <!--</span>-->
+                        <!--</label>-->
+                    <!--</div>-->
+                </div>
+                <!-- Always on VPN connection settings -->
+
             </div>
         </div>
         <!-- /VPN -->
@@ -2810,4 +2866,4 @@
         <!--/enrolment-app-install-->
     </div>
     <script type="text/javascript">applyDataTable();</script>
-</div>
\ No newline at end of file
+</div>
diff --git a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.policy-wizard/public/js/android-policy-operations.js b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.policy-wizard/public/js/android-policy-operations.js
index 5554413f2..5ae4076b3 100644
--- a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.policy-wizard/public/js/android-policy-operations.js
+++ b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.policy-wizard/public/js/android-policy-operations.js
@@ -524,17 +524,35 @@ var validatePolicyProfile = function () {
             operation = androidOperationConstants["VPN_OPERATION"];
             // initializing continueToCheckNextInputs to true
             continueToCheckNextInputs = true;
+            let openVpnConfigEnabled = document.getElementById('vpn-body').classList.contains('in');
+            if (openVpnConfigEnabled) {
+                var openvpnConfig = $("input#openvpn-config").val();
+                if (!openvpnConfig || openvpnConfig === '') {
+                    validationStatus = {
+                        "error": true,
+                        "subErrorMsg": "ovpn config file is required. You cannot proceed.",
+                        "erroneousFeature": operation
+                    };
+                    continueToCheckNextInputs = false;
+                }
+            }
 
-            var openvpnConfig = $("input#openvpn-config").val();
-            if (!openvpnConfig || openvpnConfig === '') {
-                validationStatus = {
-                    "error": true,
-                    "subErrorMsg": "ovpn config file is required. You cannot proceed.",
-                    "erroneousFeature": operation
-                };
-                continueToCheckNextInputs = false;
+            if (validationStatus) {
+                validationStatusArray.push(validationStatus);
             }
 
+            let alwaysOnVpnEnabled = document.getElementById('always-on-vpn-body').classList.contains('in');
+            if (alwaysOnVpnEnabled) {
+                var alwaysOnConfig = $("input#vpn-client-app").val();
+                if (!alwaysOnConfig || alwaysOnConfig === '') {
+                    validationStatus = {
+                        "error": true,
+                        "subErrorMsg": "Add a valid package name for the VPN client",
+                        "erroneousFeature": "always-on-vpn"
+                    };
+                    continueToCheckNextInputs = false;
+                }
+            }
             // at-last, if the value of continueToCheckNextInputs is still true
             // this means that no error is found
             if (continueToCheckNextInputs) {
@@ -1294,4 +1312,4 @@ $(document).ready(function () {
             $(this).removeClass("child-input");
         });
     });
-});
\ No newline at end of file
+});
diff --git a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.policy-wizard/public/templates/android-policy-operations.hbs b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.policy-wizard/public/templates/android-policy-operations.hbs
index 79bb6d402..36ed7b995 100644
--- a/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.policy-wizard/public/templates/android-policy-operations.hbs
+++ b/components/mobile-plugins/android-plugin/org.wso2.carbon.device.mgt.mobile.android.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.android.policy-wizard/public/templates/android-policy-operations.hbs
@@ -2160,7 +2160,67 @@
                         <input id="openvpn-config" class="form-control operationDataKeys" type="hidden" data-key="openvpn_config" />
                         <input id="vpn-type" class="form-control operationDataKeys" type="hidden" data-key="type" value="OpenVPN" />
                     </div>
+                    <br/>
+
+                </div>
+
+                <!-- Always on VPN connection settings -->
+
+                <div id="always-on-vpn-heading" class="panel-heading" role="tab">
+                    <h2 class="sub-title panel-title">
+                        Always On VPN Settings
+                        <label class="wr-input-control switch" data-toggle="collapse" data-target="#always-on-vpn-body">
+                            <input type="checkbox"/>
+                            <span class="helper"></span>
+                            <span class="text"></span>
+                        </label>
+                    </h2>
+                    <div class="panel-title-description">
+                        Configure an always-on VPN connection through a specific VPN client application
+                    </div>
+                </div>
+
+                <div id="always-on-vpn-body" class="panel-collapse panel-body collapse" role="tabpanel"
+                     aria-labelledby="vpn-body">
+                    <hr/>
+
+                    <div class="always-on-vpn-message">
+                        <ul class="message message-info">
+                            <i class="icon fw fw-info"></i>
+                            <a id="always-on-vpn-status-msg"> Below configurations are valid only when the Agent is
+                                <b>work-profile owner</b> or <b>device owner</b>.</a>
+                        </ul>
+                    </div>
+                    <br/>
+                    <div id="always-on-vpn-feature-error-msg" class="alert alert-danger hidden" role="alert">
+                        <i class="icon fw fw-error"></i><span></span>
+                    </div>
+                    <div class="wr-input-control">
+                        <label class="wr-input-label" for="vpn-client-app">
+                            VPN Client Application Package Name
+                            <span class="helper" title="Package name of the VPN client application to be configured.">
+                                <span class="wr-help-tip glyphicon glyphicon-question-sign"></span>
+                            </span>
+                        </label>
+                        <input id="vpn-client-app" type="text" class="form-control operationDataKeys"
+                               data-key="packageName" maxlength="200" placeholder="[ Should be a valid package name ]"/>
+                    </div>
+
+
+                    <!--<div class="wr-input-control">-->
+                        <!--<label class="wr-input-control checkbox">-->
+                            <!--<input id="vpn-lock-down" type="checkbox" class="operationDataKeys"-->
+                                   <!--data-key="lockDownEnable" checked="checked"/>-->
+                            <!--<span class="helper"-->
+                                  <!--title="True to disallow networking when the VPN is not connected or false otherwise">-->
+                                <!--Lock Down VPN-->
+                                <!--<span class="wr-help-tip glyphicon glyphicon-question-sign"></span>-->
+                            <!--</span>-->
+                        <!--</label>-->
+                    <!--</div>-->
                 </div>
+                <!-- Always on VPN connection settings -->
+
             </div>
         </div>
         <!-- /VPN -->
@@ -2763,4 +2823,4 @@
         <!--/enrolment-app-install-->
     </div>
     <script type="text/javascript">applyDataTable();</script>
-</div>
\ No newline at end of file
+</div>