[SEBA-623] Handled TimeOut of AAA sessions

Cherry-picked from aaa-1.10.

Change-Id: I75f255fcbc4de6b8d6aa864edc6ad7a6fe85ca7d
diff --git a/api/src/main/java/org/opencord/aaa/AaaConfig.java b/api/src/main/java/org/opencord/aaa/AaaConfig.java
index 2c4aa7b..a84e72f 100644
--- a/api/src/main/java/org/opencord/aaa/AaaConfig.java
+++ b/api/src/main/java/org/opencord/aaa/AaaConfig.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2015-present Open Networking Foundation
+  Copyright 2015-present Open Networking Foundation
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -15,22 +15,20 @@
  */
 package org.opencord.aaa;
 
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.node.ArrayNode;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.HashSet;
+import java.util.Set;
+
 import org.apache.commons.lang3.builder.ToStringBuilder;
-
-import com.google.common.collect.ImmutableSet;
-
 import org.onosproject.core.ApplicationId;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.config.Config;
 import org.onosproject.net.config.basics.BasicElementConfig;
 
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-
-import java.util.HashSet;
-import java.util.Set;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.google.common.collect.ImmutableSet;
 
 /**
  * Network config for the AAA app.
@@ -40,13 +38,14 @@
     private static final String RADIUS_HOST = "radiusHost";
     private static final String RADIUS_IP = "radiusIp";
     private static final String RADIUS_SERVER_PORT = "radiusServerPort";
+    private static final String SESSION_CLEANUP_TIMER = "sessionCleanupTimer";
     private static final String RADIUS_MAC = "radiusMac";
     private static final String NAS_IP = "nasIp";
     private static final String NAS_MAC = "nasMac";
     private static final String RADIUS_SECRET = "radiusSecret";
     private static final String RADIUS_VLAN_ID = "vlanId";
     private static final String RADIUS_VLAN_PRIORITY_BIT = "radiusPBit";
-    private static final String RADIUS_CONNECTION_TYPE =  "radiusConnectionType";
+    private static final String RADIUS_CONNECTION_TYPE = "radiusConnectionType";
     private static final String RADIUS_SERVER_CONNECTPOINTS = "radiusServerConnectPoints";
     // Which packet customizer to use
     // "packetCustomizer" : "sample" -- Means use SamplePAcketCustomizer
@@ -73,6 +72,9 @@
     // Radius Server UDP Port Number
     protected static final String DEFAULT_RADIUS_SERVER_PORT = "1812";
 
+    // Time configured for triggering timeouts in AAA app
+    protected static final String DEFAULT_SESSION_CLEANUP_TIMER = "10";
+
     // Radius Server Vlan ID
     protected static final String DEFAULT_RADIUS_VLAN_ID = "4093";
 
@@ -86,10 +88,9 @@
     protected static final String DEFAULT_PACKET_CUSTOMIZER = "default";
 
     /**
-     * Gets the value of a string property, protecting for an empty
-     * JSON object.
+     * Gets the value of a string property, protecting for an empty JSON object.
      *
-     * @param name name of the property
+     * @param name         name of the property
      * @param defaultValue default value if none has been specified
      * @return String value if one os found, default value otherwise
      */
