Analysis of the Hisense Vidaa remote app (APK) to understand TV communication protocol.
| Item | Value |
|---|---|
| MQTT Port | 36669 |
| Protocol | MQTT v3.1.1 over TLS |
| P12 Password | 186e990688070325a1c4b0ce275d2388 |
| Client keystore | res/3R.p12 (cert CN=VidaaAppAndroidV01 + key) |
| Truststore | res/dM.bks (root CN=RemoteCA, alias mykey) |
- Port: 36669
- Protocol: MQTT v3.1.1 over TLS
- TLS Version: TLSv1.2+ (self-signed certificate, no hostname verification)
The TV requires mutual TLS (mTLS) with a client certificate.
Certificate Details:
Subject: C=CN, ST=shandong, O=hh, OU=multimedia, CN=VidaaAppAndroidV01
Issuer: C=CN, ST=shandong, L=qingdao, O=hh, OU=multimedia, CN=RemoteCA
Validity: Jun 27, 2024 - Jun 25, 2034
Serial: 12 (0xc)
P12 Password: 186e990688070325a1c4b0ce275d2388
Extract certificate from APK:
# 1. Unzip APK and find the P12 file
unzip -l vidaa.apk | grep -i p12
# Usually at: assets/client_mobile_android.p12
# 2. Extract certificate and key
openssl pkcs12 -in client_mobile_android.p12 -out client_cert.pem -clcerts -nokeys \
-passin pass:186e990688070325a1c4b0ce275d2388
openssl pkcs12 -in client_mobile_android.p12 -out client_key.pem -nocerts -nodes \
-passin pass:186e990688070325a1c4b0ce275d2388Note on keystore file names: newer APK builds ship the keystores under obfuscated resource names, e.g.
res/3R.p12(client keystore) andres/dM.bks(truststore), rather thanassets/client_mobile_android.p12. The client cert/key and the P12 password are unchanged across these builds.
The app validates the TV's server certificate against a bundled BouncyCastle
truststore (res/dM.bks, alias mykey). It contains a single self-signed root,
RemoteCA, which is also the issuer of the client certificate above — so both
sides of the mTLS handshake are anchored to the same private CA.
Subject = Issuer: C=CN, ST=shandong, L=qingdao, O=hh, OU=multimedia, CN=RemoteCA
Validity: Apr 19, 2018 - Apr 13, 2043 (CA:TRUE, self-signed)
Serial: B925FCE67D5D45C3
The RemoteCA PEM is bundled at certs/remote_ca.pem. The client cert verifies
against it directly:
openssl verify -CAfile certs/remote_ca.pem certs/vidaa_client.pem # => OKThe .bks is a BKS v1 store (SHA-1 HMAC, 1904 iterations). Its single entry is a
BksTrustedCertEntry (alias mykey, no private key), stored in cleartext, so the
truststore can be parsed without the JVM/BouncyCastle by locating the
00 05 "X.509" length-prefixed blocks. Its integrity password is multiscreen123
(getClientKeyPass), confirmed by opening it with pyjks:
import jks
jks.bks.BksKeyStore.load("res/dM.bks", "multiscreen123") # opens; entry alias "mykey"The native lib exposes these via JNI getters under
com.universal.remote.multicomm.sdk.ConnectUtils. The username/password are the
static MQTT login; the others are TLS keystore passphrases, not MQTT creds:
| JNI getter | Value | Role |
|---|---|---|
getUserName |
hisenseservice (b64 aGlzZW5zZXNlcnZpY2U=) |
static MQTT username |
getUserPass |
multimqttservice (b64 bXVsdGltcXR0c2VydmljZQ==) |
static MQTT password |
getClientKeyPass |
multiscreen123 (b64 bXVsdGlzY3JlZW4xMjM=) |
BKS truststore password (dM.bks) |
getNewClientKeyPass |
ayd6afbj2huf3 (b64 YXlkNmFmYmoyaHVmMw==) |
unused for shipped keystores |
getNewClientP12Password |
186e990688070325a1c4b0ce275d2388 |
P12 store password (3R.p12); opens store + key bag + MAC |
getNewClientKeyPassword |
441a14046a67f604bb7cdb85b6783c0f ⚠ reconstructed |
intended client key-entry password; unused for shipped keystores |
Tested with cryptography/openssl (PKCS12) and pyjks (BKS):
| Getter / value | p12 store+MAC | p12 key bag | BKS truststore |
|---|---|---|---|
getClientKeyPass = multiscreen123 |
✗ | ✗ | ✓ |
getNewClientKeyPass = ayd6afbj2huf3 |
✗ | ✗ | ✗ |
getNewClientP12Password = 186e99… |
✓ | ✓ | ✗ |
getNewClientKeyPassword = 441a… |
✗ | ✗ | ✗ |
- The PKCS12 client keystore (
3R.p12) uses a single password (186e99…) for the store, the shrouded key bag, and the MAC. - The BKS truststore (
dM.bks) usesmultiscreen123. ayd6afbj2huf3and441a…unlock nothing in the shipped keystores — they appear vestigial (likely leftover from when store/key passwords were separate).
getNewClientP12Password and getNewClientKeyPassword are byte-identical functions
that each NewStringUTF-return a fixed 32-byte obfuscated seed from .rodata
(GPB\x13OOHB… and DDG\x17GDHDB… respectively); the seed→string transform runs in
the Baidu-packed Java.
186e99…is empirically confirmed — it decrypts3R.p12(and was also seen via logcat). It is not a simple MD5/XOR of its seed.441a14046a67f604bb7cdb85b6783c0fis reconstructed, not 100% confirmed. Using the known (p12 seed →186e99…) pair, the transform is a bijection over a 16-symbol alphabet → the 16 hex digits. 15 of 16 symbols are observed directly; the key seed's one new symbol (0x12) maps to the one unassigned digit (f) by elimination. It reproduces186e99…exactly but cannot be ground-truth-verified because no shipped artifact is encrypted with it. Definitive confirmation requires a runtime hook (Frida/logcat) onConnectUtils.getNewClientKeyPassword().
Dynamic Credentials (newer VIDAA TVs - REQUIRED):
The native library generates dynamic credentials using the following algorithm:
import hashlib
import time
# Constants from libmqttcrypt.so
PATTERN = "38D65DC30F45109A369A86FCE866A85B" # From getInfo()/getSalt()
VALUE_SUFFIX = "h!i@s#$v%i^d&a*a" # Obfuscated "hisvidaa"
XOR_CONST = 0x5698_1477_2b03_a968 # For username obfuscation
def md5(s):
return hashlib.md5(s.encode()).hexdigest().upper()
def generate_credentials(uuid, brand="his", operation="vidaacommon", timestamp=None):
"""
Generate MQTT credentials for Hisense VIDAA TV.
Args:
uuid: Device identifier (MAC format like "AA:BB:CC:DD:EE:FF")
brand: "his" for Hisense
operation: "vidaacommon" or "vidaavoice"
timestamp: Unix timestamp in seconds (default: current time)
Returns:
(client_id, username, password)
"""
if timestamp is None:
timestamp = int(time.time())
# Step 1: Calculate race = pattern$uuid, then MD5
race = f"{PATTERN}${uuid}"
race_md5 = md5(race)[:6] # First 6 chars, uppercase
# Step 2: Build client_id
client_id = f"{uuid}${brand}${race_md5}_{operation}_001"
# Step 3: Build username = brand$XOR(timestamp)
xor_time = timestamp ^ XOR_CONST
username = f"{brand}${xor_time}"
# Step 4: Build value for password
time_sum = sum(int(d) for d in str(timestamp))
remainder = time_sum % 10
value = f"{brand}{remainder}{VALUE_SUFFIX}"
value_md5 = md5(value)[:6]
# Step 5: Password = MD5(timestamp$value_md5)
password = md5(f"{timestamp}${value_md5}")
return client_id, username, passwordExample Generated Credentials:
Input:
UUID: 56:b8:88:4e:f7:19
Timestamp: 1766974704
Output:
Pattern: 38D65DC30F45109A369A86FCE866A85B
Race: 38D65DC30F45109A369A86FCE866A85B$56:b8:88:4e:f7:19
Race MD5[:6]: 256DBF
Client ID: 56:b8:88:4e:f7:19$his$256DBF_vidaacommon_001
Username: his$6239759786168176024
Value: his1h!i@s#$v%i^d&a*a
Value MD5[:6]: 56FAC6
Password: C3BA44782E18ABF4892AC44D79A622D2
Format: /remoteapp/tv/{service}/{client_id}/actions/{action}
| Service | Actions |
|---|---|
remote_service |
sendkey |
ui_service |
sourcelist, gettvstate, changesource, launchapp, applist, authenticationcode, vidaa_app_connect, login_each_other_info |
platform_service |
getvolume, changevolume |
IMPORTANT: Wildcard subscriptions (#, +) are DENIED by the TV. You must subscribe to specific topics.
Format: /remoteapp/mobile/{client_id}/{service}/data/{data_type}
| Topic | Data |
|---|---|
{base}/ui_service/data/authentication |
PIN dialog shown (empty payload) |
{base}/ui_service/data/authenticationcodetoast |
PIN dialog toast |
{base}/ui_service/data/authenticationcodeclose |
PIN dialog closed |
{base}/ui_service/data/tokenissuance |
Auth tokens after PIN entry |
{base}/ui_service/data/state |
TV state info |
{base}/ui_service/data/sourcelist |
Available sources |
{base}/ui_service/data/applist |
Available apps |
{base}/platform_service/data/tokenissuance |
Alt token topic |
Where {base} = /remoteapp/mobile/{client_id}
Broadcast topics (also requires specific subscription):
/remoteapp/mobile/broadcast/ui_service/state- TV state broadcasts
New devices must be paired with the TV before commands will work. Pairing involves displaying a PIN on the TV screen which the user enters in the app.
1. MQTT Connect → Establish TLS connection with client cert
2. Subscribe → Subscribe to response topics
(wildcards were long believed refused; a 2026-08-04
probe found /remoteapp/# both granted and delivering)
3. vidaa_app_connect → Trigger PIN dialog on TV
4. ← authentication → TV confirms PIN is displayed
OR
← vidaa_app_connect {"connect_result":1} → request accepted. Observed
INSTEAD of the authentication push when the client is already
authorized, in which case no PIN is displayed at all.
5. User enters PIN → Read 4-digit PIN from TV screen
6. authenticationcode → Send {"authNum": "XXXX"}
7. ← tokenissuance → Receive access/refresh tokens
8. Commands work → UUID is now authorized
{
"app_version": 2,
"connect_result": 0,
"device_type": "Mobile App"
}{
"type": "login",
"tvLogin": false,
"tvDeviceId": "",
"tvCountry": "",
"mobileLogin": true,
"mobileDeviceId": "aa:bb:cc:dd:ee:ff"
}{"authNum": "1234"}{
"accesstoken": "<token_string>",
"accesstoken_duration_day": 30,
"accesstoken_time": 1735432800,
"refreshtoken": "<token_string>",
"refreshtoken_duration_day": 365,
"refreshtoken_time": 1735432800
}- PIN dialog timeout: ~60 seconds
- Enter PIN quickly before timeout
- TV may have maximum paired device limit
- Already-paired UUIDs skip PIN step (receive
authenticationmessage but no PIN shown)
KEY_UP,KEY_DOWN,KEY_LEFT,KEY_RIGHTKEY_OK(Enter/Select)KEY_RETURNS(Back)KEY_MENU,KEY_HOME,KEY_EXIT
KEY_VOLUMEUP,KEY_VOLUMEDOWNKEY_MUTE,KEY_MUTE_LONG_PRESSKEY_VOICEUP,KEY_VOICEDOWN
KEY_PLAY,KEY_PAUSE,KEY_STOPKEY_FORWARDS,KEY_BACK(rewind)
KEY_0throughKEY_9KEY_CHANNELDOT
KEY_CHANNELUP,KEY_CHANNELDOWN
KEY_RED,KEY_GREEN,KEY_YELLOW,KEY_BLUE
KEY_LEFTMOUSEKEYSKEY_UDDLEFTMOUSEKEYS,KEY_UDULEFTMOUSEKEYSKEY_ZOOMIN,KEY_ZOOMOUT
KEY_POWERKEY_OK_LONG_PRESSKEY_SUBTITLE
| ID | Source |
|---|---|
| 0 | TV |
| 1 | AV |
| 2 | Component |
| 3 | HDMI1 |
| 4 | HDMI2 |
| 5 | HDMI3 |
| 6 | HDMI4 |
{"sourceid": "3"}/remoteapp/tv/ui_service/{client}/actions/launchapp
// Netflix
{"name":"Netflix","urlType":37,"storeType":0,"url":"netflix"}
// YouTube
{"name":"YouTube","urlType":37,"storeType":0,"url":"youtube"}
// Amazon Prime
{"name":"Amazon","urlType":37,"storeType":0,"url":"amazon"}
// Disney+
{"name":"Disney+","urlType":37,"storeType":0,"url":"disneyplus"}
// Plex
{"name":"Plex","urlType":37,"storeType":0,"url":"plex"}// Certificate passwords
getClientKeyPass() // Client key password
getNewClientKeyPassword() // New client key password
getNewClientP12Password() // P12 password: 186e990688070325a1c4b0ce275d2388
// Credential generation
getConnectUser() // Generate connection credentials
getConnectUserVidaaApp() // VidaaApp-specific credentials
getUserName() // Returns username
getUserPass() // Returns password
getSalt() // Returns salt: 38D65DC30F45109A369A86FCE866A85B
getInfo() // Same as getSalt()| Obfuscated | Original | Purpose |
|---|---|---|
p4/a |
BasicMqttManager.java |
Base MQTT management |
p4/b |
MqttConnectCallBack.java |
Connection callbacks |
p4/c |
MQTT singleton manager | Main MQTT client |
p4/d |
Secondary MQTT manager | Voice/BLE connections |
x4/a |
TopicToTvManager.java |
Topic path builder |
y3/b |
Main remote controller | Remote control logic |
ConnectUtils |
Native crypto interface | JNI to libmqttcrypt.so |
ConnectAccountBean |
Connection credentials | clientId, userName, passWord |
ConnectBean |
Connection payload | app_version, connect_result, device_type |
SdkConnectManager |
SDK connection manager | Handles auth flow, tokens |
SdkMqttPublishManager |
MQTT publish manager | Sends commands to TV |
LoginEachOtherInfoBean |
Login handshake | mType, mMobileDeviceId, etc. |
- TLS with self-signed cert - App uses
CERT_NONEverification - Credentials in native lib - Base64 encoded but easily extractable
- P12 password hardcoded -
186e990688070325a1c4b0ce275d2388 - Dynamic client ID - Generated with UUID + timestamp pattern
- No additional encryption - Payloads sent as plain JSON
- Shared certificate - All app instances use same VidaaAppAndroidV01 cert
Topic: /remoteapp/tv/remote_service/{client_id}/actions/sendkey
Payload: KEY_POWER
Topic: /remoteapp/tv/platform_service/{client_id}/actions/getvolume
Payload: (empty)
Response Topic: /remoteapp/mobile/{client_id}/platform_service/data/volume
Topic: /remoteapp/tv/platform_service/{client_id}/actions/changevolume
Payload: 50
Topic: /remoteapp/tv/ui_service/{client_id}/actions/gettvstate
Payload: (empty)
Response: {"statetype":"remote_launcher"}
Topic: /remoteapp/tv/ui_service/{client_id}/actions/launchapp
Payload: {"name":"Netflix","urlType":37,"storeType":0,"url":"netflix"}
Topic: /remoteapp/tv/ui_service/{client_id}/actions/vidaa_app_connect
Payload: {"app_version":2,"connect_result":0,"device_type":"Mobile App"}
Topic: /remoteapp/tv/ui_service/{client_id}/actions/authenticationcode
Payload: {"authNum":"1234"}
| File | Purpose |
|---|---|
tv_remote.py |
Main TV remote control client |
pair_new_client.py |
Pair new UUID with TV |
capture_pairing.py |
Debug/capture MQTT traffic |
test_connection.py |
Test TV connectivity |
pyvidaa/credentials.py |
Credential generation |
certs/fresh_cert.pem |
Client certificate |
certs/fresh_key.pem |
Client private key |
- Ensure TV and client are on same network
- Check TV IP address hasn't changed
- Verify certificates are valid (not expired)
- Enter PIN quickly (60 second timeout)
- Try restarting TV if PIN doesn't appear
- TV may have device limit - unpair old devices via TV settings
- Use working UUID (
56:b8:88:4e:f7:19) if pairing fails
- Verify UUID is paired (volume test)
- Check client_id format matches expected pattern
- Ensure subscriptions succeeded (no wildcard denial)
# Capture MQTT traffic
python capture_pairing.py sniff
# Test with working UUID interactively
python capture_pairing.py dual
# Verbose pairing attempt
python pair_new_client.py -vEvery claim below is tagged with how it was established:
[live] observed on hardware · [native] read from libmqttcrypt.so ·
[inferred] reasoned, not proven.
[native] The shipped APK is Baidu-packed: base.apk carries a single dex
holding a 29-class stub (com/sagittarius/v6), with the real code encrypted in
assets/baiduprotect{-sec.dex,1.d.jar,1.i.dex}. The app is also Flutter
(libflutter.so + libapp.so in split_config.arm64_v8a.apk).
Streaming all 154,424,013 bytes of the arm64 split and searching yields 0
occurrences of remoteapp, ui_service, gettvstate or sendkey — so the MQTT
topics are in neither the Dart snapshot nor any native library. apktool on any
split returns the packer stub. Recovering the app's topic list statically would
require unpacking the dex (runtime dump; the phone is not rooted).
libmqttcrypt.so is native, so the packer does not protect it, and everything
below came from it.
[live] This corrects a longstanding assumption. Clearing the app's storage produced a different uuid every time:
| Clear | uuid | client id |
|---|---|---|
| 1 | 88:b8:0a:41:bb:48 |
…$his$C23ED5_vidaacommon_001 |
| 2 | 2b:00:f8:ab:56:e1 |
…$his$BF0310_vidaacommon_001 |
| 3 | 9c:9d:5a:3b:a7:1a |
…$his$3E4900_vidaacommon_001 |
None matches the phone's real wlan0 MAC (26:8f:d4:f0:37:f5). The app mints a
MAC-shaped identifier, persists it, and derives race_md5 from it.
[inferred] The TV therefore cannot be validating against a MAC it already
knows — the client id carries both the uuid and md5(PATTERN$uuid)[:6], so the
check is self-consistent. This suggests any stable, self-consistent identifier is
acceptable, which would make resolving the TV's MAC unnecessary for dynamic
auth. Not yet verified — do not change the library's MAC handling on this alone.
[live] Captured from the library's own logging while the app connected:
uuid: 88:b8:0a:41:bb:48, brand: his, operation: vidaacommon,
time: 1785895169, flag: 1
pattern: 38D65DC30F45109A369A86FCE866A85B
race_md5: C23ED5
client id: 88:b8:0a:41:bb:48$his$C23ED5_vidaacommon_001
username: his$6239759786153422953
value: his9h!i@s#$v%i^d&a*a, value_md5: 68EDF5
password: 7D5A1D30D1EC5944FE46851B2C3C3AB0
pyvidaa's generate_credentials(..., AuthMethod.MODERN) reproduces all three
values byte-for-byte. PATTERN is confirmed unchanged in this build.
[native] The JNI surface differs from the table above:
getConnectUser(520 B) andgetConnectUserVidaaApp(492 B) exist and are undocumented. Both take(uuid, brand, operation, time, flag)and return aConnectAccountBean(String, String, String). They differ in time type —%sversus%lld.getSaltis not exported in this build..rodataholds the format strings behind the algorithm: client id%s%c%s%c%s%c%s%c%s, value%s%d%s, username%s%c%ld, password%ld%c%s.
[native] The value-suffix table is one 48-byte blob = three 16-byte slots:
| Offset | Bytes | pyvidaa |
|---|---|---|
| 0 | h*i&s%e!r^v0i1c9 |
VALUE_SUFFIX_LEGACY |
| 16 | h!i@s#$v%i^d&a*a |
VALUE_SUFFIX_MODERN |
| 32 | 4v5z/y9}.\x02*l5m?e |
absent |
[inferred] The third slot may not be a suffix at all — code at 0x1544 loads
it with ldr q0 while building the string "87v", which looks like
deobfuscation input. Do not add it as a third suffix without evidence.
[native] .rodata also holds 001 and 002 as separate client-id
suffixes, selected at 0x1654 by csel after cmp w23, #1 — so
flag == 1 → "001", otherwise "002". pyvidaa hardcodes _001.
[live] Only flag: 1 was ever observed, so 002 is reachable code that
normal use does not exercise.
[live] The app opens two connections on every start — vidaacommon and
vidaavoice — with the same credentials and different client ids. pyvidaa
opens only the first, which is sufficient for control.
[native] Both obfuscated seeds were re-read from this build and the bijection
re-derived from the confirmed p12 password. It yields
441a14046a67f604bb7cdb85b6783c0f, matching the reconstruction above. This
reproduces rather than upgrades that result: 0x12 → f is still fixed by
elimination, so the caveat stands.
[live] From a tv sniff capture across power-off, power-on and app use:
| Topic | Seen | pyvidaa subscribes |
|---|---|---|
broadcast/ui_service/state |
yes, retained | yes |
broadcast/platform_service/actions/volumechange |
yes | yes |
broadcast/ui_service/data/hotelmodechange |
yes — {"hotel_mode":"off"} |
yes (added 2026-08-04) |
broadcast/platform_service/actions/tvsleep |
never | yes |
broadcast/platform_service/actions/bwsinputdata |
never | no |
{client}/ui_service/data/vidaa_app_connect |
yes — {"connect_result":1} |
yes (added 2026-08-04) |
- The state topic is retained: every new subscriber immediately receives the
last state. Nothing in
pyvidaainspectsmsg.retain. - This TV announces standby as a
fake_sleep_0state on the ordinary broadcast topic and never sendstvsleep. fake_sleep_1is sent while waking, immediately followed byremote_launcher— it does not mean asleep. Matchfake_sleep_0exactly.
[live, 2026-08-04] /remoteapp/# is granted and delivers. Verified by
subscribing to it and then triggering sourcelist without subscribing to that
action's own reply topic: the reply arrived. The previous entry in this document
asserted the opposite ("/remoteapp/# returns SUBACK 0x80"), and the tooling
carried a fallback built on that belief.
The error came from a SUBACK calibration whose callback was never firing, so every subscription — including a known-good topic — looked refused. A test that cannot produce a positive result is not evidence of a negative one.
Practical consequence: the topic surface is directly observable. There is no need to guess names.
[live] With the app's client ids discovered from logcat and ~360 of its topics subscribed before pairing began, a full pairing produced zero messages on any of them, while broadcast topics flowed normally throughout.
That result stands, but the conclusion drawn from it does not: it was recorded alongside the wildcard claim as evidence that the app's topic list is unrecoverable from the TV side. Since wildcards do in fact work, the isolation experiment has not been repeated with a wildcard listener, and until it is, "the broker hides other clients' traffic" is unconfirmed rather than established.
Static analysis remains closed off (the app is packed, and it never logs topics).
[live, 2026-08-04] Every candidate action published to all three services
with three payload shapes, under a wildcard listener. Set: 32A35HUV_0002,
MTK9602, V0002.09.01O.P0814.
| Answers | Silent |
|---|---|
sourcelist, applist, capability (on ui_service/data/…) |
gettvstate, getvolume, vidaaapplist, channellist, deviceinfo, networkinfo, getsysteminfo, getpicturemode, getsoundmode, gettime, getplatformcapbility, picturesetting, soundsetting, txtinputdata, bwsinputdata, and ~15 further guesses |
gettvinfo, getdeviceinfo (on platform_service/data/…) |
gettvstateis not answered at all on this firmware — state arrives only as an unsolicited broadcast. This is the direct confirmation of the designget_state()already had for other reasons.- Silence here is model-specific, not protocol-wide. A different set may well
implement these. It is also not proof for push-only topics:
txtinputdataandbwsinputdataare most likely TV→app notifications, which by their nature cannot be provoked by publishing.
[live] Ten minutes of driving the TV by its own remote:
statetype |
Meaning | Notable fields |
|---|---|---|
fake_sleep_0 |
standby | — |
fake_sleep_1 |
waking, then remote_launcher |
— |
livetv |
watching a channel | channel_name, channel_num, channel_param, sourceid; no sourcename/displayname |
sourceswitch |
external input | sourceid, sourcename, displayname, is_signal |
remote_launcher |
home screen | — |
remote_setting |
a settings menu is open | — |
Across all of it the TV published on only two topics — broadcast/ui_service/state
and broadcast/platform_service/actions/volumechange. Everything it reports, it
reports through statetype.
Note livetv carries no display name, so a consumer mapping it to a source must
use sourceid ("TV"), which is what sourcelist reports as sourcename. That
entry's displayname is "TV Channels▶" and matches nothing in a source list
built from sourcename.