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 android.hardware.hdmi;
     17 
     18 import android.annotation.NonNull;
     19 import android.annotation.SystemApi;
     20 import android.hardware.hdmi.HdmiRecordSources.RecordSource;
     21 import android.hardware.hdmi.HdmiTimerRecordSources.TimerRecordSource;
     22 import android.os.RemoteException;
     23 import android.util.Log;
     24 
     25 import libcore.util.EmptyArray;
     26 
     27 import java.util.Collections;
     28 import java.util.List;
     29 
     30 /**
     31  * HdmiTvClient represents HDMI-CEC logical device of type TV in the Android system
     32  * which acts as TV/Display. It provides with methods that manage, interact with other
     33  * devices on the CEC bus.
     34  *
     35  * @hide
     36  */
     37 @SystemApi
     38 public final class HdmiTvClient extends HdmiClient {
     39     private static final String TAG = "HdmiTvClient";
     40 
     41     /**
     42      * Size of MHL register for vendor command
     43      */
     44     public static final int VENDOR_DATA_SIZE = 16;
     45 
     46     /* package */ HdmiTvClient(IHdmiControlService service) {
     47         super(service);
     48     }
     49 
     50     // Factory method for HdmiTvClient.
     51     // Declared package-private. Accessed by HdmiControlManager only.
     52     /* package */ static HdmiTvClient create(IHdmiControlService service) {
     53         return new HdmiTvClient(service);
     54     }
     55 
     56     @Override
     57     public int getDeviceType() {
     58         return HdmiDeviceInfo.DEVICE_TV;
     59     }
     60 
     61     /**
     62      * Callback interface used to get the result of {@link #deviceSelect}.
     63      */
     64     public interface SelectCallback {
     65         /**
     66          * Called when the operation is finished.
     67          *
     68          * @param result the result value of {@link #deviceSelect}
     69          */
     70         void onComplete(int result);
     71     }
     72 
     73     /**
     74      * Selects a CEC logical device to be a new active source.
     75      *
     76      * @param logicalAddress logical address of the device to select
     77      * @param callback callback to get the result with
     78      * @throws {@link IllegalArgumentException} if the {@code callback} is null
     79      */
     80     public void deviceSelect(int logicalAddress, @NonNull SelectCallback callback) {
     81         if (callback == null) {
     82             throw new IllegalArgumentException("callback must not be null.");
     83         }
     84         try {
     85             mService.deviceSelect(logicalAddress, getCallbackWrapper(callback));
     86         } catch (RemoteException e) {
     87             Log.e(TAG, "failed to select device: ", e);
     88         }
     89     }
     90 
     91     private static IHdmiControlCallback getCallbackWrapper(final SelectCallback callback) {
     92         return new IHdmiControlCallback.Stub() {
     93             @Override
     94             public void onComplete(int result) {
     95                 callback.onComplete(result);
     96             }
     97         };
     98     }
     99 
    100     /**
    101      * Selects a HDMI port to be a new route path.
    102      *
    103      * @param portId HDMI port to select
    104      * @param callback callback to get the result with
    105      * @throws {@link IllegalArgumentException} if the {@code callback} is null
    106      */
    107     public void portSelect(int portId, @NonNull SelectCallback callback) {
    108         if (callback == null) {
    109             throw new IllegalArgumentException("Callback must not be null");
    110         }
    111         try {
    112             mService.portSelect(portId, getCallbackWrapper(callback));
    113         } catch (RemoteException e) {
    114             Log.e(TAG, "failed to select port: ", e);
    115         }
    116     }
    117 
    118     /**
    119      * Callback interface used to get the input change event.
    120      */
    121     public interface InputChangeListener {
    122         /**
    123          * Called when the input was changed.
    124          *
    125          * @param info newly selected HDMI input
    126          */
    127         void onChanged(HdmiDeviceInfo info);
    128     }
    129 
    130     /**
    131      * Sets the listener used to get informed of the input change event.
    132      *
    133      * @param listener listener object
    134      */
    135     public void setInputChangeListener(InputChangeListener listener) {
    136         if (listener == null) {
    137             throw new IllegalArgumentException("listener must not be null.");
    138         }
    139         try {
    140             mService.setInputChangeListener(getListenerWrapper(listener));
    141         } catch (RemoteException e) {
    142             Log.e("TAG", "Failed to set InputChangeListener:", e);
    143         }
    144     }
    145 
    146     private static IHdmiInputChangeListener getListenerWrapper(final InputChangeListener listener) {
    147         return new IHdmiInputChangeListener.Stub() {
    148             @Override
    149             public void onChanged(HdmiDeviceInfo info) {
    150                 listener.onChanged(info);
    151             }
    152         };
    153     }
    154 
    155     /**
    156      * Returns all the CEC devices connected to TV.
    157      *
    158      * @return list of {@link HdmiDeviceInfo} for connected CEC devices.
    159      *         Empty list is returned if there is none.
    160      */
    161     public List<HdmiDeviceInfo> getDeviceList() {
    162         try {
    163             return mService.getDeviceList();
    164         } catch (RemoteException e) {
    165             Log.e("TAG", "Failed to call getDeviceList():", e);
    166             return Collections.<HdmiDeviceInfo>emptyList();
    167         }
    168     }
    169 
    170     /**
    171      * Sets system audio volume
    172      *
    173      * @param oldIndex current volume index
    174      * @param newIndex volume index to be set
    175      * @param maxIndex maximum volume index
    176      */
    177     public void setSystemAudioVolume(int oldIndex, int newIndex, int maxIndex) {
    178         try {
    179             mService.setSystemAudioVolume(oldIndex, newIndex, maxIndex);
    180         } catch (RemoteException e) {
    181             Log.e(TAG, "failed to set volume: ", e);
    182         }
    183     }
    184 
    185     /**
    186      * Sets system audio mute status
    187      *
    188      * @param mute {@code true} if muted; otherwise, {@code false}
    189      */
    190     public void setSystemAudioMute(boolean mute) {
    191         try {
    192             mService.setSystemAudioMute(mute);
    193         } catch (RemoteException e) {
    194             Log.e(TAG, "failed to set mute: ", e);
    195         }
    196     }
    197 
    198     /**
    199      * Sets record listener
    200      *
    201      * @param listener
    202      */
    203     public void setRecordListener(@NonNull HdmiRecordListener listener) {
    204         if (listener == null) {
    205             throw new IllegalArgumentException("listener must not be null.");
    206         }
    207         try {
    208             mService.setHdmiRecordListener(getListenerWrapper(listener));
    209         } catch (RemoteException e) {
    210             Log.e(TAG, "failed to set record listener.", e);
    211         }
    212     }
    213 
    214     private static IHdmiRecordListener getListenerWrapper(final HdmiRecordListener callback) {
    215         return new IHdmiRecordListener.Stub() {
    216             @Override
    217             public byte[] getOneTouchRecordSource(int recorderAddress) {
    218                 HdmiRecordSources.RecordSource source =
    219                         callback.onOneTouchRecordSourceRequested(recorderAddress);
    220                 if (source == null) {
    221                     return EmptyArray.BYTE;
    222                 }
    223                 byte[] data = new byte[source.getDataSize(true)];
    224                 source.toByteArray(true, data, 0);
    225                 return data;
    226             }
    227 
    228             @Override
    229             public void onOneTouchRecordResult(int recorderAddress, int result) {
    230                 callback.onOneTouchRecordResult(recorderAddress, result);
    231             }
    232 
    233             @Override
    234             public void onTimerRecordingResult(int recorderAddress, int result) {
    235                 callback.onTimerRecordingResult(recorderAddress,
    236                         HdmiRecordListener.TimerStatusData.parseFrom(result));
    237             }
    238 
    239             @Override
    240             public void onClearTimerRecordingResult(int recorderAddress, int result) {
    241                 callback.onClearTimerRecordingResult(recorderAddress, result);
    242             }
    243         };
    244     }
    245 
    246     /**
    247      * Starts one touch recording with the given recorder address and recorder source.
    248      * <p>
    249      * Usage
    250      * <pre>
    251      * HdmiTvClient tvClient = ....;
    252      * // for own source.
    253      * OwnSource ownSource = HdmiRecordSources.ofOwnSource();
    254      * tvClient.startOneTouchRecord(recorderAddress, ownSource);
    255      * </pre>
    256      */
    257     public void startOneTouchRecord(int recorderAddress, @NonNull RecordSource source) {
    258         if (source == null) {
    259             throw new IllegalArgumentException("source must not be null.");
    260         }
    261 
    262         try {
    263             byte[] data = new byte[source.getDataSize(true)];
    264             source.toByteArray(true, data, 0);
    265             mService.startOneTouchRecord(recorderAddress, data);
    266         } catch (RemoteException e) {
    267             Log.e(TAG, "failed to start record: ", e);
    268         }
    269     }
    270 
    271     /**
    272      * Stops one touch record.
    273      *
    274      * @param recorderAddress recorder address where recoding will be stopped
    275      */
    276     public void stopOneTouchRecord(int recorderAddress) {
    277         try {
    278             mService.stopOneTouchRecord(recorderAddress);
    279         } catch (RemoteException e) {
    280             Log.e(TAG, "failed to stop record: ", e);
    281         }
    282     }
    283 
    284     /**
    285      * Starts timer recording with the given recoder address and recorder source.
    286      * <p>
    287      * Usage
    288      * <pre>
    289      * HdmiTvClient tvClient = ....;
    290      * // create timer info
    291      * TimerInfo timerInfo = HdmiTimerRecourdSources.timerInfoOf(...);
    292      * // for digital source.
    293      * DigitalServiceSource recordSource = HdmiRecordSources.ofDigitalService(...);
    294      * // create timer recording source.
    295      * TimerRecordSource source = HdmiTimerRecourdSources.ofDigitalSource(timerInfo, recordSource);
    296      * tvClient.startTimerRecording(recorderAddress, source);
    297      * </pre>
    298      *
    299      * @param recorderAddress target recorder address
    300      * @param sourceType type of record source. It should be one of
    301      *          {@link HdmiControlManager#TIMER_RECORDING_TYPE_DIGITAL},
    302      *          {@link HdmiControlManager#TIMER_RECORDING_TYPE_ANALOGUE},
    303      *          {@link HdmiControlManager#TIMER_RECORDING_TYPE_EXTERNAL}.
    304      * @param source record source to be used
    305      */
    306     public void startTimerRecording(int recorderAddress, int sourceType, TimerRecordSource source) {
    307         if (source == null) {
    308             throw new IllegalArgumentException("source must not be null.");
    309         }
    310 
    311         checkTimerRecordingSourceType(sourceType);
    312 
    313         try {
    314             byte[] data = new byte[source.getDataSize()];
    315             source.toByteArray(data, 0);
    316             mService.startTimerRecording(recorderAddress, sourceType, data);
    317         } catch (RemoteException e) {
    318             Log.e(TAG, "failed to start record: ", e);
    319         }
    320     }
    321 
    322     private void checkTimerRecordingSourceType(int sourceType) {
    323         switch (sourceType) {
    324             case HdmiControlManager.TIMER_RECORDING_TYPE_DIGITAL:
    325             case HdmiControlManager.TIMER_RECORDING_TYPE_ANALOGUE:
    326             case HdmiControlManager.TIMER_RECORDING_TYPE_EXTERNAL:
    327                 break;
    328             default:
    329                 throw new IllegalArgumentException("Invalid source type:" + sourceType);
    330         }
    331     }
    332 
    333     /**
    334      * Clears timer recording with the given recorder address and recording source.
    335      * For more details, please refer {@link #startTimerRecording(int, int, TimerRecordSource)}.
    336      */
    337     public void clearTimerRecording(int recorderAddress, int sourceType, TimerRecordSource source) {
    338         if (source == null) {
    339             throw new IllegalArgumentException("source must not be null.");
    340         }
    341 
    342         checkTimerRecordingSourceType(sourceType);
    343         try {
    344             byte[] data = new byte[source.getDataSize()];
    345             source.toByteArray(data, 0);
    346             mService.clearTimerRecording(recorderAddress, sourceType, data);
    347         } catch (RemoteException e) {
    348             Log.e(TAG, "failed to start record: ", e);
    349         }
    350     }
    351 
    352     /**
    353      * Interface used to get incoming MHL vendor command.
    354      */
    355     public interface HdmiMhlVendorCommandListener {
    356         void onReceived(int portId, int offset, int length, byte[] data);
    357     }
    358 
    359     /**
    360      * Sets {@link HdmiMhlVendorCommandListener} to get incoming MHL vendor command.
    361      *
    362      * @param listener to receive incoming MHL vendor command
    363      */
    364     public void setHdmiMhlVendorCommandListener(HdmiMhlVendorCommandListener listener) {
    365         if (listener == null) {
    366             throw new IllegalArgumentException("listener must not be null.");
    367         }
    368         try {
    369             mService.addHdmiMhlVendorCommandListener(getListenerWrapper(listener));
    370         } catch (RemoteException e) {
    371             Log.e(TAG, "failed to set hdmi mhl vendor command listener: ", e);
    372         }
    373     }
    374 
    375     private IHdmiMhlVendorCommandListener getListenerWrapper(
    376             final HdmiMhlVendorCommandListener listener) {
    377         return new IHdmiMhlVendorCommandListener.Stub() {
    378             @Override
    379             public void onReceived(int portId, int offset, int length, byte[] data) {
    380                 listener.onReceived(portId, offset, length, data);
    381             }
    382         };
    383     }
    384 
    385     /**
    386      * Sends MHL vendor command to the device connected to a port of the given portId.
    387      *
    388      * @param portId id of port to send MHL vendor command
    389      * @param offset offset in the in given data
    390      * @param length length of data. offset + length should be bound to length of data.
    391      * @param data container for vendor command data. It should be 16 bytes.
    392      * @throws IllegalArgumentException if the given parameters are invalid
    393      */
    394     public void sendMhlVendorCommand(int portId, int offset, int length, byte[] data) {
    395         if (data == null || data.length != VENDOR_DATA_SIZE) {
    396             throw new IllegalArgumentException("Invalid vendor command data.");
    397         }
    398         if (offset < 0 || offset >= VENDOR_DATA_SIZE) {
    399             throw new IllegalArgumentException("Invalid offset:" + offset);
    400         }
    401         if (length < 0 || offset + length > VENDOR_DATA_SIZE) {
    402             throw new IllegalArgumentException("Invalid length:" + length);
    403         }
    404 
    405         try {
    406             mService.sendMhlVendorCommand(portId, offset, length, data);
    407         } catch (RemoteException e) {
    408             Log.e(TAG, "failed to send vendor command: ", e);
    409         }
    410     }
    411 }
    412