@@ -213,8 +214,7 @@
      * @return radius server UDP port.
      */
     public short radiusServerUdpPort() {
-        return Short.parseShort(getStringProperty(RADIUS_SERVER_PORT,
-                                                  DEFAULT_RADIUS_SERVER_PORT));
+        return Short.parseShort(getStringProperty(RADIUS_SERVER_PORT, DEFAULT_RADIUS_SERVER_PORT));
     }
 
     /**
@@ -264,6 +264,16 @@
     }
 
     /**
+     * Returns the time configured for checking timeout .
+     *
+     * @return timerTimeout
+     */
+    public int sessionCleanupTimer() {
+        return Integer
+                .parseInt(getStringProperty(SESSION_CLEANUP_TIMER, DEFAULT_SESSION_CLEANUP_TIMER));
+    }
+
+    /**
      * Returns the List of ConnectPoints to reach the Radius Server.
      *
      * @return List of ConnectPoints
diff --git a/api/src/main/java/org/opencord/aaa/AaaStatistics.java b/api/src/main/java/org/opencord/aaa/AaaStatistics.java
index 48c95b6..e345a68 100644
--- a/api/src/main/java/org/opencord/aaa/AaaStatistics.java
+++ b/api/src/main/java/org/opencord/aaa/AaaStatistics.java
@@ -30,6 +30,8 @@
     private AtomicLong accessRequestsTx = new AtomicLong();
     // Number of access request packets pending a response from the server
     private AtomicLong pendingRequests = new AtomicLong();
+    // Number of packets send to the server which timed out.
+    private AtomicLong timedOutPackets = new AtomicLong();
     // Number of packets of an unknown RADIUS type received from the accounting
     // server
     private AtomicLong unknownTypeRx = new AtomicLong();
@@ -307,4 +309,13 @@
     public void incrementEapPktTxauthEap() {
         eapPktTxauthChooseEap.incrementAndGet();
     }
+
+    public long getTimedOutPackets() {
+        return timedOutPackets.get();
+    }
+
+    public void increaseTimedOutPackets() {
+        timedOutPackets.incrementAndGet();
+    }
+
 }
diff --git a/app/src/main/java/org/opencord/aaa/impl/AaaManager.java b/app/src/main/java/org/opencord/aaa/impl/AaaManager.java
index da9ed6f..add846a 100755
--- a/app/src/main/java/org/opencord/aaa/impl/AaaManager.java
+++ b/app/src/main/java/org/opencord/aaa/impl/AaaManager.java
@@ -159,6 +159,9 @@
     // our unique identifier
     private ApplicationId appId;
 
+    // TimeOut time for cleaning up stateMachines stuck due to pending AAA/EAPOL message.
+    protected int cleanupTimerTimeOutInMins;
+
     // Setup specific customization/attributes on the RADIUS packets
     PacketCustomizer pktCustomizer;
 
@@ -248,6 +251,8 @@
         packetService.addProcessor(processor, PacketProcessor.director(2));
         StateMachine.initializeMaps();
         StateMachine.setDelegate(delegate);
+        cleanupTimerTimeOutInMins = newCfg.sessionCleanupTimer();
+        StateMachine.setcleanupTimerTimeOutInMins(cleanupTimerTimeOutInMins);
         impl.initializeLocalState(newCfg);
         impl.requestIntercepts();
         deviceService.addListener(deviceListener);
@@ -375,7 +380,22 @@
         impl.sendRadiusPacket(radiusPacket, inPkt);
     }
 
-    /**
+   /**
+     * For scheduling the timer required for cleaning up StateMachine
+     * when no response
+     * from RADIUS SERVER.
+     *
+     * @param sessionId    SessionId of the current session
+     * @param stateMachine StateMachine for the id
+     */
+    public void scheduleStateMachineCleanupTimer(String sessionId, StateMachine stateMachine) {
+        StateMachine.CleanupTimerTask cleanupTask = stateMachine.new CleanupTimerTask(sessionId, this);
+        ScheduledFuture<?> cleanupTimer = executor.schedule(cleanupTask, cleanupTimerTimeOutInMins, TimeUnit.MINUTES);
+        stateMachine.setCleanupTimer(cleanupTimer);
+
+    }
+
+   /**
      * Handles RADIUS packets.
      *
      * @param radiusPacket RADIUS packet coming from the RADIUS server.
@@ -592,6 +612,9 @@
                 case EAPOL.EAPOL_START:
                     log.debug("EAP packet: EAPOL_START");
                     stateMachine.setSupplicantConnectpoint(inPacket.receivedFrom());
+                    if (stateMachine.getCleanupTimer() == null) {
+                        scheduleStateMachineCleanupTimer(sessionId, stateMachine);
+                    }
                     stateMachine.start();
                     aaaStatisticsManager.getAaaStats().incrementEapolStartReqTrans();
                     //send an EAP Request/Identify to the supplicant
@@ -627,6 +650,10 @@
 
                         case EAP.ATTR_IDENTITY:
                             log.debug("EAP packet: EAPOL_PACKET ATTR_IDENTITY");
+                            //Setting the time of this response from RG, only when its not a re-transmission.
+                            if (stateMachine.getLastPacketReceivedTime() == 0) {
+                               stateMachine.setLastPacketReceivedTime(System.currentTimeMillis());
+                            }
                             // request id access to RADIUS
                             stateMachine.setUsername(eapPacket.getData());
 
@@ -635,6 +662,7 @@
                             radiusPayload.addMessageAuthenticator(AaaManager.this.radiusSecret);
 
                             sendRadiusPacket(radiusPayload, inPacket);
+                            stateMachine.setWaitingForRadiusResponse(true);
                             aaaStatisticsManager.getAaaStats().incrementEapolAtrrIdentity();
                             // change the state to "PENDING"
                             if (stateMachine.state() == StateMachine.STATE_PENDING) {
@@ -643,6 +671,7 @@
                             stateMachine.requestAccess();
                             break;
                         case EAP.ATTR_MD5:
+                            stateMachine.setLastPacketReceivedTime(System.currentTimeMillis());
                             log.debug("EAP packet: EAPOL_PACKET ATTR_MD5");
                             // verify if the EAP identifier corresponds to the
                             // challenge identifier from the client state
@@ -661,6 +690,7 @@
                                 }
                                 radiusPayload.addMessageAuthenticator(AaaManager.this.radiusSecret);
                                 sendRadiusPacket(radiusPayload, inPacket);
+                                stateMachine.setWaitingForRadiusResponse(true);
                                 aaaStatisticsManager.getAaaStats().incrementEapolMd5RspChall();
                             }
                             break;
@@ -678,6 +708,7 @@
 
                             radiusPayload.addMessageAuthenticator(AaaManager.this.radiusSecret);
                             sendRadiusPacket(radiusPayload, inPacket);
+                            stateMachine.setWaitingForRadiusResponse(true);
                             aaaStatisticsManager.getAaaStats().incrementEapolTlsRespChall();
 
                             if (stateMachine.state() != StateMachine.STATE_PENDING) {
@@ -824,6 +855,7 @@
             log.debug("RequestRttMilis---" + aaaStatisticsManager.getAaaStats().getRequestRttMilis());
             log.debug("UnknownServerRx---" + aaaStatisticsManager.getAaaStats().getUnknownServerRx());
             log.debug("UnknownTypeRx---" + aaaStatisticsManager.getAaaStats().getUnknownTypeRx());
+            log.debug("TimedOutPackets----" + aaaStatisticsManager.getAaaStats().getTimedOutPackets());
             log.debug("EapolLogoffRx---" + aaaStatisticsManager.getAaaStats().getEapolLogoffRx());
             log.debug("EapolAuthSuccessTrans---" + aaaStatisticsManager.getAaaStats().getEapolAuthSuccessTrans());
             log.debug("EapolAuthFailureTrans---" +
diff --git a/app/src/main/java/org/opencord/aaa/impl/StateMachine.java b/app/src/main/java/org/opencord/aaa/impl/StateMachine.java
index 9086846..5e9e140 100644
--- a/app/src/main/java/org/opencord/aaa/impl/StateMachine.java
+++ b/app/src/main/java/org/opencord/aaa/impl/StateMachine.java
@@ -17,16 +17,19 @@
 
 package org.opencord.aaa.impl;
 
-import com.google.common.collect.Maps;
+import static org.slf4j.LoggerFactory.getLogger;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
 import org.onlab.packet.MacAddress;
 import org.onosproject.net.ConnectPoint;
 import org.opencord.aaa.AuthenticationEvent;
 import org.opencord.aaa.StateMachineDelegate;
 import org.slf4j.Logger;
 
-import java.util.Map;
-
-import static org.slf4j.LoggerFactory.getLogger;
+import com.google.common.collect.Maps;
 
 /**
  * AAA Finite State Machine.
@@ -40,7 +43,13 @@
     static final int STATE_AUTHORIZED = 3;
     static final int STATE_UNAUTHORIZED = 4;
 
-    //INDEX to identify the transition in the transition table
+    // Defining the states where timeout can happen
+    static final Set<Integer> TIMEOUT_ELIGIBLE_STATES = new HashSet();
+    static {
+        TIMEOUT_ELIGIBLE_STATES.add(STATE_STARTED);
+        TIMEOUT_ELIGIBLE_STATES.add(STATE_PENDING);
+    }
+    // INDEX to identify the transition in the transition table
     static final int TRANSITION_START = 0; // --> started
     static final int TRANSITION_REQUEST_ACCESS = 1;
     static final int TRANSITION_AUTHORIZE_ACCESS = 2;
@@ -59,49 +68,54 @@
     private short vlanId;
     private byte priorityCode;
 
+    // Boolean flag indicating whether response is pending from AAA Server.
+    // Used for counting timeout happening for AAA Sessions due to no response.
+    private boolean waitingForRadiusResponse;
+
+    private static int cleanupTimerTimeOutInMins;
+
     private String sessionId = null;
 
     private final Logger log = getLogger(getClass());
 
-    private State[] states = {
-            new Idle(), new Started(), new Pending(), new Authorized(), new Unauthorized()
-    };
+    private State[] states = {new Idle(), new Started(), new Pending(), new Authorized(), new Unauthorized() };
 
+    // Cleanup Timer instance created for this session
+    private java.util.concurrent.ScheduledFuture<?> cleanupTimer = null;
 
-    //State transition table
+    // TimeStamp of last EAPOL or RADIUS message received.
+    private long lastPacketReceivedTime = 0;
+
+    // State transition table
     /*
-
-                state       IDLE    |   STARTED         |   PENDING         |   AUTHORIZED  |   UNAUTHORIZED
-             ////
-       input
-       ----------------------------------------------------------------------------------------------------
-
-       START                STARTED |   _               |   _               |   STARTED     |   STARTED
-
-       REQUEST_ACCESS       _       |   PENDING         |   _               |   _           |   _
-
-       AUTHORIZE_ACCESS     _       |   _               |   AUTHORIZED      |   _           |   _
-
-       DENY_ACCESS          _       |   -               |   UNAUTHORIZED    |   _           |   _
-
-       LOGOFF               _       |   _               |   _               |   IDLE        |   IDLE
+     *
+     * state IDLE | STARTED | PENDING | AUTHORIZED | UNAUTHORIZED //// input
+     * -----------------------------------------------------------------------------
+     * -----------------------
+     *
+     * START STARTED | _ | _ | STARTED | STARTED
+     *
+     * REQUEST_ACCESS _ | PENDING | _ | _ | _
+     *
+     * AUTHORIZE_ACCESS _ | _ | AUTHORIZED | _ | _
+     *
+     * DENY_ACCESS _ | - | UNAUTHORIZED | _ | _
+     *
+     * LOGOFF _ | _ | _ | IDLE | IDLE
      */
 
