When managing devices the administrator must be notified of events occurring on the user's devices that reduce the effectiveness and efficiency of the tasks performed by a device, so that he/she can take immediate action and correct it. For example, if a cooperate application is utilizing a high CPU or memory percentage, you as the admin can stop that application from running or uninstall and re-install the application again on the user's device.
In WSO2 EMM all such events are captured and published to the WSO2 Data Analytics Server (WSO2 DAS). In this section, let's take a look at how WSO2 EMM creates alerts to report critical issues.
Step 1: Enabling the existing event listeners.
Open the Constants.java
file that is in the
org.wso2.emm.agent.utils
package via a preferred IDE.
Assign true
as the value for the ENABLE_EVENT_LISTENING
property that is in the EventListners
class.
- Enable the preferred listeners.
APPLICATION_STATE_LISTENER
RUNTIME_STATE_LISTENER
Step 2: Writing a new event listener.
In this section, let's look at how a new event can be captured from the Android agent and published to WSO2 DAS.
- Open the Android agent source code in Android studio.
The event related logic is included in the org.wso2.emm.agent.events
package. It has the following folder structure.
Implement the AlertEventListener
interface in the org.wso2.emm.agent.events.listeners
package.
Example:
package org.wso2.emm.agent.events.listeners;
/**
* This is used to define any new events that needs to be captured and sent to server.
*/
public interface AlertEventListener {
/**
* This can be used to start listening to a specific broadcast receiver.
* Another usage would be, when there is an event that doesn't do a broadcast. For example
* Application exceeding 75% of CPU is not broadcasted by default from the Android OS. Only way
* to catch it is by constantly polling a specific API and check for the status. In such a
* situation, AlarmManager can call startListening on it onReceiver method to do the polling on
* an API.
*/
void startListening();
/**
* If in case, listening to a specific receiver need to be done here. This can be a place to,
* stop an AlarmManager.
*/
void stopListening();
/**
* This is where publishing data to EMM/DAS would happen. This can ideally be called from
* an onReceive method of a BroadcastReceiver, or from startListening method to inform the
* results of a polling.
*
* @param payload JSON string payload to be published.
* @param type type of the alert being published.
*/
void publishEvent(String payload, String type);
}
Create a new class in the org.wso2.emm.agent.events.listeners
package, extend it from BroadcastReceiver
, and implement the AlertEventListener
interface.
Example: Create a class named ApplicationStateListener
.
public class ApplicationStateListener extends BroadcastReceiver implements AlertEventListener {}
- Capturing events. There are two ways to capture the events as listed below:
Listen to events broadcasted by the Android OS.
Click here for more information.
This section provides information on how to listen and capture events that are broadcasted by the Android OS.
Prior knowledge on the Android Broadcast Receivers will help you understand the concept clearly, as WSO2 EMM uses it to listen to the events. For more information, see BroadcastReceiver.
Follow the steps given below to configure WSO2 EMM, to listen to the broadcasted events:
Register the receiver in the startListening()
method of the AlertEventListener
interface, in the class you created in the org.wso2.emm.agent.events.listeners
package.
Example:
@Override
public void startListening() {
EventRegistry.context.registerReceiver(this, intentFilter);
}
When an event is received the onReceive
method that is in the org.wso2.emm.agent.events.listeners
package will be notified. At that point you need to extract the data from the intent and populate the bean with the data.
Convert the data populated by the bean to a JSON string using the CommonUtils.toJSON(beanInstance)
.
Example:
ApplicationStatus applicationState = new ApplicationStatus();
applicationState.setState(status);
applicationState.setPackageName(packageName);
String appState = CommonUtils.toJSON(applicationState);
Publish the data to WSO2 EMM by calling the publishEvent
method. For this you need to configure the publish
method so that it calls the HttpDataPublisher
.
Example:
@Override
public void publishEvent(String payload, String type) {
EventPayload eventPayload = new EventPayload();
eventPayload.setPayload(payload);
eventPayload.setType(type);
HttpDataPublisher httpDataPublisher = new HttpDataPublisher();
httpDataPublisher.publish(eventPayload);
}
Optionally, if you wish to publish data to a different server other than WSO2 EMM or use a different protocol, you can do so by writing a new data publisher.
Implement the DataPublisher
interface that is in the package org.wso2.emm.agent.events.publisher
, to write the new data publisher.
Click here to see a sample that is written to listen to application changes on the device, such as application installs, uninstalls, upgrades, and data clearing.
/**
* Listening to application state changes such as an app getting installed, uninstalled,
* upgraded and data cleared.
*/
public class ApplicationStateListener extends BroadcastReceiver implements AlertEventListener {
private static final String TAG = ApplicationStateListener.class.getName();
@Override
public void startListening() {
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
intentFilter.addAction(Intent.ACTION_PACKAGE_REPLACED);
intentFilter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED);
intentFilter.addDataScheme("package");
EventRegistry.context.registerReceiver(this, intentFilter);
}
@Override
public void stopListening() {
if (EventRegistry.context != null) {
EventRegistry.context.unregisterReceiver(this);
}
}
@Override
public void publishEvent(String payload, String type) {
EventPayload eventPayload = new EventPayload();
eventPayload.setPayload(payload);
eventPayload.setType(type);
HttpDataPublisher httpDataPublisher = new HttpDataPublisher();
httpDataPublisher.publish(eventPayload);
}
@Override
public void onReceive(Context context, final Intent intent) {
String status = null;
ApplicationStatus applicationState;
switch (intent.getAction()) {
case Intent.ACTION_PACKAGE_ADDED:
status = "added";
break;
case Intent.ACTION_PACKAGE_REMOVED:
status = "removed";
break;
case Intent.ACTION_PACKAGE_REPLACED:
status = "upgraded";
break;
case Intent.ACTION_PACKAGE_DATA_CLEARED:
status = "dataCleared";
break;
default:
Log.i(TAG, "Invalid intent received");
}
if (status != null) {
String packageName = intent.getData().getEncodedSchemeSpecificPart();
applicationState = new ApplicationStatus();
applicationState.setState(status);
applicationState.setPackageName(packageName);
try {
String appState = CommonUtils.toJSON(applicationState);
publishEvent(appState, Constants.EventListners.APPLICATION_STATE);
if (Constants.DEBUG_MODE_ENABLED) {
Log.d(TAG, appState);
}
} catch (AndroidAgentException e) {
Log.e(TAG, "Could not convert to JSON");
}
}
}
}
Open the AndroidManifest.xml
file that is in the <EMM_Android_agent>/client/src/main
folder, and add a new receiver. The name of the receiver should be the same as the listener class you created in step 1.
Example:
<receiver android:name=".events.listeners.ApplicationStateListener">
<intent-filter>
<action android:name="android.intent.action.PACKAGE_ADDED" />
<action android:name="android.intent.action.PACKAGE_REMOVED" />
<action android:name="android.intent.action.PACKAGE_REPLACED" />
<action android:name="android.intent.action.PACKAGE_DATA_CLEARED" />
<data android:scheme="package"/>
</intent-filter>
</receiver>
Now you have set up the listener.
Call the startListening
method from the register
method, to register the listener. The register
method is in the EventRegistry
class.
Example:
public void register() {
// First, check if event listening is enabled. If yes, check each event that is enabled and
// start event listening.
if (Constants.EventListners.EVENT_LISTENING_ENABLED) {
if (Constants.EventListners.APPLICATION_STATE_LISTENER) {
// If the listener is implementing broadcast listener, calling start listener
// should start listening for events.
ApplicationStateListener applicationState = new ApplicationStateListener();
applicationState.startListening();
}
if (Constants.EventListners.RUNTIME_STATE_LISTENER) {
// If the event is running on a scheduled polling, it is only necessary to schedule
// the alarm manager. If the same DEFAULT_START_TIME and DEFAULT_INTERVAL
// can be used for any new event, there is no need to create a new
// scheduled alarm here.
EventRegistry.startDefaultAlarm(Constants.EventListners.DEFAULT_LISTENER_CODE,
Constants.EventListners.DEFAULT_START_TIME,
Constants.EventListners.DEFAULT_INTERVAL);
}
}
}
Poll an API to check for event changes that are not broadcasted continuously by the Android OS.
Click here for more information.
This section describes how WSO2 EMM is configured to check the status of the events that are not broadcasted by the OS. This is done by continuously polling the relevant APIs.
Follow the steps given below to check the status of the events via the Android API when the alarm is triggered.
Configure the startListening()
method of the AlertEventListener
interface, for the API to poll continuously, and check if any events have triggered the alarm.
@Override
public void startListening() {
RuntimeInfo runtimeInfo = new RuntimeInfo(EventRegistry.context, new String[] {
"top",
"-n",
"1",
"-m",
"1",
"-s",
"cpu"
});
Application application = runtimeInfo.getHighestCPU();
if (application.getCpu() > CPU_THRESHOLD) {
try {
String appState = CommonUtils.toJSON(application);
publishEvent(appState, Constants.EventListners.RUNTIME_STATE);
if (Constants.DEBUG_MODE_ENABLED) {
Log.d(TAG, appState);
}
} catch (AndroidAgentException e) {
Log.i(TAG, "Could not convert to JSON");
}
}
}
When an event is received, the onReceive
method that is in the org.wso2.emm.agent.events.listeners
package is notified. At that point you need to extract the data from the intent and populate the bean with the data.
Convert the data populated by the bean to a JSON string using the CommonUtils.toJSON(beanInstance)
.
Example:
ApplicationStatus applicationState = new ApplicationStatus();
applicationState.setState(status);
applicationState.setPackageName(packageName);
String appState = CommonUtils.toJSON(applicationState);
Publish the data to WSO2 EMM by calling the publishEvent
method. For this you need to configure the publish
method so that it calls the HttpDataPublisher
.
Example:
@Override
public void publishEvent(String payload, String type) {
EventPayload eventPayload = new EventPayload();
eventPayload.setPayload(payload);
eventPayload.setType(type);
HttpDataPublisher httpDataPublisher = new HttpDataPublisher();
httpDataPublisher.publish(eventPayload);
}
If you wish to periodically poll the APIs, you need to schedule the AlarmManager. This can be done via two options, depending on your requirement.
Option 1: Setting up the default alarm
Use this option, if you wish to use the existing DEFAULT_START_TIME
and DEFAULT_INTERVAL
values.
In this method you don't have to do any changes to the default EventRegistry.startDefaultAlarm
method that is in the org.wso2.emm.agent.events
package.
- Register the new listener by adding an
or
condition to the existing if
condition.
Example:
if (Constants.EventListners.RUNTIME_STATE_LISTENER || Constants.EventListners.NEWLY_ADDED_LISTENER) {
// If the event is running on a scheduled polling, it is only necessary to schedule
// the alarm manager. If the same DEFAULT_START_TIME and DEFAULT_INTERVAL
// can be used for any new event, there is no need to create a new
// scheduled alarm here.
EventRegistry.startDefaultAlarm(Constants.EventListners.DEFAULT_LISTENER_CODE,
Constants.EventListners.DEFAULT_START_TIME,
Constants.EventListners.DEFAULT_INTERVAL);
}
Option 2: Setting up a new alarm
Use this method, if you wish to change the DEFAULT_START_TIME
and DEFAULT_INTERVAL
values.
Configure the DEFAULT_LISTENER_CODE
, DEFAULT_START_TIME
, and DEFAULT_INTERVAL
values.
EventRegistry.startDefaultAlarm(Constants.EventListners.DEFAULT_LISTENER_CODE,
Constants.EventListners.DEFAULT_START_TIME,
Constants.EventListners.DEFAULT_INTERVAL);
Example:
EventRegistry.startDefaultAlarm(Constants.EventListners.NEWLY_ADDED_LISTENER_CODE,
Constants.EventListners.3000,
Constants.EventListners.20000);
- Configure the
EventAlarmReceiver
class that is in the org.wso2.emm.agent.events
package.Configure the Constants.EventListners.DEFAULT_LISTENER_CODE
property by updating it with the new code you defined in step a.
if (requestCode == Constants.EventListners.DEFAULT_LISTENER_CODE)
Example:
if (requestCode == Constants.EventListners.NEWLY_ADDED_LISTENER_CODE)
Write a logic inside the startListening()
to periodically poll the API.
if (requestCode == Constants.EventListners.DEFAULT_LISTENER_CODE) {
RuntimeStateListener runtimeStateListener = new RuntimeStateListener();
runtimeStateListener.startListening();
}