Arduino's 'bootload' their programs themselves via a USB-to-serial interface.
So, if you loaded an Arduino ATmega168 bootloader (there are several to choose from), it would do exactly what you want.
This Arduino Bootloader page explains how to do it, and gives links to bootloader source code.
Remember, there are several different ATmega's used in Arduino' so be sure to get the ATmega168 version.
An ATmega88/168/328 has two Flash memory sections called 'Read-While-Write' (RWW) and 'No Read-While-Write' (NRWW). They are described in Section "27.4 Read-While-Write and No Read-While-Write Flash Sections" of the ATmega48/88/168/328 manual.
The difference between the two Flash sections is an ATmega88/168/328 can run a program in NRWW at the same time as programming RWW Flash. However, the Atmega CPU is halted when NRWW Flash is being programmed, i.e. it will not run any program while NRWW in being programmed. So NRWW Flash memory can only be programmed using an external programmer.
Further, the manual warns against trying to read RWW Flash while it is being programmed, for example the CPU must not try to execute a program from RWW Flash while RWW Flash is being programmed:
During an on- going programming, the software must ensure that the RWW
section never is being read. If the user software is trying to read
code that is located inside the RWW section (i.e., by a call/jmp/lpm
or an interrupt) during programming, the software might end up in an
It continues to explain that interrupts should either be disabled, or be handled within the same NRWW section as the bootloader. So, the existing UART code, and bootloader code will need to be combined, and the combination must be contained completely within the NRWW section, or it won't reliably work to reprogram the 'application' in RWW Flash,
Also ATmega has its Flash memory partitioned into two areas which can protected using fuse bits, independently of the other, from being accidentally overwritten. It might be worth using this feature to protect the UART interrupt routine and bootloader from accidental damage.
The problem of loading code is not solved by an in-Flash bootloader alone. You will also need a program on the host PC to upload the program. Many ATmega tool chains, including the Arduino IDE, use avrdude. If you install that, and follow the documentation, avrdude will upload to an Arduino bootloader.
The good thing about this approach is you can install the Arduino IDE on a machine, and test the entire process using a toolchain which is known to work, and supports ATmega168. So it should be relatively straightforward to get it working.
There is also a AVR bootloader project called kavr which claims to be only 512bytes. I have no experience of it.