-    private int[] idleTransition =
-            {STATE_STARTED, STATE_IDLE, STATE_IDLE, STATE_IDLE, STATE_IDLE};
-    private int[] startedTransition =
-            {STATE_STARTED, STATE_PENDING, STATE_STARTED, STATE_STARTED, STATE_STARTED};
-    private int[] pendingTransition =
-            {STATE_PENDING, STATE_PENDING, STATE_AUTHORIZED, STATE_UNAUTHORIZED, STATE_PENDING};
-    private int[] authorizedTransition =
-            {STATE_STARTED, STATE_AUTHORIZED, STATE_AUTHORIZED, STATE_AUTHORIZED, STATE_IDLE};
-    private int[] unauthorizedTransition =
-            {STATE_STARTED, STATE_UNAUTHORIZED, STATE_UNAUTHORIZED, STATE_UNAUTHORIZED, STATE_IDLE};
+    private int[] idleTransition = {STATE_STARTED, STATE_IDLE, STATE_IDLE, STATE_IDLE, STATE_IDLE };
+    private int[] startedTransition = {STATE_STARTED, STATE_PENDING, STATE_STARTED, STATE_STARTED, STATE_STARTED };
+    private int[] pendingTransition = {STATE_PENDING, STATE_PENDING, STATE_AUTHORIZED, STATE_UNAUTHORIZED,
+            STATE_PENDING };
+    private int[] authorizedTransition = {STATE_STARTED, STATE_AUTHORIZED, STATE_AUTHORIZED, STATE_AUTHORIZED,
+            STATE_IDLE };
+    private int[] unauthorizedTransition = {STATE_STARTED, STATE_UNAUTHORIZED, STATE_UNAUTHORIZED, STATE_UNAUTHORIZED,
+            STATE_IDLE };
 
