org.wso2.iot.agent.services.MessageProcessor.java Source code

Java tutorial

Introduction

Here is the source code for org.wso2.iot.agent.services.MessageProcessor.java

Source

/*
 * Copyright (c) 2014, WSO2 Inc. (http://www.wso2.org) 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.iot.agent.services;

import android.content.Context;
import android.content.Intent;
import android.util.Log;

import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.google.gson.Gson;

import org.json.JSONException;
import org.json.JSONObject;
import org.wso2.iot.agent.AndroidAgentException;
import org.wso2.iot.agent.R;
import org.wso2.iot.agent.activities.AuthenticationActivity;
import org.wso2.iot.agent.activities.ServerConfigsActivity;
import org.wso2.iot.agent.api.ApplicationManager;
import org.wso2.iot.agent.api.DeviceInfo;
import org.wso2.iot.agent.beans.AppInstallRequest;
import org.wso2.iot.agent.beans.Operation;
import org.wso2.iot.agent.beans.ServerConfig;
import org.wso2.iot.agent.proxy.interfaces.APIResultCallBack;
import org.wso2.iot.agent.proxy.utils.Constants.HTTP_METHODS;
import org.wso2.iot.agent.services.operation.OperationProcessor;
import org.wso2.iot.agent.utils.AppInstallRequestUtil;
import org.wso2.iot.agent.utils.CommonUtils;
import org.wso2.iot.agent.utils.Constants;
import org.wso2.iot.agent.utils.Preference;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * This class handles all the functionality related to coordinating the retrieval
 * and processing of messages from the server.
 */
public class MessageProcessor implements APIResultCallBack {

    private static final String ERROR_STATE = "ERROR";
    private static List<Operation> replyPayload;
    private String TAG = MessageProcessor.class.getSimpleName();
    private Context context;
    private String deviceId;
    private OperationProcessor operationProcessor;
    private ObjectMapper mapper;
    private boolean isWipeTriggered = false;
    private boolean isRebootTriggered = false;
    private boolean isUpgradeTriggered = false;
    private boolean isShellCommandTriggered = false;
    private boolean isEnterpriseWipeTriggered = false;
    private String shellCommand = null;

    /**
     * Local notification message handler.
     *
     * @param context Context of the application.
     */
    public MessageProcessor(Context context) {
        this.context = context;

        deviceId = Preference.getString(context, Constants.PreferenceFlag.DEVICE_ID_PREFERENCE_KEY);
        operationProcessor = new OperationProcessor(context.getApplicationContext());
        mapper = new ObjectMapper();
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);

