"""SQLite parser plugin for Android Native Downloads database files.
The Android native downloads database file is typically stored as:
com.android.providers.downloads/databases/downloads.db
Also see :
STATUS_* and DESTINATION_* constants:
https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/java/android/provider/Downloads.java
ERROR_*, PAUSED_*, and VISIBILITY_* constants:
https://android.googlesource.com/platform/frameworks/base/+/HEAD/core/java/android/app/DownloadManager.java
"""
from dfdatetime import java_time as dfdatetime_java_time
from plaso.containers import events
from plaso.parsers import sqlite
from plaso.parsers.sqlite_plugins import interface
[docs]
class AndroidNativeDownloadsEventData(events.EventData):
"""Android Native Downloads (DownloadProvider) event data.
Attributes:
current_bytes (int): Number of bytes download so far.
deleted (bool): Set to true if this download is deleted.
Also Removed from the database when MediaProvider database
deletes the metadata associated with this downloaded file.
description (str): The client-supplied description of this download. This
will be displayed in system notifications.
destination (int): Flag that controls download destination. Also see the
DESTINATION_* constants for a list of supported values.
error_msg (str): The column with errorMsg for a failed downloaded.
Used only for debugging purposes.
e_tag (str): ETag of this file.
identifier (int): identifier of the download.
is_visible_in_downloads_ui (int): Whether or not this download should
be displayed in the system's Downloads UI. Defaults to true.
media_provider_uri (str): The URI to corresponding entry in MediaProvider
for this downloaded entry. If an entry is deleted from downloaded list,
it is also deleted from MediaProvider DB.
mime_type (str): Internet Media Type of the downloaded file.
modification_time (dfdatetime.DateTimeValues): Last content modification
date and time of downloaded file.
notification_package (str): Package name associated with notification
of a running download.
status (int): Holds one of the STATUS_* constants.
If an error occurred, this holds the HTTP Status Error Code (RFC 2616),
otherwise it holds one of the ERROR_* constants.
If the download is paused, this holds one of the PAUSED_* constants.
saved_to (str): Path to the downloaded file on disk.
title (str): Title of the download.
total_bytes (int): Total size of the download in bytes.
ui_visibility (int): Flags that control if the download is displayed by the
UI. Also see VISIBILITY_* constants for a list of supported values.
uri (str): Downloaded URI.
"""
DATA_TYPE = 'android:sqlite:downloads'
[docs]
def __init__(self):
"""Initializes event data."""
super().__init__(data_type=self.DATA_TYPE)
self.current_bytes = None
self.deleted = None
self.description = None
self.destination = None
self.error_msg = None
self.e_tag = None
self.identifier = None
self.is_visible_in_downloads_ui = None
self.media_provider_uri = None
self.mime_type = None
self.modification_time = None
self.notification_package = None
self.saved_to = None
self.status = None
self.title = None
self.total_bytes = None
self.ui_visibility = None
self.uri = None
[docs]
class AndroidNativeDownloadsPlugin(interface.SQLitePlugin):
"""SQLite parser plugin for Android native downloads database file."""
NAME = 'android_native_downloads'
DATA_FORMAT = 'Android native downloads SQLite database (downloads.db) file'
REQUIRED_STRUCTURE = {
'downloads': frozenset([
'_id', 'uri', '_data', 'mimetype', 'destination', 'visibility',
'status', 'lastmod', 'notificationpackage', 'total_bytes',
'current_bytes', 'etag', 'title', 'description',
'is_visible_in_downloads_ui', 'mediaprovider_uri', 'deleted',
'errorMsg'])}
QUERIES = [(
('SELECT _id, uri, _data, mimetype, destination, visibility, status, '
'lastmod, notificationpackage, total_bytes, current_bytes, etag, '
'title, description, is_visible_in_downloads_ui, mediaprovider_uri, '
'deleted, errorMsg FROM downloads'), 'ParseDownloadsRow')]
SCHEMAS = [{
'android_metadata': (
'CREATE TABLE android_metadata (locale TEXT)'),
'downloads': (
'CREATE TABLE downloads(_id INTEGER PRIMARY KEY AUTOINCREMENT, '
'uri TEXT, method INTEGER, entity TEXT, no_integrity BOOLEAN, '
'hint TEXT, otaupdate BOOLEAN, _data TEXT, mimetype TEXT, '
'destination INTEGER, no_system BOOLEAN, visibility INTEGER, '
'control INTEGER, status INTEGER, numfailed INTEGER, '
'lastmod BIGINT, notificationpackage TEXT, notificationclass TEXT, '
'notificationextras TEXT, cookiedata TEXT, useragent TEXT, '
'referer TEXT, total_bytes INTEGER, current_bytes INTEGER, '
'etag TEXT, uid INTEGER, otheruid INTEGER, title TEXT, '
'description TEXT, scanned BOOLEAN, '
'is_public_api INTEGER NOT NULL DEFAULT 0, '
'allow_roaming INTEGER NOT NULL DEFAULT 0, '
'allowed_network_types INTEGER NOT NULL DEFAULT 0, '
'is_visible_in_downloads_ui INTEGER NOT NULL DEFAULT 1, '
'bypass_recommended_size_limit INTEGER NOT NULL DEFAULT 0, '
'mediaprovider_uri TEXT, deleted BOOLEAN NOT NULL DEFAULT 0, '
'errorMsg TEXT, allow_metered INTEGER NOT NULL DEFAULT 1, '
'allow_write BOOLEAN NOT NULL DEFAULT 0, '
'flags INTEGER NOT NULL DEFAULT 0, '
'mediastore_uri TEXT DEFAULT NULL)'),
'request_headers': (
'CREATE TABLE request_headers(id INTEGER PRIMARY KEY AUTOINCREMENT,'
'download_id INTEGER NOT NULL, '
'header TEXT NOT NULL,value TEXT NOT NULL)'),
'sqlite_sequence': (
'CREATE TABLE sqlite_sequence(name,seq)')}]
def _GetDateTimeRowValue(self, query_hash, row, value_name):
"""Retrieves a date and time value from the row.
Args:
query_hash (int): hash of the query, that uniquely identifies the query
that produced the row.
row (sqlite3.Row): row.
value_name (str): name of the value.
Returns:
dfdatetime.JavaTime: date and time value or None if not available.
"""
timestamp = self._GetRowValue(query_hash, row, value_name)
if timestamp is None:
return None
return dfdatetime_java_time.JavaTime(timestamp=timestamp)
[docs]
def ParseDownloadsRow(self, parser_mediator, query, row, **unused_kwargs):
"""Parses a download row.
Args:
parser_mediator (ParserMediator): mediates interactions between parsers
and other components, such as storage and dfVFS.
query (str): query that created the row.
row (sqlite3.Row): row.
"""
query_hash = hash(query)
event_data = AndroidNativeDownloadsEventData()
event_data.current_bytes = self._GetRowValue(
query_hash, row, 'current_bytes')
event_data.deleted = self._GetRowValue(query_hash, row, 'deleted')
# Description defaults to an empty string.
event_data.description = self._GetRowValue(
query_hash, row, 'description') or None
event_data.destination = self._GetRowValue(query_hash, row, 'destination')
event_data.error_msg = self._GetRowValue(query_hash, row, 'errorMsg')
event_data.e_tag = self._GetRowValue(query_hash, row, 'etag')
event_data.identifier = self._GetRowValue(query_hash, row, '_id')
event_data.is_visible_in_downloads_ui = self._GetRowValue(
query_hash, row, 'is_visible_in_downloads_ui')
event_data.modification_time = self._GetDateTimeRowValue(
query_hash, row, 'lastmod')
event_data.media_provider_uri = self._GetRowValue(
query_hash, row, 'mediaprovider_uri')
event_data.mime_type = self._GetRowValue(query_hash, row, 'mimetype')
event_data.notification_package = self._GetRowValue(
query_hash, row, 'notificationpackage')
event_data.saved_to = self._GetRowValue(query_hash, row, '_data')
event_data.status = self._GetRowValue(query_hash, row, 'status')
event_data.title = self._GetRowValue(query_hash, row, 'title')
event_data.total_bytes = self._GetRowValue(query_hash, row, 'total_bytes')
event_data.ui_visibility = self._GetRowValue(query_hash, row, 'visibility')
event_data.uri = self._GetRowValue(query_hash, row, 'uri')
parser_mediator.ProduceEventData(event_data)
sqlite.SQLiteParser.RegisterPlugin(AndroidNativeDownloadsPlugin)