Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8fb0faf3 authored by Android (Google) Code Review's avatar Android (Google) Code Review
Browse files

Merge change I0a56959e into eclair-mr2

* changes:
  Implement a HierarchicalStateMachine
parents b5ef7ee1 fc5b4802
Loading
Loading
Loading
Loading
+0 −33
Original line number Diff line number Diff line
/*
 * Copyright (C) 2006 The Android Open Source Project
 *
 * Licensed 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 android.os;

/**
 * {@hide}
 */
public abstract class HandlerState {
    public HandlerState() {
    }

    public void enter(Message message) {
    }

    public abstract void processMessage(Message message);

    public void exit(Message message) {
    }
}
+0 −290
Original line number Diff line number Diff line
/*
 * Copyright (C) 2006 The Android Open Source Project
 *
 * Licensed 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 android.os;

import android.util.Log;
import android.util.LogPrinter;

/**
 * {@hide}
 *
 * Implement a state machine where each state is an object,
 * HandlerState. Each HandlerState must implement processMessage
 * and optionally enter/exit. When a state machine is created
 * the initial state must be set. When messages are sent to
 * a state machine the current state's processMessage method is
 * invoked. If this is the first message for this state the
 * enter method is called prior to processMessage and when
 * transtionTo is invoked the state's exit method will be
 * called after returning from processMessage.
 *
 * If a message should be handled in a different state the
 * processMessage method may call deferMessage. This causes
 * the message to be saved on a list until transitioning
 * to a new state, at which time all of the deferred messages
 * will be put on the front of the state machines queue and
 * processed by the new current state's processMessage
 * method.
 *
 * Below is an example state machine with two state's, S1 and S2.
 * The initial state is S1 which defers all messages and only
 * transition to S2 when message.what == TEST_WHAT_2. State S2
 * will process each messages until it receives TEST_WHAT_2
 * where it will transition back to S1:
<code>
     class StateMachine1 extends HandlerStateMachine {
        private static final int TEST_WHAT_1 = 1;
        private static final int TEST_WHAT_2 = 2;

        StateMachine1(String name) {
            super(name);
            setInitialState(mS1);
        }

        class S1 extends HandlerState {
            &amp;#064;Override public void enter(Message message) {
            }

            &amp;#064;Override public void processMessage(Message message) {
                deferMessage(message);
                if (message.what == TEST_WHAT_2) {
                    transitionTo(mS2);
                }
            }

            &amp;#064;Override public void exit(Message message) {
            }
        }

        class S2 extends HandlerState {
            &amp;#064;Override public void processMessage(Message message) {
                // Do some processing
                if (message.what == TEST_WHAT_2) {
                    transtionTo(mS1);
                }
            }
        }

        private S1 mS1 = new S1();
        private S2 mS2 = new S2();
    }
</code>
 */
public class HandlerStateMachine {

    private boolean mDbg = false;
    private static final String TAG = "HandlerStateMachine";
    private String mName;
    private SmHandler mHandler;
    private HandlerThread mHandlerThread;

    /**
     * Handle messages sent to the state machine by calling
     * the current state's processMessage. It also handles
     * the enter/exit calls and placing any deferred messages
     * back onto the queue when transitioning to a new state.
     */
    class SmHandler extends Handler {

        SmHandler(Looper looper) {
          super(looper);
        }

        /**
         * This will dispatch the message to the
         * current state's processMessage.
         */
        @Override
        final public void handleMessage(Message msg) {
            if (mDbg) Log.d(TAG, "SmHandler.handleMessage E");
            if (mDestState != null) {
                if (mDbg) Log.d(TAG, "SmHandler.handleMessage; new destation call enter");
                mCurrentState = mDestState;
                mDestState = null;
                mCurrentState.enter(msg);
            }
            if (mCurrentState != null) {
                if (mDbg) Log.d(TAG, "SmHandler.handleMessage; call processMessage");
                mCurrentState.processMessage(msg);
            } else {
                /* Strange no state to execute */
                Log.e(TAG, "handleMessage: no current state, did you call setInitialState");
            }

            if (mDestState != null) {
                if (mDbg) Log.d(TAG, "SmHandler.handleMessage; new destination call exit");
                mCurrentState.exit(msg);

                /**
                 * Place the messages from the deferred queue:t
                 * on to the Handler's message queue in the
                 * same order that they originally arrived.
                 *
                 * We set cur.when = 0 to circumvent the check
                 * that this message has already been sent.
                 */
                while (mDeferredMessages != null) {
                    Message cur = mDeferredMessages;
                    mDeferredMessages = mDeferredMessages.next;
                    cur.when = 0;
                    if (mDbg) Log.d(TAG, "SmHandler.handleMessage; queue deferred message what="
                                                + cur.what + " target=" + cur.target);
                    sendMessageAtFrontOfQueue(cur);
                }
                if (mDbg) Log.d(TAG, "SmHandler.handleMessage X");
            }
        }

