My tests have been on a plain vanilla FA 2.3.24+ install and the problem exists in it. It is imperative to fix it in the stable version as well based on the commit in my repo. The current activate_hooks is in itself a workaround on which the deactivate_hooks is based and the comment above the former would attest to this in includes/hooks.inc after the class definition:
/*
Non active hooks are not included in $Hooks array, so we can use special function to
activate.
*/
Only, in this case, at this point of entry into the hooks_invoke even the active hooks are not in it! There are a host of functions that are outside the hooks class definition that create a new instance of the hooks class and then invoke the hooks methods!
Install the extension in the default company and activate it for a non default company alone and then try to de-activate it and see if the tables get dropped.
Only the official dashboard extension has the drop sql in it.
Try to only install the dashboard extension (and not the theme) and then activate it and then de-activate it and you will see that the tables remain. At first I thought that it was to save the tables for a re-installation so that user changes are persistent but then most extensions drop the table if it exists before re-creating them during the activation phase and hence it gives us an illusion of having been deleted during deactivation unless the tables are checked just after deactivation.
The usage of XOR is may be standard even if not very widely used, but the usage of the hook_invoke by callback isn't where a simple if...else could have sufficed.
The real solution however, would be to make sure that the global $Hooks array gets pupulated earlier than this invocation and does not get overwritten thereafter. In fact, we have @kvvaradha to thank as this was investigated during the vetting of his hrm extension based on the dashboard skeleton.
It is pertinent to note that solution may lie in the fact that the extension install and uninstall works perfectly in includes/packages.inc wherein the $Hooks array gets populated with the active extensions in:
if (($ext['active'] == true) && file_exists($path_to_root.'/'.$ext['path'].'/hooks.php'))
{
// we need to include the new hooks file to activate extension
include_once($path_to_root.'/'.$ext['path'].'/hooks.php');
foreach($db_connections as $comp => $db)
activate_hooks($ext['package'], $comp);
}
Perhaps something like this should precede the hooks_invoke in admin/inst_module.php.