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 
     17 package com.android.server.hdmi;
     18 
     19 import android.hardware.hdmi.HdmiControlManager;
     20 import android.hardware.hdmi.HdmiDeviceInfo;
     21 import android.hardware.hdmi.IHdmiControlCallback;
     22 import android.os.RemoteException;
     23 import android.os.SystemProperties;
     24 import android.util.Slog;
     25 
     26 import com.android.internal.util.IndentingPrintWriter;
     27 import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
     28 
     29 /**
     30  * Represent a logical device of type Playback residing in Android system.
     31  */
     32 final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDevice {
     33     private static final String TAG = "HdmiCecLocalDevicePlayback";
     34 
     35     private boolean mIsActiveSource = false;
     36 
     37     HdmiCecLocalDevicePlayback(HdmiControlService service) {
     38         super(service, HdmiDeviceInfo.DEVICE_PLAYBACK);
     39     }
     40 
     41     @Override
     42     void init() {
     43         super.init();
     44         mIsActiveSource = false;
     45     }
     46 
     47     @Override
     48     @ServiceThreadOnly
     49     protected void onAddressAllocated(int logicalAddress, int reason) {
     50         assertRunOnServiceThread();
     51         mService.sendCecCommand(HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
     52                 mAddress, mService.getPhysicalAddress(), mDeviceType));
     53         startQueuedActions();
     54     }
     55 
     56     @Override
     57     @ServiceThreadOnly
     58     protected int getPreferredAddress() {
     59         assertRunOnServiceThread();
     60         return SystemProperties.getInt(Constants.PROPERTY_PREFERRED_ADDRESS_PLAYBACK,
     61                 Constants.ADDR_UNREGISTERED);
     62     }
     63 
     64     @Override
     65     @ServiceThreadOnly
     66     protected void setPreferredAddress(int addr) {
     67         assertRunOnServiceThread();
     68         SystemProperties.set(Constants.PROPERTY_PREFERRED_ADDRESS_PLAYBACK,
     69                 String.valueOf(addr));
     70     }
     71 
     72     @ServiceThreadOnly
     73     void oneTouchPlay(IHdmiControlCallback callback) {
     74         assertRunOnServiceThread();
     75         if (hasAction(OneTouchPlayAction.class)) {
     76             Slog.w(TAG, "oneTouchPlay already in progress");
     77             invokeCallback(callback, HdmiControlManager.RESULT_ALREADY_IN_PROGRESS);
     78             return;
     79         }
     80 
     81         // TODO: Consider the case of multiple TV sets. For now we always direct the command
     82         //       to the primary one.
     83         OneTouchPlayAction action = OneTouchPlayAction.create(this, Constants.ADDR_TV,
     84                 callback);
     85         if (action == null) {
     86             Slog.w(TAG, "Cannot initiate oneTouchPlay");
     87             invokeCallback(callback, HdmiControlManager.RESULT_EXCEPTION);
     88             return;
     89         }
     90         addAndStartAction(action);
     91     }
     92 
     93     @ServiceThreadOnly
     94     void queryDisplayStatus(IHdmiControlCallback callback) {
     95         assertRunOnServiceThread();
     96         if (hasAction(DevicePowerStatusAction.class)) {
     97             Slog.w(TAG, "queryDisplayStatus already in progress");
     98             invokeCallback(callback, HdmiControlManager.RESULT_ALREADY_IN_PROGRESS);
     99             return;
    100         }
    101         DevicePowerStatusAction action = DevicePowerStatusAction.create(this,
    102                 Constants.ADDR_TV, callback);
    103         if (action == null) {
    104             Slog.w(TAG, "Cannot initiate queryDisplayStatus");
    105             invokeCallback(callback, HdmiControlManager.RESULT_EXCEPTION);
    106             return;
    107         }
    108         addAndStartAction(action);
    109     }
    110 
    111     @ServiceThreadOnly
    112     private void invokeCallback(IHdmiControlCallback callback, int result) {
    113         assertRunOnServiceThread();
    114         try {
    115             callback.onComplete(result);
    116         } catch (RemoteException e) {
    117             Slog.e(TAG, "Invoking callback failed:" + e);
    118         }
    119     }
    120 
    121     @Override
    122     @ServiceThreadOnly
    123     void onHotplug(int portId, boolean connected) {
    124         assertRunOnServiceThread();
    125         mCecMessageCache.flushAll();
    126         // We'll not clear mIsActiveSource on the hotplug event to pass CETC 11.2.2-2 ~ 3.
    127         if (connected && mService.isPowerStandbyOrTransient()) {
    128             mService.wakeUp();
    129         }
    130     }
    131 
    132     @ServiceThreadOnly
    133     void markActiveSource() {
    134         assertRunOnServiceThread();
    135         mIsActiveSource = true;
    136     }
    137 
    138     @Override
    139     @ServiceThreadOnly
    140     protected boolean handleActiveSource(HdmiCecMessage message) {
    141         assertRunOnServiceThread();
    142         int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams());
    143         mayResetActiveSource(physicalAddress);
    144         return true;  // Broadcast message.
    145     }
    146 
    147     private void mayResetActiveSource(int physicalAddress) {
    148         if (physicalAddress != mService.getPhysicalAddress()) {
    149             mIsActiveSource = false;
    150         }
    151     }
    152 
    153     @Override
    154     @ServiceThreadOnly
    155     protected boolean handleSetStreamPath(HdmiCecMessage message) {
    156         assertRunOnServiceThread();
    157         int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams());
    158         maySetActiveSource(physicalAddress);
    159         maySendActiveSource();
    160         wakeUpIfActiveSource();
    161         return true;  // Broadcast message.
    162     }
    163 
    164     // Samsung model, we tested, sends <RoutingChange> and <RequestActiveSource> consecutively,
    165     // Then if there is no <ActiveSource> response, it will change the input to
    166     // the internal source.  To handle this, we'll set ActiveSource aggressively.
    167     @Override
    168     @ServiceThreadOnly
    169     protected boolean handleRoutingChange(HdmiCecMessage message) {
    170         assertRunOnServiceThread();
    171         int newPath = HdmiUtils.twoBytesToInt(message.getParams(), 2);
    172         maySetActiveSource(newPath);
    173         return true;  // Broadcast message.
    174     }
    175 
    176     @Override
    177     @ServiceThreadOnly
    178     protected boolean handleRoutingInformation(HdmiCecMessage message) {
    179         assertRunOnServiceThread();
    180         int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams());
    181         maySetActiveSource(physicalAddress);
    182         return true;  // Broadcast message.
    183     }
    184 
    185     private void maySetActiveSource(int physicalAddress) {
    186         if (physicalAddress == mService.getPhysicalAddress()) {
    187             mIsActiveSource = true;
    188         } else {
    189             mIsActiveSource = false;
    190         }
    191     }
    192 
    193     private void wakeUpIfActiveSource() {
    194         if (mIsActiveSource && mService.isPowerStandbyOrTransient()) {
    195             mService.wakeUp();
    196         }
    197     }
    198 
    199     private void maySendActiveSource() {
    200         if (mIsActiveSource) {
    201             mService.sendCecCommand(HdmiCecMessageBuilder.buildActiveSource(
    202                     mAddress, mService.getPhysicalAddress()));
    203         }
    204     }
    205 
    206     @Override
    207     @ServiceThreadOnly
    208     protected boolean handleRequestActiveSource(HdmiCecMessage message) {
    209         assertRunOnServiceThread();
    210         maySendActiveSource();
    211         return true;  // Broadcast message.
    212     }
    213 
    214     @Override
    215     @ServiceThreadOnly
    216     protected void disableDevice(boolean initiatedByCec, PendingActionClearedCallback callback) {
    217         super.disableDevice(initiatedByCec, callback);
    218 
    219         assertRunOnServiceThread();
    220         if (!initiatedByCec && mIsActiveSource) {
    221             mService.sendCecCommand(HdmiCecMessageBuilder.buildInactiveSource(
    222                     mAddress, mService.getPhysicalAddress()));
    223         }
    224         mIsActiveSource = false;
    225         checkIfPendingActionsCleared();
    226     }
    227 
    228     @Override
    229     protected void dump(final IndentingPrintWriter pw) {
    230         super.dump(pw);
    231         pw.println("mIsActiveSource: " + mIsActiveSource);
    232     }
    233 }