[MTCB Android Head Units Q&A] agentdr8:
One would think. But the MCU is what's sending codes via serial to the MicrontekServer service, and that decides what keycodes to generate:

SerialReceiver from MCU -- calls DataProc method

mSerialManager = new SerialManager();
mSerialManager.openPort(6, 38400);
serialreceiver = new SerialReceiver() {

final MicrontekServer this$0;

public void onSerialReceived(byte abyte0[])
{
[B] DataProc(abyte0, abyte0.length);
[/B] }

{
this$0 = MicrontekServer.this;
super();
}
}

DataProc method -- calls cmdProc method

private void DataProc(byte abyte0[], int i)
{
if (i + bufRevCount >= 1024)
{
bufRevCount = 0;
if (i >= 1024)
i = 1024;
}
for (int j = 0; j < i; j++)
serialRevBuf[j + bufRevCount] = abyte0[j];

int k = i + bufRevCount;
int l = 0;
do
{
if (l >= k)
break;
int j1 = getFramePos(serialRevBuf, l, k);
if (j1 == -1)
break;
int k1 = j1 & 0xffff;
int l1 = 0xffff & j1 >> 16;
l = l1 + 1;
[B]cmdProc(serialRevBuf, k1, l1);[/B]
} while (true);
bufRevCount = k - l;
for (int i1 = 0; i1 < bufRevCount; i1++)
serialRevBuf[i1] = serialRevBuf[i1 + l];

}

cmdProc method -- determines what intent to broadcast

private void cmdProc(byte abyte0[], int i, int j)
{
label0:
{
label1:
{
label2:
{
label3:
{
label4:
{
switch (0xff & abyte0[i + 1])
{
case 130:
case 131:
case 146:
case 147:
case 148:
case 149:
case 157:
default:
break;

case 128:
break label0;

case 129:
break label1;

case 132:
break label2;

case 133:
break label3;

case 134:
break label4;

case 254:
powershutdown();
return;

case 241:
powerOn();
return;

case 240:
powerOff();
return;

case 162:
Intent intent19 = new Intent("com.microntek.rds.tastop");
mContext.sendBroadcast(intent19);
return;

case 161:
Intent intent18 = new Intent("com.microntek.rds.tastart");
mContext.sendBroadcast(intent18);
return;

case 160:
mContext.sendBroadcast(new Intent("com.microntek.ajx"));
mContext.sendBroadcast(new Intent("com.microntek.smallbton"));
RunApp(mAjXTopPackage);
mAjXTopPackage = null;
return;

case 159:
mContext.sendBroadcast(new Intent("com.microntek.smallbtoff"));
mContext.startService(new Intent("com.microntek.ajxService"));
mAjXTopPackage = getTopActivityPackageName();
startHome();
return;

case 155:
int i2 = getInt(abyte0, i + 3, 1);
Intent intent17 = new Intent("com.microntek.rds.ta");
boolean flag5;
if (i2 != 0)
flag5 = true;
else
flag5 = false;
intent17.putExtra("state", flag5);
mContext.sendBroadcast(intent17);
return;

case 154:
int l1 = getInt(abyte0, i + 3, 1);
Intent intent16 = new Intent("com.microntek.rds.af");
boolean flag4;
if (l1 != 0)
flag4 = true;
else
flag4 = false;
intent16.putExtra("state", flag4);
mContext.sendBroadcast(intent16);
return;

case 156:
Intent intent15 = new Intent("com.microntek.rds.rt");
intent15.putExtra("pi", getInt(abyte0, i + 3, 2));
intent15.putExtra("rt", getAsciiString(abyte0, i + 5, getInt(abyte0, i + 2, 1)));
mContext.sendBroadcast(intent15);
return;

case 153:
Intent intent14 = new Intent("com.microntek.rds.psn");
intent14.putExtra("pi", getInt(abyte0, i + 3, 2));
intent14.putExtra("psn", getAsciiString(abyte0, i + 5, 8));
mContext.sendBroadcast(intent14);
return;

case 152:
Intent intent13 = new Intent("com.microntek.rds.sta");
intent13.putExtra("pi", getInt(abyte0, i + 3, 2));
intent13.putExtra("pty", getInt(abyte0, i + 5, 1));
boolean flag2;
if (getInt(abyte0, i + 6, 1) != 0)
flag2 = true;
else
flag2 = false;
intent13.putExtra("tp", flag2);
boolean flag3;
if (getInt(abyte0, i + 7, 1) != 0)
flag3 = true;
else
flag3 = false;
intent13.putExtra("ta", flag3);
mContext.sendBroadcast(intent13);
return;

case 151:
Intent intent12 = new Intent("com.microntek.radio.sta");
intent12.putExtra("frequency", getInt(abyte0, i + 3, 4));
intent12.putExtra("signal", getInt(abyte0, i + 7, 1));
boolean flag1;
if (getInt(abyte0, i + 8, 1) != 0)
flag1 = true;
else
flag1 = false;
intent12.putExtra("stereo", flag1);
mContext.sendBroadcast(intent12);
return;

case 150:
int j1 = getInt(abyte0, i + 3, 1);
int k1 = 0;
if (j1 == 0)
{
String s;
if (btLock)
s = "av_phone_volume=";
else
s = "av_volume=";
k1 = android.provider.Settings.System.getInt(getContentResolver(), s, 15);
}
handler.removeMessages(6);
Message message = new Message();
message.what = 6;
message.arg1 = k1;
handler.sendMessageDelayed(message, 300L);
return;

case 158:
Intent intent11 = new Intent("com.microntek.videosignalchange");
int k = getInt(abyte0, i + 3, 2);
int l = 0xff & k >> 8;
int i1 = k & 0xff;
boolean flag;
if (l == 1)
flag = true;
else
flag = false;
intent11.putExtra("sta", flag);
if (i1 == 1)
intent11.putExtra("type", "backview");
else
if (i1 == 2)
intent11.putExtra("type", "avin");
else
if (i1 == 3)
intent11.putExtra("type", "tv");
else
intent11.putExtra("type", "null");
mContext.sendBroadcast(intent11);
return;

case 145:
ipod = am.getParameters("sta_ipod=");
if (mPoweron && ipod.equals("true"))
{
startIpod(0);
return;
}
break;

case 144:
safe = am.getParameters("sta_driving=");
Intent intent10 = new Intent("com.microntek.carstatechange");
intent10.putExtra("type", "SAFE");
mContext.sendBroadcast(intent10);
return;

case 143:
Intent intent9 = new Intent("com.microntek.irkeyUp");
intent9.putExtra("keyCode", 1 + getInt(abyte0, i + 3, 1));
mContext.sendBroadcast(intent9);
return;

case 142:
Intent intent8 = new Intent("com.microntek.irkeyDown");
intent8.putExtra("keyCode", 1 + getInt(abyte0, i + 3, 1));
mContext.sendBroadcast(intent8);
clearMusicClock();
return;
<snip>

The MCU sends out a byte array, which if it matches 142 (after being ANDed with 0xff), sends out the irkeyDown intent, with the keyCode being calculated in the DataProc method. You would think this would affect all KGL devices, but I believe the button circuits are wired differently depending on which faceplate is used.

I think I could probably hook the cmdProc method with my xposed module, and check for certain values and adjust them accordingly to correct the issue. Problem is, my headunit doesn't have mixed up buttons on 1.86, so a lot would be trial and error based on what the values should be.