KaiOS APIs
You might have already heard that #WhatsApp uses some undocumented APIs that are built into the #KaiOS system. These APIs are powered by the api-daemon
, a daemon which lies at a lower level than Gecko. In this post, I will explain what I have found out about this daemon.
The api-daemon is written in #Rust, and provides several “services”. Each service has two parts, the JS client and the Rust library. The Rust library is linked into the api-daemon executable. The JS clients are accessible via HTTP at http://127.0.0.1:8081/api/v1/<service>/<script>.js
and expose APIs in a lib_<service>
variable. A pre-installed version of the daemon is available in /system/kaios
, but it is copied to /data/local/service
to allow updates without modifying the system partition.
Using the APIs
To demonstrate the usage, let's create a simple app called “Daemon Test”. We will use the telephony
service. Although it is meant to be used to get and set the call state, it seems that real calls have no effect on the reported state. However, the state might reflect ongoing WhatsApp and other VoIP calls.
To use the APIs on KaiOS 2.5, we need the external-api
permission:
manifest.webapp
{
"name": "Daemon Test",
"launch_path": "/index.html",
"description": "An app to demonstrate api-daemon functionality",
"type": "privileged",
"permissions": {
"external-api": {}
}
}
index.html
<!DOCTYPE html>
<html>
<body>
<div id="callstate">Loading...</div>
<script src="http://127.0.0.1:8081/api/v1/shared/core.js"></script>
<script src="http://127.0.0.1:8081/api/v1/shared/session.js"></script>
<script src="http://127.0.0.1:8081/api/v1/telephony/service.js"></script>
<script src="daemon-test.js"></script>
</body>
</html>
We can now use these APIs as described in https://developer.stage.kaiostech.com/docs/sfp-3.0/09.migration-from-2.5/next-new-apis/daemon-api/telephony/daemon-api-telephony/, except that we will need to replace localhost
with localhost:8081
. Note that the documentation is for KaiOS 3.0, but on 2.5 only the telephony
, libsignal
(for WhatsApp) and tcpsocket
services are available. You can also look at the api-daemon source code, which is also only for KaiOS 3.0.
First, we need to set up a sess
variable that stores the api-daemon session:
var sess = new lib_session.Session();
Then, we write some handlers that get called when the session connects/disconnects:
function onsessionconnected(){
lib_telephony.TelephonyService.get(sess).then(function(service){
/* handle session connect */
});
}
function onsessiondisconnected(){
/* handle session disconnect */
}
And after that we try connecting.
sess.open('websocket', 'localhost:8081', 'secrettoken',
{onsessionconnected, onsessiondisconnected}, true);
Here is the final code:
daemon-test.js
window.onerror = function(e){
// Error handling
alert(e);
}
var sess = new lib_session.Session();
var el_state = document.getElementById('callstate');
var toggleCallState = null;
function callStateChangeCallback(state){
el_state.textContent = state;
}
// Set up session callbacks
var onsessionconnected = function(){
alert('Session connected');
// Get an instance of the telephony manager
lib_telephony.TelephonyService.get(sess).then(function(manager){
alert('Got Telephony manager');
try {
// Show the call state
manager.callState.then(function(state){
el_state.textContent = 'Call state: ' + state;
});
// Show it again if it changes
manager.addEventListener(manager.CALLSTATE_CHANGE_EVENT, function(state){
el_state.textContent = 'Call state: ' + state;
});
toggleCallState = function(){
// Get the call state
manager.callState.then(function(state){
// Set the call state
manager.callState = state ? 0 : 1;
alert(state ? 'Idle' : 'Calling');
});
}
} catch(e) {
alert(e);
}
}).catch(function(e){
alert('Failed to get Telephony manager: ' + JSON.stringify(e));
toggleCallState = null;
});
}
var onsessiondisconnected = function(){
alert('Disconnected');
toggleCallState = null;
}
// Initialize the api-daemon session
sess.open('websocket', 'localhost:8081', 'secrettoken',
{onsessionconnected, onsessiondisconnected}, true);
window.addEventListener('keydown', function(e){
if(e.key == 'Call'){
// Toggle the state if 'Call' is pressed and the session is ready
if(typeof toggleCallState === 'function') toggleCallState();
}
});
There is a lot more to this. I will also make a wiki page, and some more posts about things such as the updater-daemon
and remote services.
More tags: #ApiDaemon