So, I was also interested why 3rd party infrared apps are not working on the LePro 3, and did some debugging. This thread somehow documents my findings step by step...
Edit - Dec 1, 2016:
As the postings below got a little bit technical, I'll try to give a short overview and some background infos about the situation with Infrared apps on the LePro 3 (and others, like some LG or Honor phones)
Situation with LePro 3 Stock ROM (EUI):
Background:
How is the Universal Electronics chip working?
What are possible future steps?
---------------------------------------------------------------------------
---------------------------------------------------------------------------
---------------------------------------------------------------------------
Original posting below:
As other IR apps seem to work (i.e. they don't crash or throw Exceptions) it seems that the basic Infrared services are installed on the system. And indeed the ConsumerIrService class is available. This system service provides the API that user-installed apps can use to send IR commands (that's what all the standard Infrared apps from playstore are doing).
This (Java) class uses a native implementation (i.e. written in C or C++) to perform the actual commands. In Android hardware stuff gets abstracted using so-called HALs (=hardware abstraction layer). So when the ConsumerIrService class gets instantiated it calls the native method halOpen() which is implemented here in com_android_server_ConsumerIrService.cpp.
This C++ function uses hw_get_module to load the consumerir HAL (hardware abstraction layer) implementation. In the first logcat I made on my LEX720 after buying I found the error message from this very method:
-2 means ENOENT in C, which stands for "file not found".
Since updating to EUI 5.8.018S this error does not appear anymore, which means the loading of the HAL now is working just fine. And indeed, (after finding out how hw_get_module is locating the modules) I could verify that the HAL implementation file exists on the system and gets loaded without problems. You can find it here: /system/lib64/hw/consumerir.default.so
So what is the implementation in consumerir.default.so actually doing?
When using a 3rd party app and trying to send IR commands, you will find in the logcat messages like this:
This log messages looks exactly like log message from the Android Open Source example implementation of the consumer IR HAL in the file consumerir.c in line 46. If we look in the same C module at the end, where the HAL module info is declared, you will see the strings:
You can find this here at the end of the file.
I did a quick "strings" (this command just displays readable text parts contained in binary files) on the LEX720 device on the /system/lib64/hw/consumerir.default.so file and indeed I found the same strings in there:
This is not a proof but might be a strong indication that this is the same file as the one from the Android Open Source project (especially as it also contains the string "Demo IR HAL").
Ok, how is the AOSP implementation actually sending IR codes?
It turns out, it doesn't at all. It's just meant as a tamplate for OEM manufacturers to include their own hardware-specific implementation. If you look at the relevant function "consumerir_transmit" you see that it's just simulating and does nothing else but sleeping:
Ok. So it seems the reason why we the official android Infrared API is not working, is because the actual implementation in "consumerir.c" is missing.
While the LePro does include a consumerir HAL implementation, it is just a dummy, it does nothing on the hardware.
Ok, so how is then the LeEco Remote Control (package "com.letv.android.remotecontrol" in /system/app/LetvRemoteControl_preinstall/) doing it?
As far as I could see, the Java part of the application is communicating with a service ("com.uei.control.Service") which seems to perform the actual sending of infrared commands.Unfortunately this seems to be implemented native (in C code) so I have no idea how to find out what it's doing.
I can see that in the Intent which binds the service two parameters (using "putExtra()") are given, which sound very interesting:
/sys/remote/enable (which is world writable, permissions: 0666 - UPDATE: not world-writable, protected by SELinux: -rw-rw-rw- ubject_r:sysfs_ir_enable:s0 /sys/remote/enable)
(Btw, this sys file is provided by the driver file "kernel/drivers/misc/ir_maxq616.c" in the kernel source and just seems to set a specific GPIO pin to 0 or 1. So I guess this GPIO PIN is wired to the IR chip to turn it on and off).
Summary:
So I see this options here:
Unfortunately my own knowledge about serial device communication as well as Infrared transmitter devices is not existing.. Also my latest experiences in C are from several years ago.
But maybe the findings in this posting might help somebody else to implement this (app developers as well as platform developers).
So if you know somebody, feel free to spread the link to this thread.
John
Edit - Dec 1, 2016:
As the postings below got a little bit technical, I'll try to give a short overview and some background infos about the situation with Infrared apps on the LePro 3 (and others, like some LG or Honor phones)
Situation with LePro 3 Stock ROM (EUI):
- Since Kitkat, the official Android API has included functions for controlling Infrared emitting devices (IR blaster). (see ConsumerIrManager in Android API Doc)
- The LePro 3 (at least in Version 5.8.018S) includes this official API, so all standard Infrared apps from Play Store seem to install and work fine on a first look (the apps don't complain and don't crash)
- But actually the infrared HAL (HAL=hardware abstraction layer) implementation which is responsible to actually DO the stuff on hardware seems to be a dummy implementation: It takes the command (e.g. transmit IR pattern XXXYYZZZYY), and then simply does nothing. So the Infrared apps think everything is working fine, but in reality the IR blaster is not sending anything. (You can easily check with a second phone if the IR blaster is working by looking with the camera of the second phone on the IR blaster, as most cameras can see Infrared light.)
- The included system app "Remote Control" from LeEco works fine, so it must use other mechanisms to control the IR hardware.
Background:
- Official Android API has support for only sending Infrared patterns over an Infrared emitting device (IR led). No receiving supported.
- An Infrared app has to give the raw pattern it wants to send, and the IR emitter simply sends this raw led on/led off pattern (see the method transmit(..)) . Such a pattern could look like: "use carrier frequency 38 kHz; then turn the LED on for 169ms, turn it off for 168ms, turn on for 21ms, turn off for 263ms, etc.... So it's really a low-level way to tell the IR blaster what to do. That is the official Android API way to send Infrared. (See also this short YouTube video for more details on how infrared remotes are working in general: How It Works - Infrared Remote Control.)
- On the other side the hardware included in LePro 3 supports both: sending and receiving of infrared. So it can "learn" infrared patterns from other remote controls (something what the official Android API doesn't support at the moment).
- Also the hardware is not just a "dumb" infrared LED, but instead they included a fully independent System-on-chip from a company named "Universal Electronics" (UEI). They seem to be specialized on making infrared remote controllers and also sell physical universal remote controls.
- So, LeEco (and LG and others) decided to not just put a simple infrared LED into their devices, but instead include an advanced product from a company which has more experience on the field of infrared remotes (Universal Electronics).
- Or in other words: Imagine Le Eco crammed one of these universal remote controls, which Universal Electronics is selling, into your phone and the included app doesn't send directly infrared, but instead just presses the buttons on this built-in universal remote control.
How is the Universal Electronics chip working?
- As you may imagine a full blown universal remote control in hardware is more complex than a simple LED. In fact the infrared chip (on the LePro 3 it seems to be the MAXQ616) is a separate system-on-chip, running its own firmware on a 12 MHz processor with its own 80KB flash memory and its own RAM (2KB) which communicates over a serial line with the Snapdragon main system where Android is running (using the serial device /dev/ttyHSL1 with a baudrate of 230400). (According to the short spec-sheet it seems to support other protocols for communication as well, but at least in the Le Pro 3 it is wired to the Snapdragon over a serial line.)
- Power-on/power-off: The LeEco kernel source contains a small driver (ir_maxq616.c) which provides a sys-file (/sys/remote/enable). This driver only seems to set a GPIO pin if a 1 or a 0 is written into this sys-file. I guess this GPIO pin is used as kind of a "power-switch" for the MAXQ616 chip (or maybe to trigger some kind of power-save mode). This file is protected by SELinux policies (so not all apps are allowed to toggle this "switch"). If a 0 is written into this sys-file the IR blaster no longer emits IR.
- The commands sent between the infrared chip system and Android kernel seem to be more high-level than the IR patterns which are used in the official Android Infrared API (which are just simple LED-on/LED-off patterns as described above). Probably this will make it much harder to implement a HAL for the standard Android API.
- To make it easier for manufacturers like LeEco and LG to include this sophisticated infrared devices into their phones, Universal Electronics provides an SDK (software development kit) which is called QuickSet SDK (btw, when you search for it on XDA you see that a also some other phones are using this SDK).
- This SDK is running as a bound service (on the LePro 3 it'c called "UEI Android Services SDK(LeTV)") and offers high level APIs for app developers which can build infrared apps. This was used to build the pre-installed infrared remote app. The SDK converts the commands of the app to low-level serial signals which are sent to the infrared chip, which then performs the actions.
- LeEco did some customizations to this SDK package (e.g. changed the packagename to "com.uei.quicksetsdk.letv"). So we cannot assume this a generic package also to be found on other OEM's phones.
- The high-level protocol between the Android App and the QuickSet SDK looks something like: "start sending Samsung TV power button signal", "stop sending infrared". QuickSet SDK and the Infrared chip understand this high level commands and decide on their own which pattern and frequencies need to be used to send a "power button" signal for samsung TVs.
- So not the app is in control what is actually sent over infrared, but instead the QuickSet SDK (or the infrared chip-system)
- This is a totally different approach than what we have in the official Android Infrared API. So now I can imagine a little bit better, why LeEco/LG/Universal Electronics decided not to support the official API (but it's still unclear for me why they included the dummy implementation - maybe by mistake?).
- On the other hand the Android API is much less complex, and allows much more freedom in what to send over IR (just give it a pattern and the hardware emits it).
What are possible future steps?
- On EUI: We could try live with the original remote application (but the LeEco remote control app is very bloated and claims a lot of permissions not related to infrared - see this posting here).
- On EUI: We could try to port preinstalled remote applications from other phones which also are built with QickSet SDK (for example LG's QuickRemote app), but this might be difficult if the other phone used a different version of QuickSet SDK.
- On cyanogenmod or AOSP: Out of the box the standard infrared API will not work (as described above) but we could try to port QuickSet SDK and the LeEco remote app to cyanogenmod (this is the way other devs did it on LG phones). But the LeEco Remote control app seems to have some dependencies to other LeEco services, so this might be difficult.
- On all ROMs: If it is possible to reverse the protocol which QuickSet SDK and the infrared chip-system are using then we might be able to implement the standard Android infrared API (writing our own consumer_ir.c HAL which does the same as the QuickSet SDK). But this would also mean, that we degrade this high-level full-blown general remote control system to a dumb simple IR emitter. Also receiving of IR (learning mode) would not work, as standard Android has no API for this. This approach is much more difficult than the others and maybe might never be possible at all.
- Update, Dec 2, 2016: On EUI: There is another option: infrared apps could try to use the QuickSet SDK directly, like the LeEco remote control app does. This seems to work in principle (every app can bind the QuickSet SDK service without special permissions) but has some disadvantages:
- I don't know if the API is somewhere publicly available. I don't see any docs online, maybe an account is needed at developer.quicksetcloud.com. I didn't try this.
- At least LeEco did some customizations on the QuickSet service on the LePro 3 (changing the package name), so this solution is very targeted for specific devices and not generic
- There is still the "master switch" (the kernel sys-file: /sys/remote/enable) which is used to turn on or off the MAXQ616 infrared controller. On the EUI stock kernel this file is protected be SELinux (context ubject_r:sysfs_ir_enable:s0), so other apps would need root to enable the IR chip at first
- 2nd Update - SUCCESS!!, Dec 2, 2016: On EUI: And I found another, more generic solution! Instead of binding the QuickSet SDK directly from an app (which would need customizations for every IR app), I wrote an Xposed module which does the same but within the standard Android ConsumerIrService (which provides the default Android IR API). So the standard Android API receives IR patterns from apps and simply forwards them to the QuickSet SDK API. (This only is possible because the QuickSet SDK - besides all of its high-level API methods - fortunately also contains a single very important low-level API method which allows sending of raw IR patterns: transmit(int carrierFrequency, int[] pattern)). So this Xposed module can act as a simple "bridge" between the 2 APIs. I've published this Xposed module in the XPosed repo, see this thread for details: [MOD][Xposed] Make Infrared Blaster working with all 3rd party apps (on EUI)
---------------------------------------------------------------------------
---------------------------------------------------------------------------
---------------------------------------------------------------------------
Original posting below:
As other IR apps seem to work (i.e. they don't crash or throw Exceptions) it seems that the basic Infrared services are installed on the system. And indeed the ConsumerIrService class is available. This system service provides the API that user-installed apps can use to send IR commands (that's what all the standard Infrared apps from playstore are doing).
This (Java) class uses a native implementation (i.e. written in C or C++) to perform the actual commands. In Android hardware stuff gets abstracted using so-called HALs (=hardware abstraction layer). So when the ConsumerIrService class gets instantiated it calls the native method halOpen() which is implemented here in com_android_server_ConsumerIrService.cpp.
This C++ function uses hw_get_module to load the consumerir HAL (hardware abstraction layer) implementation. In the first logcat I made on my LEX720 after buying I found the error message from this very method:
Code:
E/ConsumerIrService(10728): Can't open consumer IR HW Module, error: -2
Since updating to EUI 5.8.018S this error does not appear anymore, which means the loading of the HAL now is working just fine. And indeed, (after finding out how hw_get_module is locating the modules) I could verify that the HAL implementation file exists on the system and gets loaded without problems. You can find it here: /system/lib64/hw/consumerir.default.so
So what is the implementation in consumerir.default.so actually doing?
When using a 3rd party app and trying to send IR commands, you will find in the logcat messages like this:
Code:
D/ConsumerIrHal( 3445): transmit for 2272 uS at 37647 Hz
Code:
consumerir_module_t HAL_MODULE_INFO_SYM = {
.common = {
.tag = HARDWARE_MODULE_TAG,
.module_api_version = CONSUMERIR_MODULE_API_VERSION_1_0,
.hal_api_version = HARDWARE_HAL_API_VERSION,
.id = CONSUMERIR_HARDWARE_MODULE_ID,
.name = "Demo IR HAL",
.author = "The Android Open Source Project",
.methods = &consumerir_module_methods,
},
};
I did a quick "strings" (this command just displays readable text parts contained in binary files) on the LEX720 device on the /system/lib64/hw/consumerir.default.so file and indeed I found the same strings in there:
Code:
# strings /system/lib64/hw/consumerir.default.so
...
Demo IR HAL
The Android Open Source Project
...
Ok, how is the AOSP implementation actually sending IR codes?
It turns out, it doesn't at all. It's just meant as a tamplate for OEM manufacturers to include their own hardware-specific implementation. If you look at the relevant function "consumerir_transmit" you see that it's just simulating and does nothing else but sleeping:
Code:
static int consumerir_transmit(struct consumerir_device *dev __unused,
int carrier_freq, const int pattern[], int pattern_len)
{
int total_time = 0;
long i;
for (i = 0; i < pattern_len; i++)
total_time += pattern[i];
/* simulate the time spent transmitting by sleeping */
ALOGD("transmit for %d uS at %d Hz", total_time, carrier_freq);
usleep(total_time); // <<------- IT IS JUST SLEEPING, DOING NOTHING!
return 0;
}
Ok. So it seems the reason why we the official android Infrared API is not working, is because the actual implementation in "consumerir.c" is missing.
While the LePro does include a consumerir HAL implementation, it is just a dummy, it does nothing on the hardware.
Ok, so how is then the LeEco Remote Control (package "com.letv.android.remotecontrol" in /system/app/LetvRemoteControl_preinstall/) doing it?
As far as I could see, the Java part of the application is communicating with a service ("com.uei.control.Service") which seems to perform the actual sending of infrared commands.
I can see that in the Intent which binds the service two parameters (using "putExtra()") are given, which sound very interesting:
- intent.putExtra("Port", this.platformParams.getSerialName()); // which is /dev/ttyHSL1 at this point
- intent.putExtra("Baudrate", this.platformParams.getSerialPort()); // which is 230400 at this point
/sys/remote/enable (which is world writable, permissions: 0666 - UPDATE: not world-writable, protected by SELinux: -rw-rw-rw- ubject_r:sysfs_ir_enable:s0 /sys/remote/enable)
(Btw, this sys file is provided by the driver file "kernel/drivers/misc/ir_maxq616.c" in the kernel source and just seems to set a specific GPIO pin to 0 or 1. So I guess this GPIO PIN is wired to the IR chip to turn it on and off).
Summary:
- LeEco Remote uses own proprietary and unknown C implementation (only logs the command and sleeps)
- The Android ConsumerIR HAL is installed, but is just a dummy implementation
- The physical infrared device seems to be a serial device, accessible under /dev/ttyHSL1 (also world-writable)
- Baudrate seems to be 230400
- Infrared chip needs to be enabled by: echo 1 > /sys/remote/enable (needs to be allowed by SE policy or maybe as root)
So I see this options here:
Some of the infrared apps in PlayStore directly use the serial device file (apps must be modified)- Somebody is able to implement the "Consumer IR HAL" driver so that it really sends the the actual command over the serial device (i.e. compile a new "consumerir.default.so" file and replace it in the /system/lib64/hw/ directory with a root file explorer or maybe using a magisk module). This way all standard infrared apps should work without modification.
Unfortunately my own knowledge about serial device communication as well as Infrared transmitter devices is not existing.. Also my latest experiences in C are from several years ago.
But maybe the findings in this posting might help somebody else to implement this (app developers as well as platform developers).
So if you know somebody, feel free to spread the link to this thread.
John
Last edited: