Skip to content

Commit 38fa61b

Browse files
authored
Added Android application usage SQLite parser plugin (#4881)
1 parent 76f72bc commit 38fa61b

File tree

7 files changed

+143
-0
lines changed

7 files changed

+143
-0
lines changed

plaso/data/formatters/android.yaml

+9
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,15 @@ short_source: 'LOG'
121121
source: 'Android SMS messages'
122122
---
123123
type: 'conditional'
124+
data_type: 'android:sqlite:app_usage'
125+
message:
126+
- 'Package Name: {package_name}'
127+
short_message:
128+
- 'Package Name: {package_name}'
129+
short_source: 'Android app usage'
130+
source: 'Android SQLite App Usage'
131+
---
132+
type: 'conditional'
124133
data_type: 'android:tango:contact'
125134
message:
126135
- '{first_name}'

plaso/data/presets.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ parsers:
66
- android_app_usage
77
- chrome_cache
88
- filestat
9+
- sqlite/android_app_usage
910
- sqlite/android_calls
1011
- sqlite/android_sms
1112
- sqlite/android_turbo

plaso/data/timeliner.yaml

+6
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,12 @@ attribute_mappings:
4848
description: 'Last Active Time'
4949
place_holder_event: false
5050
---
51+
data_type: 'android:sqlite:app_usage'
52+
attribute_mappings:
53+
- name: 'start_time'
54+
description: 'Start Time'
55+
place_holder_event: false
56+
---
5157
data_type: 'android:tango:conversation'
5258
place_holder_event: true
5359
---

plaso/parsers/sqlite_plugins/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# -*- coding: utf-8 -*-
22
"""Imports for the SQLite database parser plugins."""
33

4+
from plaso.parsers.sqlite_plugins import android_app_usage
45
from plaso.parsers.sqlite_plugins import android_calls
56
from plaso.parsers.sqlite_plugins import android_hangouts
67
from plaso.parsers.sqlite_plugins import android_sms
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# -*- coding: utf-8 -*-
2+
"""SQLite parser plugin for Android app_usage database files."""
3+
4+
from dfdatetime import posix_time as dfdatetime_posix_time
5+
6+
from plaso.containers import events
7+
from plaso.parsers import sqlite
8+
from plaso.parsers.sqlite_plugins import interface
9+
10+
11+
class AndroidAppUsage(events.EventData):
12+
"""Android app usage event data.
13+
14+
Attributes:
15+
package_name (str): name of the launched package.
16+
start_time (dfdatetime.DateTimeValues): date and time when the application
17+
was launched.
18+
"""
19+
20+
DATA_TYPE = 'android:sqlite:app_usage'
21+
22+
def __init__(self):
23+
"""Initializes event data."""
24+
super(AndroidAppUsage, self).__init__(data_type=self.DATA_TYPE)
25+
self.package_name = None
26+
self.start_time = None
27+
28+
29+
class AndroidSQLiteAppUsage(interface.SQLitePlugin):
30+
"""SQLite parser plugin for Android application usage database files."""
31+
32+
NAME = 'android_app_usage'
33+
DATA_FORMAT = 'Android app_usage SQLite database (app_usage) file'
34+
35+
REQUIRED_STRUCTURE = {
36+
'events': frozenset(['_id', 'timestamp', 'package_id']),
37+
'packages': frozenset(['_id', 'package_name'])
38+
}
39+
40+
QUERIES = [
41+
('SELECT events.timestamp, packages.package_name FROM events JOIN '
42+
'packages ON packages._id = events.package_id', 'ParseAppUsageRow')]
43+
44+
SCHEMAS = [{
45+
'events': (
46+
'CREATE TABLE "events" (_id INTEGER PRIMARY KEY,timestamp INTEGER '
47+
'NOT NULL,type INTEGER NOT NULL,package_id INTEGER NOT NULL '
48+
'REFERENCES packages(_id) ON UPDATE CASCADE ON DELETE CASCADE, '
49+
'instance_id INTEGER DEFAULT NULL, task_root_package_id INTEGER '
50+
'DEFAULT NULL REFERENCES packages(_id) ON UPDATE CASCADE ON DELETE '
51+
'CASCADE)'),
52+
'packages': (
53+
'CREATE TABLE packages (_id INTEGER PRIMARY KEY,package_name TEXT, '
54+
'UNIQUE(package_name) ON CONFLICT ABORT)')}]
55+
56+
def ParseAppUsageRow(self, parser_mediator, query, row, **unused_kwargs):
57+
"""Parses an event record row.
58+
59+
Args:
60+
parser_mediator (ParserMediator): mediates interactions between parsers
61+
and other components, such as storage and dfVFS.
62+
query (str): query that created the row.
63+
row (sqlite3.Row): row.
64+
"""
65+
query_hash = hash(query)
66+
67+
event_data = AndroidAppUsage()
68+
event_data.package_name = self._GetRowValue(query_hash, row, 'package_name')
69+
70+
timestamp = self._GetRowValue(query_hash, row, 'timestamp')
71+
event_data.start_time = dfdatetime_posix_time.PosixTimeInMilliseconds(
72+
timestamp=timestamp)
73+
74+
parser_mediator.ProduceEventData(event_data)
75+
76+
77+
sqlite.SQLiteParser.RegisterPlugin(AndroidSQLiteAppUsage)

test_data/android_app_usage

1.13 MB
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8 -*-
3+
"""Tests for the Android turbo plugin."""
4+
5+
import unittest
6+
7+
from plaso.parsers.sqlite_plugins import android_app_usage
8+
9+
from tests.parsers.sqlite_plugins import test_lib
10+
11+
12+
class AndroidSQLiteAppUsagePluginTest(test_lib.SQLitePluginTestCase):
13+
"""Tests for the Android app_usage database plugin."""
14+
15+
def testProcess(self):
16+
"""Tests the Process function on an Android app_usage file."""
17+
plugin = android_app_usage.AndroidSQLiteAppUsage()
18+
storage_writer = self._ParseDatabaseFileWithPlugin(
19+
['android_app_usage'], plugin)
20+
21+
number_of_event_data = storage_writer.GetNumberOfAttributeContainers(
22+
'event_data')
23+
self.assertEqual(number_of_event_data, 11545)
24+
25+
number_of_warnings = storage_writer.GetNumberOfAttributeContainers(
26+
'extraction_warning')
27+
self.assertEqual(number_of_warnings, 0)
28+
29+
number_of_warnings = storage_writer.GetNumberOfAttributeContainers(
30+
'recovery_warning')
31+
self.assertEqual(number_of_warnings, 0)
32+
33+
expected_event_values = {
34+
'package_name': 'com.whatsapp',
35+
'start_time': '2023-05-31T01:29:31.577+00:00'
36+
}
37+
event_data = storage_writer.GetAttributeContainerByIndex('event_data', 339)
38+
self.CheckEventData(event_data, expected_event_values)
39+
40+
expected_event_values = {
41+
'package_name': 'com.spotify.music',
42+
'start_time': '2023-06-18T19:29:02.869+00:00'
43+
}
44+
event_data = storage_writer.GetAttributeContainerByIndex('event_data', 8589)
45+
self.CheckEventData(event_data, expected_event_values)
46+
47+
48+
if __name__ == '__main__':
49+
unittest.main()

0 commit comments

Comments
 (0)