        if (deviceId == null) {
            DeviceInfo deviceInfo = new DeviceInfo(context.getApplicationContext());
            deviceId = deviceInfo.getDeviceId();
            Preference.putString(context, Constants.PreferenceFlag.DEVICE_ID_PREFERENCE_KEY, deviceId);
        }
    }

    /**
     * This method executes the set of pending operations which is received from the
     * backend server.
     *
     * @param response Response received from the server that needs to be processed
     *                 and applied to the device.
     */
    private void performOperation(String response) {
        List<Operation> operations = new ArrayList<>();
        try {
            if (response != null) {
                operations = mapper.readValue(response,
                        mapper.getTypeFactory().constructCollectionType(List.class, Operation.class));
            }
            // check whether if there are any dismissed notifications to be sent
            operationProcessor.checkPreviousNotifications();
        } catch (JsonProcessingException e) {
            Log.e(TAG, "Issue in json parsing", e);
        } catch (IOException e) {
            Log.e(TAG, "Issue in stream parsing", e);
        } catch (AndroidAgentException e) {
            Log.e(TAG, "Error occurred while checking previous notification", e);
        }

        if (!(operations.isEmpty() || (operations.size() == 1
                && Constants.Operation.POLICY_MONITOR.equals(operations.get(0).getCode())))) {
            if (Constants.DEBUG_MODE_ENABLED) {
                Log.d(TAG, "Restarting to send quick update of received pending operations.");
            }
            LocalNotification.startPolling(context);
        }

        for (Operation op : operations) {
            try {
                operationProcessor.doTask(op);
            } catch (AndroidAgentException e) {
                Log.e(TAG, "Failed to perform operation", e);
            }
        }
        replyPayload = operationProcessor.getResultPayload();
    }

    /**
     * Call the message retrieval end point of the server to get messages pending.
     */
    public void getMessages() throws AndroidAgentException {
        String ipSaved = Constants.DEFAULT_HOST;
        String prefIP = Preference.getString(context.getApplicationContext(), Constants.PreferenceFlag.IP);
        if (prefIP != null) {
            ipSaved = prefIP;
        }
        ServerConfig utils = new ServerConfig();
        utils.setServerIP(ipSaved);
        String url = utils.getAPIServerURL(context) + Constants.DEVICES_ENDPOINT + deviceId
                + Constants.NOTIFICATION_ENDPOINT;

        Log.i(TAG, "Get pending operations from: " + url);

        String requestParams;
        ObjectMapper mapper = new ObjectMapper();
        try {
            if (replyPayload != null) {
                for (Operation operation : replyPayload) {
                    if (operation.getCode().equals(Constants.Operation.WIPE_DATA)
                            && !operation.getStatus().equals(ERROR_STATE)) {
                        isWipeTriggered = true;
                    } else if (operation.getCode().equals(Constants.Operation.ENTERPRISE_WIPE)
                            && !operation.getStatus().equals(ERROR_STATE)) {
                        isEnterpriseWipeTriggered = true;
                    } else if (operation.getCode().equals(Constants.Operation.REBOOT)
                            && !operation.getStatus().equals(ERROR_STATE)) {
                        isRebootTriggered = true;
                    } else if (operation.getCode().equals(Constants.Operation.UPGRADE_FIRMWARE)
                            && !operation.getStatus().equals(ERROR_STATE)) {
                        isUpgradeTriggered = true;
                        Preference.putInt(context, "firmwareOperationId", operation.getId());
                    } else if (operation.getCode().equals(Constants.Operation.EXECUTE_SHELL_COMMAND)
                            && !operation.getStatus().equals(ERROR_STATE)) {
                        isShellCommandTriggered = true;
                        try {
                            JSONObject payload = new JSONObject(operation.getPayLoad().toString());
                            shellCommand = (String) payload
                                    .get(context.getResources().getString(R.string.shared_pref_command));
                        } catch (JSONException e) {
                            throw new AndroidAgentException("Invalid JSON format.", e);
                        }
                    }
                }
            }
            int firmwareOperationId = Preference.getInt(context,
                    context.getResources().getString(R.string.firmware_upgrade_response_id));
            if (firmwareOperationId != 0) {
                Operation firmwareOperation = new Operation();
                firmwareOperation.setId(firmwareOperationId);
                firmwareOperation.setCode(Constants.Operation.UPGRADE_FIRMWARE);
                firmwareOperation.setStatus(Preference.getString(context,
                        context.getResources().getString(R.string.firmware_upgrade_response_status)));
                boolean isRetryPending = Preference.getBoolean(context,
                        context.getResources().getString(R.string.firmware_upgrade_retry_pending));
                if (isRetryPending) {
                    isUpgradeTriggered = true;
                    int retryCount = Preference.getInt(context,
                            context.getResources().getString(R.string.firmware_upgrade_retries));
                    firmwareOperation.setOperationResponse(
                            "Attempt " + retryCount + " has failed due to: " + Preference.getString(context,
                                    context.getResources().getString(R.string.firmware_upgrade_response_message)));
                } else {
                    firmwareOperation.setOperationResponse(Preference.getString(context,
                            context.getResources().getString(R.string.firmware_upgrade_response_message)));
                }
                if (replyPayload != null) {
                    replyPayload.add(firmwareOperation);
                } else {
                    replyPayload = new ArrayList<>();
                    replyPayload.add(firmwareOperation);
                }
                Preference.putInt(context, context.getResources().getString(R.string.firmware_upgrade_response_id),
                        0);
                Preference.putString(context,
                        context.getResources().getString(R.string.firmware_upgrade_response_status),
                        context.getResources().getString(R.string.operation_value_error));
                Preference.putString(context,
                        context.getResources().getString(R.string.firmware_upgrade_response_message), null);
            }

            int applicationOperationId = Preference.getInt(context,
                    context.getResources().getString(R.string.app_install_id));
            String applicationOperationCode = Preference.getString(context,
                    context.getResources().getString(R.string.app_install_code));
            String applicationOperationStatus = Preference.getString(context,
                    context.getResources().getString(R.string.app_install_status));
            String applicationOperationMessage = Preference.getString(context,
                    context.getResources().getString(R.string.app_install_failed_message));
            if (applicationOperationStatus != null && applicationOperationId != 0
                    && applicationOperationCode != null) {
                Operation applicationOperation = new Operation();
                ApplicationManager appMgt = new ApplicationManager(context);
                applicationOperation.setId(applicationOperationId);
                applicationOperation.setCode(applicationOperationCode);
                applicationOperation = appMgt.getApplicationInstallationStatus(applicationOperation,
                        applicationOperationStatus, applicationOperationMessage);
                if (replyPayload == null) {
                    replyPayload = new ArrayList<>();
                }
                replyPayload.add(applicationOperation);
                Preference.putString(context, context.getResources().getString(R.string.app_install_status), null);
                Preference.putString(context, context.getResources().getString(R.string.app_install_failed_message),
                        null);
                if (context.getResources().getString(R.string.operation_value_error)
                        .equals(applicationOperation.getStatus())
                        || context.getResources().getString(R.string.operation_value_completed)
                                .equals(applicationOperation.getStatus())) {
                    Preference.putInt(context, context.getResources().getString(R.string.app_install_id), 0);
                    Preference.putString(context, context.getResources().getString(R.string.app_install_code),
                            null);
                    startPendingInstallation();
                }
            } else {
                startPendingInstallation();
            }

            if (Preference.hasPreferenceKey(context, Constants.Operation.LOGCAT)) {
                if (Preference.hasPreferenceKey(context, Constants.Operation.LOGCAT)) {
                    Gson operationGson = new Gson();
                    Operation logcatOperation = operationGson
                            .fromJson(Preference.getString(context, Constants.Operation.LOGCAT), Operation.class);
                    if (replyPayload == null) {
                        replyPayload = new ArrayList<>();
                    }
                    replyPayload.add(logcatOperation);
                    Preference.removePreference(context, Constants.Operation.LOGCAT);
                }
            }
            requestParams = mapper.writeValueAsString(replyPayload);
        } catch (JsonMappingException e) {
            throw new AndroidAgentException("Issue in json mapping", e);
        } catch (JsonGenerationException e) {
            throw new AndroidAgentException("Issue in json generation", e);
        } catch (IOException e) {
            throw new AndroidAgentException("Issue in parsing stream", e);
        }
        if (Constants.DEBUG_MODE_ENABLED) {
            Log.d(TAG, "Reply Payload: " + requestParams);
        }

        if (requestParams != null
                && requestParams.trim().equals(context.getResources().getString(R.string.operation_value_null))) {
            requestParams = null;
        }

        if (ipSaved != null && !ipSaved.isEmpty()) {
            CommonUtils.callSecuredAPI(context, url, HTTP_METHODS.PUT, requestParams, MessageProcessor.this,
                    Constants.NOTIFICATION_REQUEST_CODE);
        } else {
            Log.e(TAG, "There is no valid IP to contact the server");
        }
    }

    private void startPendingInstallation() {
        AppInstallRequest appInstallRequest = AppInstallRequestUtil.getPending(context);
        if (appInstallRequest != null) {
            ApplicationManager applicationManager = new ApplicationManager(context.getApplicationContext());
            Operation applicationOperation = new Operation();
            applicationOperation.setId(appInstallRequest.getApplicationOperationId());
            applicationOperation.setCode(appInstallRequest.getApplicationOperationCode());
            Log.d(TAG, "Try to start app installation from queue.");
            applicationManager.installApp(appInstallRequest.getAppUrl(), null, applicationOperation);
        }
    }

    @SuppressWarnings("unused")
    @Override
    public void onReceiveAPIResult(Map<String, String> result, int requestCode) {
        String responseStatus;
        String response;
        if (requestCode == Constants.NOTIFICATION_REQUEST_CODE) {
            Preference.putLong(context, Constants.PreferenceFlag.LAST_SERVER_CALL,
                    CommonUtils.currentDate().getTime());
            Intent intent = new Intent();
            intent.setAction(Constants.SYNC_BROADCAST_ACTION);
            context.sendBroadcast(intent);

            if (isWipeTriggered) {
                if (Constants.SYSTEM_APP_ENABLED) {
                    CommonUtils.callSystemApp(context, Constants.Operation.WIPE_DATA, null, null);
                } else {
                    Log.i(TAG, "Not the device owner.");
                }
            }

            if (isEnterpriseWipeTriggered) {
                CommonUtils.disableAdmin(context);

                Intent intentEnterpriseWipe = new Intent(context, ServerConfigsActivity.class);
                intentEnterpriseWipe.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                intentEnterpriseWipe.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                context.startActivity(intentEnterpriseWipe);
                if (Constants.DEBUG_MODE_ENABLED) {
                    Log.d(TAG, "Started enterprise wipe");
                }
            }

            if (isRebootTriggered) {
                CommonUtils.callSystemApp(context, Constants.Operation.REBOOT, null, null);
            }

            if (isUpgradeTriggered) {
                String schedule = Preference.getString(context,
                        context.getResources().getString(R.string.pref_key_schedule));
                CommonUtils.callSystemApp(context, Constants.Operation.UPGRADE_FIRMWARE, schedule, null);
            }

            if (isShellCommandTriggered && shellCommand != null) {
                CommonUtils.callSystemApp(context, Constants.Operation.EXECUTE_SHELL_COMMAND, shellCommand, null);
            }

            if (result != null) {
                responseStatus = result.get(Constants.STATUS_KEY);
                response = result.get(Constants.RESPONSE);
                if (Constants.Status.SUCCESSFUL.equals(responseStatus)
                        || Constants.Status.CREATED.equals(responseStatus)) {
                    if (response != null && !response.isEmpty()) {
                        if (Constants.DEBUG_MODE_ENABLED) {
                            Log.d(TAG, "Pending Operations List: " + response);
                        }
                        if (Constants.DEFAULT_OWNERSHIP.equals(Constants.OWNERSHIP_COSU)) {
                            if (!Preference.getBoolean(context, Constants.PreferenceFlag.DEVICE_INITIALIZED)) {
                                Preference.putBoolean(context, Constants.PreferenceFlag.DEVICE_INITIALIZED, true);
                            }
                        }
                        performOperation(response);
                    }
                } else if (Constants.Status.AUTHENTICATION_FAILED.equals(responseStatus)
                        && org.wso2.iot.agent.proxy.utils.Constants.REFRESH_TOKEN_EXPIRED.equals(response)) {
                    Log.d(TAG, "Requesting credentials to obtain new token pair.");
                    LocalNotification.stopPolling(context);
                    Preference.putBoolean(context, Constants.TOKEN_EXPIRED, true);
                    CommonUtils.displayNotification(context, R.drawable.ic_error_outline_white_24dp,
                            context.getResources().getString(R.string.title_need_to_sign_in),
                            context.getResources().getString(R.string.msg_need_to_sign_in),
                            AuthenticationActivity.class, Constants.TOKEN_EXPIRED,
                            Constants.SIGN_IN_NOTIFICATION_ID);
                }
            }
        }
    }

}