com.atlassian.confluence.content.render.xhtml.migration.exceptions.UnknownMacroMigrationException: The macro 'next_previous_link3' is unknown.

Reporting Critical Events via Alerts

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 IoT Server 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 IoT Server creates alerts to report critical issues.

Step 1: Enabling the existing event listeners.

  1. Open the Constants.java file that is in the org.wso2.emm.agent.utils package via a preferred IDE.

    For more information on the default configuration, see the Constants.java file.

  2. Assign true as the value for the ENABLE_EVENT_LISTENING property that is in the EventListners class.

    Why is this step needed?

     The ENABLE_EVENT_LISTENING property is a global configuration and it needs to be enabled in order to enable event listening.

  3. 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.

Prerequisite

 Download and install Android Studio. For more information, see installing Android Studio.

  1. 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.
  2. Implement the AlertEventListener interface in the org.wso2.emm.agent.events.listeners package.

     Click here for more information on the methods used in the AlertEventListener interface.

    The following methods are used:

    • startListening() - This method is used to start listening to an event broadcasted by the OS. In the case of a polling based check, this method can be called by the onReceive method of the AlarmReceiver.
    • stopListening() - Unregister the event receiver or stop an alarm.
    • publishEvent(String payload) - Handle the data published to IoT Server.

    Example:

    AlertEventListener interface
    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);
    
    }
    
  3. 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 {}
  4. Capturing events. There are two ways to capture the events as listed below:
    1. 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.

      Prerequsite

      Prior knowledge on the Android Broadcast Receivers will help you understand the concept clearly, as WSO2 IoT Server uses it to listen to the events. For more information, see BroadcastReceiver.

      Follow the steps given below to configure WSO2 IoT Server, to listen to the broadcasted events:

      1. 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);
        }
      2. 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.

        The Bean classes are located in the org.wso2.emm.agent.events.beans package.

      3. 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);
      4. Publish the data to WSO2 IoT Server 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);
        }
      5. Optionally, if you wish to publish data to a different server other than WSO2 IoT Server 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");
                    }
                }
            }
        }
        
      6. Open the AndroidManifest.xml file that is in the <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
        <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.

      7. Call the startListening method from the register method, to register the listener. The register method is in the EventRegistry class.

        For more information on the default configuration, see the EventRegistry.java file.

        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);
                }
            }
        }

    2. 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 IoT Server 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.

      1. Configure the startListening() method of the AlertEventListener interface, for the API to poll continuously, and check if any events have triggered the alarm.

        The startListening() method needs to be configured in the new class you created. This class is in the org.wso2.emm.agent.events.listeners package.

        @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");
          }
         }
        }
      2. 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.

        The Bean classes are located in the org.wso2.emm.agent.events.beans package.

      3. 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);
      4. Publish the data to WSO2 IoT Server 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);
        }
      5. 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.

        For more information on the AlarmManager concept, see AlarmManager.

        • Option 1: Setting up the default alarm
          Use this option, if you wish to use the existing DEFAULT_START_TIME and DEFAULT_INTERVAL values. 

          1. In this method, you don't have to do any changes to the default EventRegistry.startAlarm method that is in the org.wso2.emm.agent.events package.

            • DEFAULT_START_TIME: The time the alarm should first go off, in milliseconds. The default value is 1000.
            • DEFAULT_INTERVAL: The interval between subsequent repeats of the alarm, in milliseconds. The default value is 30000.
            • For more information on the default configuration, see EventRegistry.java file.
          2. 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.startAlarm(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. 

          You will be using the same EventAlarmReceiver class, which is in the org.wso2.emm.agent.events package, in both option 1 and option 2.

          1. Configure the DEFAULT_LISTENER_CODEDEFAULT_START_TIME, and DEFAULT_INTERVAL values.

            EventRegistry.startAlarm(Constants.EventListners.DEFAULT_LISTENER_CODE,
                                                Constants.EventListners.DEFAULT_START_TIME,
                                                Constants.EventListners.DEFAULT_INTERVAL);

            Example:

            EventRegistry.startDAlarm(Constants.EventListners.NEWLY_ADDED_LISTENER_CODE,
                                                Constants.EventListners.3000,
                                                Constants.EventListners.20000);
            • DEFAULT_LISTENER_CODE: The code used by the EventAlarmReceiver to identify the event listeners. Make sure to change this field to a preferred value.

            • DEFAULT_START_TIME: Define the time the alarm should first go off, in milliseconds.
            • DEFAULT_INTERVAL: Define the interval between subsequent repeats of the alarm, in milliseconds.
            • For more information on the default configuration, see EventRegistry.java file.
          2. Configure the EventAlarmReceiver class that is in the org.wso2.emm.agent.events package.
            1. 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)
            2. Write a logic inside the startListening() to periodically poll the API.

              if (requestCode == Constants.EventListners.DEFAULT_LISTENER_CODE) {
                  RuntimeStateListener runtimeStateListener = new RuntimeStateListener();
                runtimeStateListener.startListening();
              }

com.atlassian.confluence.content.render.xhtml.migration.exceptions.UnknownMacroMigrationException: The macro 'next_previous_links2' is unknown.