-    //THE TRANSITION TABLE
-    private int[][] transition =
-            {idleTransition, startedTransition, pendingTransition, authorizedTransition,
-                    unauthorizedTransition};
+    // THE TRANSITION TABLE
+    private int[][] transition = {idleTransition, startedTransition, pendingTransition, authorizedTransition,
+            unauthorizedTransition };
 
     private int currentState = STATE_IDLE;
 
@@ -127,6 +141,10 @@
         StateMachine.delegate = delegate;
     }
 
+    public static void setcleanupTimerTimeOutInMins(int cleanupTimerTimeoutInMins) {
+        cleanupTimerTimeOutInMins = cleanupTimerTimeoutInMins;
+    }
+
     public static void unsetDelegate(StateMachineDelegate delegate) {
         if (StateMachine.delegate == delegate) {
             StateMachine.delegate = null;
@@ -145,12 +163,37 @@
         return sessionIdMap.get(sessionId);
     }
 
+    public static void deleteStateMachineId(String sessionId) {
+        sessionIdMap.remove(sessionId);
+    }
+
     public static void deleteStateMachineMapping(StateMachine machine) {
         identifierMap.entrySet().removeIf(e -> e.getValue().equals(machine));
+        if (machine.cleanupTimer != null) {
+            machine.cleanupTimer.cancel(false);
+            machine.cleanupTimer = null;
+        }
+    }
+
+    public java.util.concurrent.ScheduledFuture<?> getCleanupTimer() {
+        return cleanupTimer;
+    }
+
+    public boolean isWaitingForRadiusResponse() {
+        return waitingForRadiusResponse;
+    }
+
+    public void setWaitingForRadiusResponse(boolean waitingForRadiusResponse) {
+        this.waitingForRadiusResponse = waitingForRadiusResponse;
+    }
+
+    public void setCleanupTimer(java.util.concurrent.ScheduledFuture<?> cleanupTimer) {
+        this.cleanupTimer = cleanupTimer;
     }
 
     /**
      * Deletes authentication state machine records for a given MAC address.
+     *
      * @param mac mac address of the suppliant who's state machine should be removed
      */
     public static void deleteByMac(MacAddress mac) {
@@ -161,8 +204,8 @@
             // If a MAC match is found then delete the entry from the session ID
             // and identifier map as well as call delete identifier to clean up
             // the identifier bit set.
-            if (e.getValue() != null && e.getValue().supplicantAddress != null &&
-                   e.getValue().supplicantAddress.equals(mac)) {
+            if (e.getValue() != null && e.getValue().supplicantAddress != null
+                    && e.getValue().supplicantAddress.equals(mac)) {
                 sessionIdMap.remove(e.getValue().sessionId);
                 if (e.getValue().identifier != -1) {
                     deleteStateMachineMapping(e.getValue());
@@ -175,7 +218,7 @@
     /**
      * Creates a new StateMachine with the given session ID.
      *
-     * @param sessionId session Id represented by the switch dpid +  port number
+     * @param sessionId session Id represented by the switch dpid + port number
      */
     public StateMachine(String sessionId) {
         log.info("Creating a new state machine for {}", sessionId);
@@ -220,6 +263,24 @@
     }
 
     /**
+     * Sets the lastPacketReceivedTime.
+     *
+     * @param lastPacketReceivedTime timelastPacket was received
+     */
+    public void setLastPacketReceivedTime(long lastPacketReceivedTime) {
+        this.lastPacketReceivedTime = lastPacketReceivedTime;
+    }
+
+    /**
+     * Gets the lastPacketReceivedTime.
+     *
+     * @return lastPacketReceivedTime
+     */
+    public long getLastPacketReceivedTime() {
+        return lastPacketReceivedTime;
+    }
+
+    /**
      * Gets the client's Vlan ID.
      *
      * @return client vlan ID
@@ -267,7 +328,8 @@
     /**
      * Set the challenge identifier and the state issued by the RADIUS.
      *
-     * @param challengeIdentifier The challenge identifier set into the EAP packet from the RADIUS message.
+     * @param challengeIdentifier The challenge identifier set into the EAP packet
+     *                            from the RADIUS message.
      * @param challengeState      The challenge state from the RADIUS.
      */
     protected void setChallengeInfo(byte challengeIdentifier, byte[] challengeState) {
@@ -276,9 +338,11 @@
     }
 
     /**
-     * Set the challenge identifier issued by the RADIUS on the access challenge request.
+     * Set the challenge identifier issued by the RADIUS on the access challenge
+     * request.
      *
-     * @param challengeIdentifier The challenge identifier set into the EAP packet from the RADIUS message.
+     * @param challengeIdentifier The challenge identifier set into the EAP packet
+     *                            from the RADIUS message.
      */
     protected void setChallengeIdentifier(byte challengeIdentifier) {
         log.info("Set Challenge Identifier to {}", challengeIdentifier);
@@ -294,7 +358,6 @@
         return this.challengeIdentifier;
     }
 
-
     /**
      * Set the challenge state info issued by the RADIUS.
      *
@@ -341,7 +404,6 @@
         this.requestAuthenticator = authenticator;
     }
 
-
     /**
      * Gets the username.
      *
@@ -380,83 +442,76 @@
     public void start() throws StateMachineException {
         states[currentState].start();
 
-        delegate.notify(new AuthenticationEvent(
-                AuthenticationEvent.Type.STARTED, supplicantConnectpoint));
+        delegate.notify(new AuthenticationEvent(AuthenticationEvent.Type.STARTED, supplicantConnectpoint));
 
-        //move to the next state
+        // move to the next state
         next(TRANSITION_START);
         identifier = this.identifier();
     }
 
     /**
-     * An Identification information has been sent by the supplicant.
-     * Move to the next state if possible.
+     * An Identification information has been sent by the supplicant. Move to the
+     * next state if possible.
      *
      * @throws StateMachineException if authentication protocol is violated
      */
     public void requestAccess() throws StateMachineException {
         states[currentState].requestAccess();
 
-        delegate.notify(new AuthenticationEvent(
-                AuthenticationEvent.Type.REQUESTED, supplicantConnectpoint));
+        delegate.notify(new AuthenticationEvent(AuthenticationEvent.Type.REQUESTED, supplicantConnectpoint));
 
-        //move to the next state
+        // move to the next state
         next(TRANSITION_REQUEST_ACCESS);
     }
 
     /**
-     * RADIUS has accepted the identification.
-     * Move to the next state if possible.
+     * RADIUS has accepted the identification. Move to the next state if possible.
      *
      * @throws StateMachineException if authentication protocol is violated
      */
     public void authorizeAccess() throws StateMachineException {
         states[currentState].radiusAccepted();
-        //move to the next state
+        // move to the next state
         next(TRANSITION_AUTHORIZE_ACCESS);
 
-        delegate.notify(new AuthenticationEvent(
-                AuthenticationEvent.Type.APPROVED, supplicantConnectpoint));
+        delegate.notify(new AuthenticationEvent(AuthenticationEvent.Type.APPROVED, supplicantConnectpoint));
 
         // Clear mapping
         deleteStateMachineMapping(this);
     }
 
     /**
-     * RADIUS has denied the identification.
-     * Move to the next state if possible.
+     * RADIUS has denied the identification. Move to the next state if possible.
      *
      * @throws StateMachineException if authentication protocol is violated
      */
     public void denyAccess() throws StateMachineException {
         states[currentState].radiusDenied();
-        //move to the next state
+        // move to the next state
         next(TRANSITION_DENY_ACCESS);
 
-        delegate.notify(new AuthenticationEvent(
-                AuthenticationEvent.Type.DENIED, supplicantConnectpoint));
+        delegate.notify(new AuthenticationEvent(AuthenticationEvent.Type.DENIED, supplicantConnectpoint));
 
         // Clear mappings
         deleteStateMachineMapping(this);
     }
 
     /**
-     * Logoff request has been requested.
-     * Move to the next state if possible.
+     * Logoff request has been requested. Move to the next state if possible.
      *
      * @throws StateMachineException if authentication protocol is violated
      */
     public void logoff() throws StateMachineException {
         states[currentState].logoff();
-        //move to the next state
+        // move to the next state
         next(TRANSITION_LOGOFF);
     }
 
     /**
      * Gets the current state.
      *
-     * @return The current state. Could be STATE_IDLE, STATE_STARTED, STATE_PENDING, STATE_AUTHORIZED,
-     * STATE_UNAUTHORIZED.
+     * @return The current state. Could be STATE_IDLE, STATE_STARTED, STATE_PENDING,
+     *         STATE_AUTHORIZED, STATE_UNAUTHORIZED.
      */
     public int state() {
         return currentState;
@@ -464,8 +519,8 @@
 
     @Override
     public String toString() {
-        return ("sessionId: " + this.sessionId) + "\t" + ("identifier: " + this.identifier) + "\t" +
-                ("state: " + this.currentState);
+        return ("sessionId: " + this.sessionId) + "\t" + ("identifier: " + this.identifier) + "\t"
+                + ("state: " + this.currentState);
     }
 
     abstract class State {
@@ -501,34 +556,40 @@
         private final Logger log = getLogger(getClass());
         private String name = "IDLE_STATE";
 
+        @Override
         public void start() {
             log.info("Moving from IDLE state to STARTED state.");
         }
     }
 
     /**
-     * Started state: supplicant has entered the network and informed the authenticator.
+     * Started state: supplicant has entered the network and informed the
+     * authenticator.
      */
     class Started extends State {
         private final Logger log = getLogger(getClass());
         private String name = "STARTED_STATE";
 
+        @Override
         public void requestAccess() {
             log.info("Moving from STARTED state to PENDING state.");
         }
     }
 
     /**
-     * Pending state: supplicant has been identified by the authenticator but has not access yet.
+     * Pending state: supplicant has been identified by the authenticator but has
+     * not access yet.
      */
     class Pending extends State {
         private final Logger log = getLogger(getClass());
         private String name = "PENDING_STATE";
 
+        @Override
         public void radiusAccepted() {
             log.info("Moving from PENDING state to AUTHORIZED state.");
         }
 
+        @Override
         public void radiusDenied() {
             log.info("Moving from PENDING state to UNAUTHORIZED state.");
         }
@@ -541,10 +602,12 @@
         private final Logger log = getLogger(getClass());
         private String name = "AUTHORIZED_STATE";
 
+        @Override
         public void start() {
             log.info("Moving from AUTHORIZED state to STARTED state.");
         }
 
+        @Override
         public void logoff() {
 
             log.info("Moving from AUTHORIZED state to IDLE state.");
@@ -558,14 +621,65 @@
         private final Logger log = getLogger(getClass());
         private String name = "UNAUTHORIZED_STATE";
 
+        @Override
         public void start() {
             log.info("Moving from UNAUTHORIZED state to STARTED state.");
         }
 
+        @Override
         public void logoff() {
             log.info("Moving from UNAUTHORIZED state to IDLE state.");
         }
     }
 
+    /**
+     * Class for cleaning the StateMachine for those session for which no response
+     * is coming--implementing timeout.
+     */
+    class CleanupTimerTask implements Runnable {
+        private final Logger log = getLogger(getClass());
+        private String sessionId;
+        private AaaManager aaaManager;
+
+        CleanupTimerTask(String sessionId, AaaManager aaaManager) {
+            this.sessionId = sessionId;
+            this.aaaManager = aaaManager;
+        }
+
+        @Override
+        public void run() {
+            StateMachine stateMachine = StateMachine.lookupStateMachineBySessionId(sessionId);
+            if (null != stateMachine) {
+                // Asserting if last packet received for this stateMachine session was beyond half of timeout period.
+                // StateMachine is considered eligible for cleanup when no packets has been exchanged by it with AAA
+                // Server or RG during a long period (half of timeout period). For example, when cleanup timer has
+                // been configured as 10 minutes, StateMachine would be cleaned up at the end of 10 minutes if
+                // the authentication is still pending and no packet was exchanged for this session during last 5
+                // minutes.
+
+                boolean noTrafficWithinThreshold = (System.currentTimeMillis()
+                        - stateMachine.getLastPacketReceivedTime()) > ((cleanupTimerTimeOutInMins * 60 * 1000) / 2);
+
+                        if ((TIMEOUT_ELIGIBLE_STATES.contains(stateMachine.state())) && noTrafficWithinThreshold) {
+                            log.info("Deleting StateMachineMapping for sessionId: {}", sessionId);
+                            cleanupTimer = null;
+                            if (stateMachine.state() == STATE_PENDING && stateMachine.isWaitingForRadiusResponse()) {
+                                aaaManager.aaaStatisticsManager.getAaaStats().increaseTimedOutPackets();
+                            }
+                            deleteStateMachineId(sessionId);
+                            deleteStateMachineMapping(stateMachine);
+
+                            // If StateMachine is not eligible for cleanup yet, reschedule cleanupTimer further.
+                        } else {
+                            aaaManager.scheduleStateMachineCleanupTimer(sessionId, stateMachine);
+                        }
+            } else {
+                // This statement should not be logged; cleanupTimer should be cancelled for stateMachine
+                // instances which have been authenticated successfully.
+                log.warn("state-machine not found for sessionId: {}", sessionId);
+            }
+
+        }
+    }
 
 }