Renamed and refactored ADB exceptions into an exceptions module.

ADBExecutionError is now called ADBProcessError to align with the intent
of it being thrown when a CalledProcessError occurs. This exception was
moved to exceptions.py where it should live with other ADB specific
exceptions.

ADBException was created as a generic exception to throw for any ADB
related issues. ADBExecutionError now inherits from ADBException since
it is a specialized version of ADBException.
Enhance ADB executable detection to be more platform independent and robust

Removed the use of the linux 'which' command that was used to determine the
full path to the adb executable. Now the environment variable PATH is
split into a list. The list is iterated over until ADB is found based on a
isFile test.

A adb_self_test method was also created to ensure the executable is
properly functioning. ADB is first tested to see if it is executable
then an 'adb version' command is run to see if adb returns a zero
exit status.

An ADBException or ADBProcessError can be thrown depending on what part of
the self test fails.