Home | History | Annotate | Download | only in hdmi
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package com.android.server.hdmi;
     17 
     18 import android.os.Handler;
     19 import android.os.Looper;
     20 import android.os.Message;
     21 import android.util.Pair;
     22 import android.util.Slog;
     23 
     24 import com.android.internal.annotations.VisibleForTesting;
     25 import com.android.server.hdmi.HdmiControlService.DevicePollingCallback;
     26 
     27 import java.util.ArrayList;
     28 import java.util.List;
     29 
     30 /**
     31  * Encapsulates a sequence of CEC command exchange for a certain feature.
     32  * <p>
     33  * Many CEC features are accomplished by CEC devices on the bus exchanging more than one
     34  * command. {@link HdmiCecFeatureAction} represents the life cycle of the communication, manages the
     35  * state as the process progresses, and if necessary, returns the result to the caller which
     36  * initiates the action, through the callback given at the creation of the object. All the actual
     37  * action classes inherit FeatureAction.
     38  * <p>
     39  * More than one FeatureAction objects can be up and running simultaneously, maintained by
     40  * {@link HdmiCecLocalDevice}. Each action is passed a new command arriving from the bus, and either
     41  * consumes it if the command is what the action expects, or yields it to other action. Declared as
     42  * package private, accessed by {@link HdmiControlService} only.
     43  */
     44 abstract class HdmiCecFeatureAction {
     45     private static final String TAG = "HdmiCecFeatureAction";
     46 
     47     // Timer handler message used for timeout event
     48     protected static final int MSG_TIMEOUT = 100;
     49 
     50     // Default state used in common by all the feature actions.
     51     protected static final int STATE_NONE = 0;
     52 
     53     // Internal state indicating the progress of action.
     54     protected int mState = STATE_NONE;
     55 
     56     private final HdmiControlService mService;
     57     private final HdmiCecLocalDevice mSource;
     58 
     59     // Timer that manages timeout events.
     60     protected ActionTimer mActionTimer;
     61 
     62     private ArrayList<Pair<HdmiCecFeatureAction, Runnable>> mOnFinishedCallbacks;
     63 
     64     HdmiCecFeatureAction(HdmiCecLocalDevice source) {
     65         mSource = source;
     66         mService = mSource.getService();
     67         mActionTimer = createActionTimer(mService.getServiceLooper());
     68     }
     69 
     70     @VisibleForTesting
     71     void setActionTimer(ActionTimer actionTimer) {
     72         mActionTimer = actionTimer;
     73     }
     74 
     75     /**
     76      * Called after the action is created. Initialization or first step to take
     77      * for the action can be done in this method. Shall update {@code mState} to
     78      * indicate that the action has started.
     79      *
     80      * @return true if the operation is successful; otherwise false.
     81      */
     82     abstract boolean start();
     83 
     84     /**
     85      * Process the command. Called whenever a new command arrives.
     86      *
     87      * @param cmd command to process
     88      * @return true if the command was consumed in the process; Otherwise false.
     89      */
     90     abstract boolean processCommand(HdmiCecMessage cmd);
     91 
     92     /**
     93      * Called when the action should handle the timer event it created before.
     94      *
     95      * <p>CEC standard mandates each command transmission should be responded within
     96      * certain period of time. The method is called when the timer it created as it transmitted
     97      * a command gets expired. Inner logic should take an appropriate action.
     98      *
     99      * @param state the state associated with the time when the timer was created
    100      */
    101     abstract void handleTimerEvent(int state);
    102 
    103     /**
    104      * Timer handler interface used for FeatureAction classes.
    105      */
    106     interface ActionTimer {
    107         /**
    108          * Send a timer message.
    109          *
    110          * Also carries the state of the action when the timer is created. Later this state is
    111          * compared to the one the action is in when it receives the timer to let the action tell
    112          * the right timer to handle.
    113          *
    114          * @param state state of the action is in
    115          * @param delayMillis amount of delay for the timer
    116          */
    117         void sendTimerMessage(int state, long delayMillis);
    118 
    119         /**
    120          * Removes any pending timer message.
    121          */
    122         void clearTimerMessage();
    123     }
    124 
    125     private class ActionTimerHandler extends Handler implements ActionTimer {
    126 
    127         public ActionTimerHandler(Looper looper) {
    128             super(looper);
    129         }
    130 
    131         @Override
    132         public void sendTimerMessage(int state, long delayMillis) {
    133             // The third argument(0) is not used.
    134             sendMessageDelayed(obtainMessage(MSG_TIMEOUT, state, 0), delayMillis);
    135         }
    136 
    137         @Override
    138         public void clearTimerMessage() {
    139             removeMessages(MSG_TIMEOUT);
    140         }
    141 
    142         @Override
    143         public void handleMessage(Message msg) {
    144             switch (msg.what) {
    145             case MSG_TIMEOUT:
    146                 handleTimerEvent(msg.arg1);
    147                 break;
    148             default:
    149                 Slog.w(TAG, "Unsupported message:" + msg.what);
    150                 break;
    151             }
    152         }
    153     }
    154 
    155     private ActionTimer createActionTimer(Looper looper) {
    156         return new ActionTimerHandler(looper);
    157     }
    158 
    159     // Add a new timer. The timer event will come to mActionTimer.handleMessage() in
    160     // delayMillis.
    161     protected void addTimer(int state, int delayMillis) {
    162         mActionTimer.sendTimerMessage(state, delayMillis);
    163     }
    164 
    165     boolean started() {
    166         return mState != STATE_NONE;
    167     }
    168 
    169     protected final void sendCommand(HdmiCecMessage cmd) {
    170         mService.sendCecCommand(cmd);
    171     }
    172 
    173     protected final void sendCommand(HdmiCecMessage cmd,
    174             HdmiControlService.SendMessageCallback callback) {
    175         mService.sendCecCommand(cmd, callback);
    176     }
    177 
    178     protected final void addAndStartAction(HdmiCecFeatureAction action) {
    179         mSource.addAndStartAction(action);
    180     }
    181 
    182     protected final <T extends HdmiCecFeatureAction> List<T> getActions(final Class<T> clazz) {
    183         return mSource.getActions(clazz);
    184     }
    185 
    186     protected final HdmiCecMessageCache getCecMessageCache() {
    187         return mSource.getCecMessageCache();
    188     }
    189 
    190     /**
    191      * Remove the action from the action queue. This is called after the action finishes
    192      * its role.
    193      *
    194      * @param action
    195      */
    196     protected final void removeAction(HdmiCecFeatureAction action) {
    197         mSource.removeAction(action);
    198     }
    199 
    200     protected final <T extends HdmiCecFeatureAction> void removeAction(final Class<T> clazz) {
    201         mSource.removeActionExcept(clazz, null);
    202     }
    203 
    204     protected final <T extends HdmiCecFeatureAction> void removeActionExcept(final Class<T> clazz,
    205             final HdmiCecFeatureAction exception) {
    206         mSource.removeActionExcept(clazz, exception);
    207     }
    208 
    209     protected final void pollDevices(DevicePollingCallback callback, int pickStrategy,
    210             int retryCount) {
    211         mService.pollDevices(callback, getSourceAddress(), pickStrategy, retryCount);
    212     }
    213 
    214     /**
    215      * Clean up action's state.
    216      *
    217      * <p>Declared as package-private. Only {@link HdmiControlService} can access it.
    218      */
    219     void clear() {
    220         mState = STATE_NONE;
    221         // Clear all timers.
    222         mActionTimer.clearTimerMessage();
    223     }
    224 
    225     /**
    226      * Finish up the action. Reset the state, and remove itself from the action queue.
    227      */
    228     protected void finish() {
    229         finish(true);
    230     }
    231 
    232     void finish(boolean removeSelf) {
    233         clear();
    234         if (removeSelf) {
    235             removeAction(this);
    236         }
    237         if (mOnFinishedCallbacks != null) {
    238             for (Pair<HdmiCecFeatureAction, Runnable> actionCallbackPair: mOnFinishedCallbacks) {
    239                 if (actionCallbackPair.first.mState != STATE_NONE) {
    240                     actionCallbackPair.second.run();
    241                 }
    242             }
    243             mOnFinishedCallbacks = null;
    244         }
    245     }
    246 
    247     protected final HdmiCecLocalDevice localDevice() {
    248         return mSource;
    249     }
    250 
    251     protected final HdmiCecLocalDevicePlayback playback() {
    252         return (HdmiCecLocalDevicePlayback) mSource;
    253     }
    254 
    255     protected final HdmiCecLocalDeviceTv tv() {
    256         return (HdmiCecLocalDeviceTv) mSource;
    257     }
    258 
    259     protected final int getSourceAddress() {
    260         return mSource.getDeviceInfo().getLogicalAddress();
    261     }
    262 
    263     protected final int getSourcePath() {
    264         return mSource.getDeviceInfo().getPhysicalAddress();
    265     }
    266 
    267     protected final void sendUserControlPressedAndReleased(int targetAddress, int uiCommand) {
    268         mSource.sendUserControlPressedAndReleased(targetAddress, uiCommand);
    269     }
    270 
    271     protected final void addOnFinishedCallback(HdmiCecFeatureAction action, Runnable runnable) {
    272         if (mOnFinishedCallbacks == null) {
    273             mOnFinishedCallbacks = new ArrayList<>();
    274         }
    275         mOnFinishedCallbacks.add(Pair.create(action, runnable));
    276     }
    277 }
    278