Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add SQLite parser for Android App Launch (SimpleStorage) file #4930

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added log2timeline-20241202T054857.log.gz
Binary file not shown.
17 changes: 17 additions & 0 deletions plaso/data/formatters/android.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,23 @@ short_source: 'LOG'
source: 'Android SMS messages'
---
type: 'conditional'
data_type: 'android:sqlite:app_launch'
message:
- 'Package: {package_name}'
- 'Launch Location Id: {launch_location_id}'
- 'Prediction Ui Surface Id: {prediction_ui_surface_id}'
- 'Prediction Source Id: {prediction_source_id}'
- 'Prediction Rank: {prediction_rank}'
short_message:
- 'Package: {package_name}'
- 'Launch Location Id: {launch_location_id}'
- 'Prediction Ui Surface Id: {prediction_ui_surface_id}'
- 'Prediction Source Id: {prediction_source_id}'
- 'Prediction Rank: {prediction_rank}'
short_source: 'App Launch'
source: 'Android App launch (Simple Storage)'
---
type: 'conditional'
data_type: 'android:sqlite:app_usage'
message:
- 'Package Name: {package_name}'
Expand Down
6 changes: 6 additions & 0 deletions plaso/data/timeliner.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ attribute_mappings:
description: 'Last Active Time'
place_holder_event: false
---
data_type: 'android:sqlite:app_launch'
attribute_mappings:
- name: 'launch_time'
description: 'App Launch Time'
place_holder_event: true
---
data_type: 'android:sqlite:app_usage'
attribute_mappings:
- name: 'start_time'
Expand Down
1 change: 1 addition & 0 deletions plaso/parsers/sqlite_plugins/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
"""Imports for the SQLite database parser plugins."""

from plaso.parsers.sqlite_plugins import android_app_launch
from plaso.parsers.sqlite_plugins import android_app_usage
from plaso.parsers.sqlite_plugins import android_calls
from plaso.parsers.sqlite_plugins import android_hangouts
Expand Down
119 changes: 119 additions & 0 deletions plaso/parsers/sqlite_plugins/android_app_launch.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
# -*- coding: utf-8 -*-
"""SQLite parser plugin for Android App Launch (SimpleStorage) database file."""

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

class AndroidAppLaunch(events.EventData):
"""Android App Launch event data.
Attributes:
date (dfdatetime.DateTimeValues): date and time the app was launch.
package_name (str): The unique package identifier of the app.
launch_location_id (int): Id of location where the app was launch.
prediction_ui_surface_id (int): Id of UI surface where prediction was
made.
prediction_source_id (int): Id that indicates the source of prediction.
prediction_rank (int): A value that indicates the relevance of the
prediction.
id (int): An identifier.
"""

DATA_TYPE = 'android:sqlite:app_launch'

def __init__(self):
"""Initializes event data."""
super(AndroidAppLaunch, self).__init__(data_type=self.DATA_TYPE)
self.launch_time = None
self.package_name = None
self.launch_location_id = None
self.prediction_ui_surface_id = None
self.prediction_source_id = None
self.prediction_rank = None
self.id = None

class AndroidAppLaunchPlugin(interface.SQLitePlugin):
"""
SQLite parser plugin for Android App Launch (SimpleStorage) database files.
"""

NAME = 'android_app_launch'
DATA_FORMAT = 'Android App Launch SQLite database (SimpleStorage) file'

REQUIRED_STRUCTURE = {
'EchoAppLaunchMetricsEvents': frozenset([
'timestampMillis', 'packageName', 'launchLocationId',
'predictionUiSurfaceId', 'predictionSourceId', 'predictionRank', 'id'])
}

QUERIES = [((
'SELECT timestampMillis, packageName, launchLocationId, '
'predictionUiSurfaceId, predictionSourceId, predictionRank, id '
'FROM EchoAppLaunchMetricsEvents'),
'ParseAppLaunchRow'
)]

SCHEMAS = {
'EchoAppLaunchMetricsEvents': (
'CREATE TABLE `EchoAppLaunchMetricsEvents` '
'(`timestampMillis` INTEGER NOT NULL, `packageName` TEXT NOT NULL, '
'`launchLocationId` INTEGER NOT NULL, `predictionUiSurfaceId` '
'INTEGER NOT NULL, `predictionSourceId` INTEGER NOT NULL, '
'`predictionRank` INTEGER NOT NULL, `id` INTEGER PRIMARY '
'KEY AUTOINCREMENT NOT NULL, FOREIGN KEY(`packageName`) REFERENCES '
'`Packages`(`packageName`) ON UPDATE CASCADE ON DELETE CASCADE )'
),
'Packages': (
'CREATE TABLE `Packages` (`packageName` TEXT NOT NULL, '
'`loggablePackageName` TEXT NOT NULL, PRIMARY KEY(`packageName`))'
)
}

def _GetTimeRowValue(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.
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)

# pylint: disable=unused-argument
def ParseAppLaunchRow(
self, parser_mediator, query, row, **unused_kwargs):
"""Parses an account 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 = AndroidAppLaunch()
event_data.launch_time = self._GetTimeRowValue(query_hash, row,
'timestampMillis')
event_data.package_name = self._GetRowValue(query_hash, row, 'packageName')
event_data.launch_location_id = self._GetRowValue(query_hash, row,
'launchLocationId')
event_data.prediction_ui_surface_id = self._GetRowValue(query_hash, row,
'predictionUiSurfaceId')
event_data.prediction_source_id = self._GetRowValue(query_hash, row,
'predictionSourceId')
event_data.prediction_rank = self._GetRowValue(query_hash, row,
'predictionRank')
event_data.id = self._GetRowValue(query_hash, row, 'id')


parser_mediator.ProduceEventData(event_data)

sqlite.SQLiteParser.RegisterPlugin(AndroidAppLaunchPlugin)
Binary file added test_data/SimpleStorage
Binary file not shown.
41 changes: 41 additions & 0 deletions tests/parsers/sqlite_plugins/android_app_launch.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# -*- coding: utf-8 -*-
"""Tests for the SQLite parser plugin for iOS accounts database files."""

import unittest
from plaso.parsers.sqlite_plugins import android_app_launch
from tests.parsers.sqlite_plugins import test_lib


class AndroidAppLaunchPluginTest(test_lib.SQLitePluginTestCase):
"""Tests for the SQLite parser plugin for Android App Launch database file."""

def testParse(self):
"""Tests the ParseAccountRow method."""
plugin = android_app_launch.AndroidAppLaunchPlugin()
storage_writer = self._ParseDatabaseFileWithPlugin(
['SimpleStorage'], plugin)

number_of_event_data = storage_writer.GetNumberOfAttributeContainers(
'event_data')
self.assertEqual(number_of_event_data, 434)

number_of_warnings = storage_writer.GetNumberOfAttributeContainers(
'extraction_warning')
self.assertEqual(number_of_warnings, 0)

expected_event_values = {
'launch_time': '2022-12-04T16:59:28.274+00:00',
'package_name': 'com.android.settings',
'launch_location_id': 4,
'prediction_ui_surface_id': 1,
'prediction_source_id': 3,
'prediction_rank': -1,
'id': 2980
}

event_data = storage_writer.GetAttributeContainerByIndex('event_data', 0)
self.CheckEventData(event_data, expected_event_values)


if __name__ == '__main__':
unittest.main()