        public HandlerState mCurrentState;
        public HandlerState mDestState;
        public Message mDeferredMessages;
    }

    /**
     * Create an active StateMachine, one that has a
     * dedicated thread/looper/queue.
     */
    public HandlerStateMachine(String name) {
        mName = name;
        mHandlerThread =  new HandlerThread(name);
        mHandlerThread.start();
        mHandler = new SmHandler(mHandlerThread.getLooper());
    }

    /**
     * Get a message and set Message.target = this.
     */
    public final Message obtainMessage()
    {
        Message msg = Message.obtain(mHandler);
        if (mDbg) Log.d(TAG, "StateMachine.obtainMessage() EX target=" + msg.target);
        return msg;
    }

    /**
     * Get a message and set Message.target = this and
     * Message.what = what.
     */
    public final Message obtainMessage(int what) {
        Message msg = Message.obtain(mHandler, what);
        if (mDbg) {
            Log.d(TAG, "StateMachine.obtainMessage(what) EX what=" + msg.what +
                       " target=" + msg.target);
        }
        return msg;
    }

    /**
     * Enqueue a message to this state machine.
     */
    public final void sendMessage(Message msg) {
        if (mDbg) Log.d(TAG, "StateMachine.sendMessage EX msg.what=" + msg.what);
        mHandler.sendMessage(msg);
    }

    /**
     * Enqueue a message to this state machine after a delay.
     */
    public final void sendMessageDelayed(Message msg, long delayMillis) {
        if (mDbg) {
            Log.d(TAG, "StateMachine.sendMessageDelayed EX msg.what="
                            + msg.what + " delay=" + delayMillis);
        }
        mHandler.sendMessageDelayed(msg, delayMillis);
    }

    /**
     * Set the initial state. This must be invoked before
     * and messages are sent to the state machine.
     */
    public void setInitialState(HandlerState initialState) {
        if (mDbg) {
            Log.d(TAG, "StateMachine.setInitialState EX initialState"
                            + initialState.getClass().getName());
        }
        mHandler.mDestState = initialState;
    }

    /**
     * transition to destination state. Upon returning
     * from processMessage the current state's exit will
     * be executed and upon the next message arriving
     * destState.enter will be invoked.
     */
    final public void transitionTo(HandlerState destState) {
        if (mDbg) {
            Log.d(TAG, "StateMachine.transitionTo EX destState"
                            + destState.getClass().getName());
        }
        mHandler.mDestState = destState;
    }

    /**
     * Defer this message until next state transition.
     * Upon transitioning all deferred messages will be
     * placed on the queue and reprocessed in the original
     * order. (i.e. The next state the oldest messages will
     * be processed first)
     */
    final public void deferMessage(Message msg) {
        if (mDbg) {
            Log.d(TAG, "StateMachine.deferMessage EX mDeferredMessages="
                            + mHandler.mDeferredMessages);
        }

        /* Copy the "msg" to "newMsg" as "msg" will be recycled */
        Message newMsg = obtainMessage();
        newMsg.copyFrom(msg);

        /* Place on front of queue */
        newMsg.next = mHandler.mDeferredMessages;
        mHandler.mDeferredMessages = newMsg;
    }

    /**
     * @return the name
     */
    public String getName() {
        return mName;
    }

    /**
     * @return Handler
     */
    public Handler getHandler() {
        return mHandler;
    }

    /**
     * @return if debugging is enabled
     */
    public boolean isDbg() {
        return mDbg;
    }

    /**
     * Set debug enable/disabled.
     */
    public void setDbg(boolean dbg) {
        mDbg = dbg;
        if (mDbg) {
            mHandlerThread.getLooper().setMessageLogging(new LogPrinter(Log.VERBOSE, TAG));
        } else {
            mHandlerThread.getLooper().setMessageLogging(null);
        }
   }
}
+75 −0
Original line number Diff line number Diff line
/**
 * Copyright (C) 2009 The Android Open Source Project
 *
 * Licensed 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 com.android.internal.util;

import android.os.Message;

/**
 * {@hide}
 *
 * The abstract class for implementing states in a
 * HierarchicalStateMachine and HandlerStateMachine.
 */
public abstract class HierarchicalState {

    /**
     * Constructor
     */
    protected HierarchicalState() {
    }

    /**
     * Called when a state is entered.
     */
    protected void enter() {
    }

    /**
     * Called when a message is to be processed by the
     * state machine.
     *
     * This routine is never reentered thus no synchronization
     * is needed as only one processMessage method will ever be
     * executing within a state machine at any given time. This
     * does mean that processing by this routine must be completed
     * as expeditiously as possible as no subsequent messages will
     * be processed until this routine returns.
     *
     * @param msg to process
     * @return true if processing has completed and false
     *         if the parent state's processMessage should
     *         be invoked.
     */
    abstract protected boolean processMessage(Message msg);

