• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.HdmiPortInfo;
20 import android.os.Handler;
21 import android.os.Looper;
22 import android.os.MessageQueue;
23 import android.util.Slog;
24 import android.util.SparseArray;
25 
26 import com.android.internal.util.IndentingPrintWriter;
27 import com.android.internal.util.Predicate;
28 import com.android.server.hdmi.HdmiAnnotations.IoThreadOnly;
29 import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
30 import com.android.server.hdmi.HdmiControlService.DevicePollingCallback;
31 
32 import libcore.util.EmptyArray;
33 
34 import java.util.ArrayList;
35 import java.util.LinkedList;
36 import java.util.List;
37 
38 /**
39  * Manages HDMI-CEC command and behaviors. It converts user's command into CEC command
40  * and pass it to CEC HAL so that it sends message to other device. For incoming
41  * message it translates the message and delegates it to proper module.
42  *
43  * <p>It should be careful to access member variables on IO thread because
44  * it can be accessed from system thread as well.
45  *
46  * <p>It can be created only by {@link HdmiCecController#create}
47  *
48  * <p>Declared as package-private, accessed by {@link HdmiControlService} only.
49  */
50 final class HdmiCecController {
51     private static final String TAG = "HdmiCecController";
52 
53     /**
54      * Interface to report allocated logical address.
55      */
56     interface AllocateAddressCallback {
57         /**
58          * Called when a new logical address is allocated.
59          *
60          * @param deviceType requested device type to allocate logical address
61          * @param logicalAddress allocated logical address. If it is
62          *                       {@link Constants#ADDR_UNREGISTERED}, it means that
63          *                       it failed to allocate logical address for the given device type
64          */
onAllocated(int deviceType, int logicalAddress)65         void onAllocated(int deviceType, int logicalAddress);
66     }
67 
68     private static final byte[] EMPTY_BODY = EmptyArray.BYTE;
69 
70     private static final int NUM_LOGICAL_ADDRESS = 16;
71 
72     // Predicate for whether the given logical address is remote device's one or not.
73     private final Predicate<Integer> mRemoteDeviceAddressPredicate = new Predicate<Integer>() {
74         @Override
75         public boolean apply(Integer address) {
76             return !isAllocatedLocalDeviceAddress(address);
77         }
78     };
79 
80     // Predicate whether the given logical address is system audio's one or not
81     private final Predicate<Integer> mSystemAudioAddressPredicate = new Predicate<Integer>() {
82         @Override
83         public boolean apply(Integer address) {
84             return HdmiUtils.getTypeFromAddress(address) == Constants.ADDR_AUDIO_SYSTEM;
85         }
86     };
87 
88     // Handler instance to process synchronous I/O (mainly send) message.
89     private Handler mIoHandler;
90 
91     // Handler instance to process various messages coming from other CEC
92     // device or issued by internal state change.
93     private Handler mControlHandler;
94 
95     // Stores the pointer to the native implementation of the service that
96     // interacts with HAL.
97     private volatile long mNativePtr;
98 
99     private final HdmiControlService mService;
100 
101     // Stores the local CEC devices in the system. Device type is used for key.
102     private final SparseArray<HdmiCecLocalDevice> mLocalDevices = new SparseArray<>();
103 
104     // Private constructor.  Use HdmiCecController.create().
HdmiCecController(HdmiControlService service)105     private HdmiCecController(HdmiControlService service) {
106         mService = service;
107     }
108 
109     /**
110      * A factory method to get {@link HdmiCecController}. If it fails to initialize
111      * inner device or has no device it will return {@code null}.
112      *
113      * <p>Declared as package-private, accessed by {@link HdmiControlService} only.
114      * @param service {@link HdmiControlService} instance used to create internal handler
115      *                and to pass callback for incoming message or event.
116      * @return {@link HdmiCecController} if device is initialized successfully. Otherwise,
117      *         returns {@code null}.
118      */
create(HdmiControlService service)119     static HdmiCecController create(HdmiControlService service) {
120         HdmiCecController controller = new HdmiCecController(service);
121         long nativePtr = nativeInit(controller, service.getServiceLooper().getQueue());
122         if (nativePtr == 0L) {
123             controller = null;
124             return null;
125         }
126 
127         controller.init(nativePtr);
128         return controller;
129     }
130 
init(long nativePtr)131     private void init(long nativePtr) {
132         mIoHandler = new Handler(mService.getIoLooper());
133         mControlHandler = new Handler(mService.getServiceLooper());
134         mNativePtr = nativePtr;
135     }
136 
137     @ServiceThreadOnly
addLocalDevice(int deviceType, HdmiCecLocalDevice device)138     void addLocalDevice(int deviceType, HdmiCecLocalDevice device) {
139         assertRunOnServiceThread();
140         mLocalDevices.put(deviceType, device);
141     }
142 
143     /**
144      * Allocate a new logical address of the given device type. Allocated
145      * address will be reported through {@link AllocateAddressCallback}.
146      *
147      * <p> Declared as package-private, accessed by {@link HdmiControlService} only.
148      *
149      * @param deviceType type of device to used to determine logical address
150      * @param preferredAddress a logical address preferred to be allocated.
151      *                         If sets {@link Constants#ADDR_UNREGISTERED}, scans
152      *                         the smallest logical address matched with the given device type.
153      *                         Otherwise, scan address will start from {@code preferredAddress}
154      * @param callback callback interface to report allocated logical address to caller
155      */
156     @ServiceThreadOnly
allocateLogicalAddress(final int deviceType, final int preferredAddress, final AllocateAddressCallback callback)157     void allocateLogicalAddress(final int deviceType, final int preferredAddress,
158             final AllocateAddressCallback callback) {
159         assertRunOnServiceThread();
160 
161         runOnIoThread(new Runnable() {
162             @Override
163             public void run() {
164                 handleAllocateLogicalAddress(deviceType, preferredAddress, callback);
165             }
166         });
167     }
168 
169     @IoThreadOnly
handleAllocateLogicalAddress(final int deviceType, int preferredAddress, final AllocateAddressCallback callback)170     private void handleAllocateLogicalAddress(final int deviceType, int preferredAddress,
171             final AllocateAddressCallback callback) {
172         assertRunOnIoThread();
173         int startAddress = preferredAddress;
174         // If preferred address is "unregistered", start address will be the smallest
175         // address matched with the given device type.
176         if (preferredAddress == Constants.ADDR_UNREGISTERED) {
177             for (int i = 0; i < NUM_LOGICAL_ADDRESS; ++i) {
178                 if (deviceType == HdmiUtils.getTypeFromAddress(i)) {
179                     startAddress = i;
180                     break;
181                 }
182             }
183         }
184 
185         int logicalAddress = Constants.ADDR_UNREGISTERED;
186         // Iterates all possible addresses which has the same device type.
187         for (int i = 0; i < NUM_LOGICAL_ADDRESS; ++i) {
188             int curAddress = (startAddress + i) % NUM_LOGICAL_ADDRESS;
189             if (curAddress != Constants.ADDR_UNREGISTERED
190                     && deviceType == HdmiUtils.getTypeFromAddress(curAddress)) {
191                 int failedPollingCount = 0;
192                 for (int j = 0; j < HdmiConfig.ADDRESS_ALLOCATION_RETRY; ++j) {
193                     if (!sendPollMessage(curAddress, curAddress, 1)) {
194                         failedPollingCount++;
195                     }
196                 }
197 
198                 // Pick logical address if failed ratio is more than a half of all retries.
199                 if (failedPollingCount * 2 >  HdmiConfig.ADDRESS_ALLOCATION_RETRY) {
200                     logicalAddress = curAddress;
201                     break;
202                 }
203             }
204         }
205 
206         final int assignedAddress = logicalAddress;
207         HdmiLogger.debug("New logical address for device [%d]: [preferred:%d, assigned:%d]",
208                         deviceType, preferredAddress, assignedAddress);
209         if (callback != null) {
210             runOnServiceThread(new Runnable() {
211                 @Override
212                 public void run() {
213                     callback.onAllocated(deviceType, assignedAddress);
214                 }
215             });
216         }
217     }
218 
buildBody(int opcode, byte[] params)219     private static byte[] buildBody(int opcode, byte[] params) {
220         byte[] body = new byte[params.length + 1];
221         body[0] = (byte) opcode;
222         System.arraycopy(params, 0, body, 1, params.length);
223         return body;
224     }
225 
226 
getPortInfos()227     HdmiPortInfo[] getPortInfos() {
228         return nativeGetPortInfos(mNativePtr);
229     }
230 
231     /**
232      * Return the locally hosted logical device of a given type.
233      *
234      * @param deviceType logical device type
235      * @return {@link HdmiCecLocalDevice} instance if the instance of the type is available;
236      *          otherwise null.
237      */
getLocalDevice(int deviceType)238     HdmiCecLocalDevice getLocalDevice(int deviceType) {
239         return mLocalDevices.get(deviceType);
240     }
241 
242     /**
243      * Add a new logical address to the device. Device's HW should be notified
244      * when a new logical address is assigned to a device, so that it can accept
245      * a command having available destinations.
246      *
247      * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
248      *
249      * @param newLogicalAddress a logical address to be added
250      * @return 0 on success. Otherwise, returns negative value
251      */
252     @ServiceThreadOnly
addLogicalAddress(int newLogicalAddress)253     int addLogicalAddress(int newLogicalAddress) {
254         assertRunOnServiceThread();
255         if (HdmiUtils.isValidAddress(newLogicalAddress)) {
256             return nativeAddLogicalAddress(mNativePtr, newLogicalAddress);
257         } else {
258             return -1;
259         }
260     }
261 
262     /**
263      * Clear all logical addresses registered in the device.
264      *
265      * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
266      */
267     @ServiceThreadOnly
clearLogicalAddress()268     void clearLogicalAddress() {
269         assertRunOnServiceThread();
270         for (int i = 0; i < mLocalDevices.size(); ++i) {
271             mLocalDevices.valueAt(i).clearAddress();
272         }
273         nativeClearLogicalAddress(mNativePtr);
274     }
275 
276     @ServiceThreadOnly
clearLocalDevices()277     void clearLocalDevices() {
278         assertRunOnServiceThread();
279         mLocalDevices.clear();
280     }
281 
282     /**
283      * Return the physical address of the device.
284      *
285      * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
286      *
287      * @return CEC physical address of the device. The range of success address
288      *         is between 0x0000 and 0xFFFF. If failed it returns -1
289      */
290     @ServiceThreadOnly
getPhysicalAddress()291     int getPhysicalAddress() {
292         assertRunOnServiceThread();
293         return nativeGetPhysicalAddress(mNativePtr);
294     }
295 
296     /**
297      * Return CEC version of the device.
298      *
299      * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
300      */
301     @ServiceThreadOnly
getVersion()302     int getVersion() {
303         assertRunOnServiceThread();
304         return nativeGetVersion(mNativePtr);
305     }
306 
307     /**
308      * Return vendor id of the device.
309      *
310      * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
311      */
312     @ServiceThreadOnly
getVendorId()313     int getVendorId() {
314         assertRunOnServiceThread();
315         return nativeGetVendorId(mNativePtr);
316     }
317 
318     /**
319      * Set an option to CEC HAL.
320      *
321      * @param flag key of option
322      * @param value value of option
323      */
324     @ServiceThreadOnly
setOption(int flag, int value)325     void setOption(int flag, int value) {
326         assertRunOnServiceThread();
327         HdmiLogger.debug("setOption: [flag:%d, value:%d]", flag, value);
328         nativeSetOption(mNativePtr, flag, value);
329     }
330 
331     /**
332      * Configure ARC circuit in the hardware logic to start or stop the feature.
333      *
334      * @param port ID of HDMI port to which AVR is connected
335      * @param enabled whether to enable/disable ARC
336      */
337     @ServiceThreadOnly
setAudioReturnChannel(int port, boolean enabled)338     void setAudioReturnChannel(int port, boolean enabled) {
339         assertRunOnServiceThread();
340         nativeSetAudioReturnChannel(mNativePtr, port, enabled);
341     }
342 
343     /**
344      * Return the connection status of the specified port
345      *
346      * @param port port number to check connection status
347      * @return true if connected; otherwise, return false
348      */
349     @ServiceThreadOnly
isConnected(int port)350     boolean isConnected(int port) {
351         assertRunOnServiceThread();
352         return nativeIsConnected(mNativePtr, port);
353     }
354 
355     /**
356      * Poll all remote devices. It sends &lt;Polling Message&gt; to all remote
357      * devices.
358      *
359      * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
360      *
361      * @param callback an interface used to get a list of all remote devices' address
362      * @param sourceAddress a logical address of source device where sends polling message
363      * @param pickStrategy strategy how to pick polling candidates
364      * @param retryCount the number of retry used to send polling message to remote devices
365      */
366     @ServiceThreadOnly
pollDevices(DevicePollingCallback callback, int sourceAddress, int pickStrategy, int retryCount)367     void pollDevices(DevicePollingCallback callback, int sourceAddress, int pickStrategy,
368             int retryCount) {
369         assertRunOnServiceThread();
370 
371         // Extract polling candidates. No need to poll against local devices.
372         List<Integer> pollingCandidates = pickPollCandidates(pickStrategy);
373         ArrayList<Integer> allocated = new ArrayList<>();
374         runDevicePolling(sourceAddress, pollingCandidates, retryCount, callback, allocated);
375     }
376 
377     /**
378      * Return a list of all {@link HdmiCecLocalDevice}s.
379      *
380      * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
381      */
382     @ServiceThreadOnly
getLocalDeviceList()383     List<HdmiCecLocalDevice> getLocalDeviceList() {
384         assertRunOnServiceThread();
385         return HdmiUtils.sparseArrayToList(mLocalDevices);
386     }
387 
pickPollCandidates(int pickStrategy)388     private List<Integer> pickPollCandidates(int pickStrategy) {
389         int strategy = pickStrategy & Constants.POLL_STRATEGY_MASK;
390         Predicate<Integer> pickPredicate = null;
391         switch (strategy) {
392             case Constants.POLL_STRATEGY_SYSTEM_AUDIO:
393                 pickPredicate = mSystemAudioAddressPredicate;
394                 break;
395             case Constants.POLL_STRATEGY_REMOTES_DEVICES:
396             default:  // The default is POLL_STRATEGY_REMOTES_DEVICES.
397                 pickPredicate = mRemoteDeviceAddressPredicate;
398                 break;
399         }
400 
401         int iterationStrategy = pickStrategy & Constants.POLL_ITERATION_STRATEGY_MASK;
402         LinkedList<Integer> pollingCandidates = new LinkedList<>();
403         switch (iterationStrategy) {
404             case Constants.POLL_ITERATION_IN_ORDER:
405                 for (int i = Constants.ADDR_TV; i <= Constants.ADDR_SPECIFIC_USE; ++i) {
406                     if (pickPredicate.apply(i)) {
407                         pollingCandidates.add(i);
408                     }
409                 }
410                 break;
411             case Constants.POLL_ITERATION_REVERSE_ORDER:
412             default:  // The default is reverse order.
413                 for (int i = Constants.ADDR_SPECIFIC_USE; i >= Constants.ADDR_TV; --i) {
414                     if (pickPredicate.apply(i)) {
415                         pollingCandidates.add(i);
416                     }
417                 }
418                 break;
419         }
420         return pollingCandidates;
421     }
422 
423     @ServiceThreadOnly
isAllocatedLocalDeviceAddress(int address)424     private boolean isAllocatedLocalDeviceAddress(int address) {
425         assertRunOnServiceThread();
426         for (int i = 0; i < mLocalDevices.size(); ++i) {
427             if (mLocalDevices.valueAt(i).isAddressOf(address)) {
428                 return true;
429             }
430         }
431         return false;
432     }
433 
434     @ServiceThreadOnly
runDevicePolling(final int sourceAddress, final List<Integer> candidates, final int retryCount, final DevicePollingCallback callback, final List<Integer> allocated)435     private void runDevicePolling(final int sourceAddress,
436             final List<Integer> candidates, final int retryCount,
437             final DevicePollingCallback callback, final List<Integer> allocated) {
438         assertRunOnServiceThread();
439         if (candidates.isEmpty()) {
440             if (callback != null) {
441                 HdmiLogger.debug("[P]:AllocatedAddress=%s", allocated.toString());
442                 callback.onPollingFinished(allocated);
443             }
444             return;
445         }
446 
447         final Integer candidate = candidates.remove(0);
448         // Proceed polling action for the next address once polling action for the
449         // previous address is done.
450         runOnIoThread(new Runnable() {
451             @Override
452             public void run() {
453                 if (sendPollMessage(sourceAddress, candidate, retryCount)) {
454                     allocated.add(candidate);
455                 }
456                 runOnServiceThread(new Runnable() {
457                     @Override
458                     public void run() {
459                         runDevicePolling(sourceAddress, candidates, retryCount, callback,
460                                 allocated);
461                     }
462                 });
463             }
464         });
465     }
466 
467     @IoThreadOnly
sendPollMessage(int sourceAddress, int destinationAddress, int retryCount)468     private boolean sendPollMessage(int sourceAddress, int destinationAddress, int retryCount) {
469         assertRunOnIoThread();
470         for (int i = 0; i < retryCount; ++i) {
471             // <Polling Message> is a message which has empty body.
472             // If sending <Polling Message> failed (NAK), it becomes
473             // new logical address for the device because no device uses
474             // it as logical address of the device.
475             if (nativeSendCecCommand(mNativePtr, sourceAddress, destinationAddress, EMPTY_BODY)
476                     == Constants.SEND_RESULT_SUCCESS) {
477                 return true;
478             }
479         }
480         return false;
481     }
482 
assertRunOnIoThread()483     private void assertRunOnIoThread() {
484         if (Looper.myLooper() != mIoHandler.getLooper()) {
485             throw new IllegalStateException("Should run on io thread.");
486         }
487     }
488 
assertRunOnServiceThread()489     private void assertRunOnServiceThread() {
490         if (Looper.myLooper() != mControlHandler.getLooper()) {
491             throw new IllegalStateException("Should run on service thread.");
492         }
493     }
494 
495     // Run a Runnable on IO thread.
496     // It should be careful to access member variables on IO thread because
497     // it can be accessed from system thread as well.
runOnIoThread(Runnable runnable)498     private void runOnIoThread(Runnable runnable) {
499         mIoHandler.post(runnable);
500     }
501 
runOnServiceThread(Runnable runnable)502     private void runOnServiceThread(Runnable runnable) {
503         mControlHandler.post(runnable);
504     }
505 
506     @ServiceThreadOnly
flush(final Runnable runnable)507     void flush(final Runnable runnable) {
508         assertRunOnServiceThread();
509         runOnIoThread(new Runnable() {
510             @Override
511             public void run() {
512                 // This ensures the runnable for cleanup is performed after all the pending
513                 // commands are processed by IO thread.
514                 runOnServiceThread(runnable);
515             }
516         });
517     }
518 
isAcceptableAddress(int address)519     private boolean isAcceptableAddress(int address) {
520         // Can access command targeting devices available in local device or broadcast command.
521         if (address == Constants.ADDR_BROADCAST) {
522             return true;
523         }
524         return isAllocatedLocalDeviceAddress(address);
525     }
526 
527     @ServiceThreadOnly
onReceiveCommand(HdmiCecMessage message)528     private void onReceiveCommand(HdmiCecMessage message) {
529         assertRunOnServiceThread();
530         if (isAcceptableAddress(message.getDestination()) && mService.handleCecCommand(message)) {
531             return;
532         }
533         // Not handled message, so we will reply it with <Feature Abort>.
534         maySendFeatureAbortCommand(message, Constants.ABORT_UNRECOGNIZED_OPCODE);
535     }
536 
537     @ServiceThreadOnly
maySendFeatureAbortCommand(HdmiCecMessage message, int reason)538     void maySendFeatureAbortCommand(HdmiCecMessage message, int reason) {
539         assertRunOnServiceThread();
540         // Swap the source and the destination.
541         int src = message.getDestination();
542         int dest = message.getSource();
543         if (src == Constants.ADDR_BROADCAST || dest == Constants.ADDR_UNREGISTERED) {
544             // Don't reply <Feature Abort> from the unregistered devices or for the broadcasted
545             // messages. See CEC 12.2 Protocol General Rules for detail.
546             return;
547         }
548         int originalOpcode = message.getOpcode();
549         if (originalOpcode == Constants.MESSAGE_FEATURE_ABORT) {
550             return;
551         }
552         sendCommand(
553                 HdmiCecMessageBuilder.buildFeatureAbortCommand(src, dest, originalOpcode, reason));
554     }
555 
556     @ServiceThreadOnly
sendCommand(HdmiCecMessage cecMessage)557     void sendCommand(HdmiCecMessage cecMessage) {
558         assertRunOnServiceThread();
559         sendCommand(cecMessage, null);
560     }
561 
562     @ServiceThreadOnly
sendCommand(final HdmiCecMessage cecMessage, final HdmiControlService.SendMessageCallback callback)563     void sendCommand(final HdmiCecMessage cecMessage,
564             final HdmiControlService.SendMessageCallback callback) {
565         assertRunOnServiceThread();
566         runOnIoThread(new Runnable() {
567             @Override
568             public void run() {
569                 HdmiLogger.debug("[S]:" + cecMessage);
570                 byte[] body = buildBody(cecMessage.getOpcode(), cecMessage.getParams());
571                 int i = 0;
572                 int errorCode = Constants.SEND_RESULT_SUCCESS;
573                 do {
574                     errorCode = nativeSendCecCommand(mNativePtr, cecMessage.getSource(),
575                             cecMessage.getDestination(), body);
576                     if (errorCode == Constants.SEND_RESULT_SUCCESS) {
577                         break;
578                     }
579                 } while (i++ < HdmiConfig.RETRANSMISSION_COUNT);
580 
581                 final int finalError = errorCode;
582                 if (finalError != Constants.SEND_RESULT_SUCCESS) {
583                     Slog.w(TAG, "Failed to send " + cecMessage);
584                 }
585                 if (callback != null) {
586                     runOnServiceThread(new Runnable() {
587                         @Override
588                         public void run() {
589                             callback.onSendCompleted(finalError);
590                         }
591                     });
592                 }
593             }
594         });
595     }
596 
597     /**
598      * Called by native when incoming CEC message arrived.
599      */
600     @ServiceThreadOnly
handleIncomingCecCommand(int srcAddress, int dstAddress, byte[] body)601     private void handleIncomingCecCommand(int srcAddress, int dstAddress, byte[] body) {
602         assertRunOnServiceThread();
603         HdmiCecMessage command = HdmiCecMessageBuilder.of(srcAddress, dstAddress, body);
604         HdmiLogger.debug("[R]:" + command);
605         onReceiveCommand(command);
606     }
607 
608     /**
609      * Called by native when a hotplug event issues.
610      */
611     @ServiceThreadOnly
handleHotplug(int port, boolean connected)612     private void handleHotplug(int port, boolean connected) {
613         assertRunOnServiceThread();
614         HdmiLogger.debug("Hotplug event:[port:%d, connected:%b]", port, connected);
615         mService.onHotplug(port, connected);
616     }
617 
dump(final IndentingPrintWriter pw)618     void dump(final IndentingPrintWriter pw) {
619         for (int i = 0; i < mLocalDevices.size(); ++i) {
620             pw.println("HdmiCecLocalDevice #" + i + ":");
621             pw.increaseIndent();
622             mLocalDevices.valueAt(i).dump(pw);
623             pw.decreaseIndent();
624         }
625     }
626 
nativeInit(HdmiCecController handler, MessageQueue messageQueue)627     private static native long nativeInit(HdmiCecController handler, MessageQueue messageQueue);
nativeSendCecCommand(long controllerPtr, int srcAddress, int dstAddress, byte[] body)628     private static native int nativeSendCecCommand(long controllerPtr, int srcAddress,
629             int dstAddress, byte[] body);
nativeAddLogicalAddress(long controllerPtr, int logicalAddress)630     private static native int nativeAddLogicalAddress(long controllerPtr, int logicalAddress);
nativeClearLogicalAddress(long controllerPtr)631     private static native void nativeClearLogicalAddress(long controllerPtr);
nativeGetPhysicalAddress(long controllerPtr)632     private static native int nativeGetPhysicalAddress(long controllerPtr);
nativeGetVersion(long controllerPtr)633     private static native int nativeGetVersion(long controllerPtr);
nativeGetVendorId(long controllerPtr)634     private static native int nativeGetVendorId(long controllerPtr);
nativeGetPortInfos(long controllerPtr)635     private static native HdmiPortInfo[] nativeGetPortInfos(long controllerPtr);
nativeSetOption(long controllerPtr, int flag, int value)636     private static native void nativeSetOption(long controllerPtr, int flag, int value);
nativeSetAudioReturnChannel(long controllerPtr, int port, boolean flag)637     private static native void nativeSetAudioReturnChannel(long controllerPtr, int port, boolean flag);
nativeIsConnected(long controllerPtr, int port)638     private static native boolean nativeIsConnected(long controllerPtr, int port);
639 }
640