"""Parser for the Android usage history (usage-history.xml) files."""
import os
from defusedxml import ElementTree
from dfdatetime import java_time as dfdatetime_java_time
from plaso.containers import events
from plaso.lib import errors
from plaso.parsers import interface
from plaso.parsers import manager
[docs]
class AndroidAppUsageEventData(events.EventData):
"""Android application usage event data.
Attributes:
component (str): name of the individual component of the application.
last_resume_time (dfdatetime.DateTimeValues): date and time the application
was last resumed.
package (str): name of the Android application.
"""
DATA_TYPE = "android:app_usage"
[docs]
def __init__(self):
"""Initializes event data."""
super().__init__(data_type=self.DATA_TYPE)
self.component = None
self.last_resume_time = None
self.package = None
[docs]
class AndroidAppUsageParser(interface.FileObjectParser):
"""Parses the Android usage history (usage-history.xml) file."""
NAME = "android_app_usage"
DATA_FORMAT = "Android usage history (usage-history.xml) file"
_HEADER_READ_SIZE = 128
[docs]
def ParseFileObject(self, parser_mediator, file_object):
"""Parses an Android usage-history file-like object.
Args:
parser_mediator (ParserMediator): mediates interactions between parsers
and other components, such as storage and dfVFS.
file_object (dfvfs.FileIO): file-like object.
Raises:
WrongParser: when the file cannot be parsed.
"""
data = file_object.read(self._HEADER_READ_SIZE)
if not data.startswith(b"<?xml"):
raise errors.WrongParser("Not an Android usage history file [not XML]")
_, _, data = data.partition(b"\n")
if not data.startswith(b"<usage-history"):
raise errors.WrongParser(
"Not an Android usage history file [wrong XML root key]"
)
# The current offset of the file-like object needs to point at the start of
# the file for ElementTree to parse the XML data correctly.
file_object.seek(0, os.SEEK_SET)
xml = ElementTree.parse(file_object)
root_node = xml.getroot()
for application_node in root_node:
package_name = application_node.get("name")
for part_node in application_node.iter():
if part_node.tag != "comp":
continue
last_resume_time = part_node.get("lrt")
if last_resume_time is None:
parser_mediator.ProduceExtractionWarning(
"missing last resume time."
)
continue
try:
last_resume_time = int(last_resume_time, 10)
except ValueError:
parser_mediator.ProduceExtractionWarning(
f"unsupported last resume time: {last_resume_time:s}."
)
continue
event_data = AndroidAppUsageEventData()
event_data.component = part_node.get("name")
event_data.last_resume_time = dfdatetime_java_time.JavaTime(
timestamp=last_resume_time
)
event_data.package = package_name
parser_mediator.ProduceEventData(event_data)
manager.ParsersManager.RegisterParser(AndroidAppUsageParser)