    /**
     * Called when a state is exited.
     */
    protected void exit() {
    }

    /**
     * @return name of state, but default returns the states
     * class name. An instance name would be better but requiring
     * it seems unnecessary.
     */
    public String getName() {
        String name = getClass().getName();
        int lastDollar = name.lastIndexOf('$');
        return name.substring(lastDollar + 1);
    }
}
+1164 −0

File added.

Preview size limit exceeded, changes collapsed.

+198 −0
Original line number Diff line number Diff line
/**
 * Copyright (C) 2009 The Android Open Source Project
 *
 * Licensed 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 com.android.internal.util;

import android.os.Message;

import java.util.Vector;

/**
 * {@hide}
 *
 * A list of messages recently processed by the state machine.
 *
 * The class maintains a list of messages that have been most
 * recently processed. The list is finite and may be set in the
 * constructor or by calling setSize. The public interface also
 * includes size which returns the number of recent messages,
 * count which is the number of message processed since the
 * the last setSize, get which returns a processed message and
 * add which adds a processed messaged.
 */
public class ProcessedMessages {

    public static final int DEFAULT_SIZE = 20;

    /**
     * The information maintained for a processed message.
     */
    public class Info {
        private int what;
        private HierarchicalState state;
        private HierarchicalState orgState;

        /**
         * Constructor
         * @param message
         * @param state that handled the message
         * @param orgState is the first state the received the message but
         * did not processes the message.
         */
        Info(Message message, HierarchicalState state, HierarchicalState orgState) {
            update(message, state, orgState);
        }

        /**
         * Update the information in the record.
         * @param state that handled the message
         * @param orgState is the first state the received the message but
         * did not processes the message.
         */
        public void update(Message message, HierarchicalState state, HierarchicalState orgState) {
            this.what = message.what;
            this.state = state;
            this.orgState = orgState;
        }

        /**
         * @return the command that was executing
         */
        public int getWhat() {
            return what;
        }

        /**
         * @return the state that handled this message
         */
        public HierarchicalState getState() {
            return state;
        }

        /**
         * @return the original state that received the message.
         */
        public HierarchicalState getOriginalState() {
            return orgState;
        }

        /**
         * @return as string
         */
        public String toString() {
            StringBuilder sb = new StringBuilder();
            sb.append("what=");
            sb.append(what);
            sb.append(" state=");
            sb.append(cn(state));
            sb.append(" orgState=");
            sb.append(cn(orgState));
            return sb.toString();
        }

        /**
         * @return an objects class name
         */
        private String cn(Object n) {
            if (n == null) {
                return "null";
            } else {
                String name = n.getClass().getName();
                int lastDollar = name.lastIndexOf('$');
                return name.substring(lastDollar + 1);
            }
        }
    }

    private Vector<Info> mMessages = new Vector<Info>();
    private int mMaxSize = DEFAULT_SIZE;
    private int mOldestIndex = 0;
    private int mCount = 0;

    /**
     * Constructor
     */
    ProcessedMessages() {
    }

    ProcessedMessages(int maxSize) {
        setSize(maxSize);
    }

    /**
     * Set size of messages to maintain and clears all current messages.
     *
     * @param maxSize number of messages to maintain at anyone time.
    */
    void setSize(int maxSize) {
        mMaxSize = maxSize;
        mCount = 0;
        mMessages.clear();
    }

    /**
     * @return the number of recent messages.
     */
    int size() {
        return mMessages.size();
    }

    /**
     * @return the total number of messages processed since size was set.
     */
    int count() {
        return mCount;
    }

    /**
     * @return the information on a particular record. 0 is the oldest
     * record and size()-1 is the newest record. If the index is to
     * large null is returned.
     */
    Info get(int index) {
        int nextIndex = mOldestIndex + index;
        if (nextIndex >= mMaxSize) {
            nextIndex -= mMaxSize;
        }
        if (nextIndex >= size()) {
            return null;
        } else {
            return mMessages.get(nextIndex);
        }
    }

    /**
     * Add a processed message.
     *
     * @param message
     * @param state that handled the message
     * @param orgState is the first state the received the message but
     * did not processes the message.
     */
    void add(Message message, HierarchicalState state, HierarchicalState orgState) {
        mCount += 1;
        if (mMessages.size() < mMaxSize) {
            mMessages.add(new Info(message, state, orgState));
        } else {
            Info info = mMessages.get(mOldestIndex);
            mOldestIndex += 1;
            if (mOldestIndex >= mMaxSize) {
                mOldestIndex = 0;
            }
            info.update(message, state, orgState);
        }
    }
}
Loading