Autopsy  4.18.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
TimelineSummary.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2020 Basis Technology Corp.
5  * Contact: carrier <at> sleuthkit <dot> org
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 package org.sleuthkit.autopsy.datasourcesummary.datamodel;
20 
21 import java.time.Instant;
22 import java.util.ArrayList;
23 import java.util.Arrays;
24 import java.util.Collections;
25 import java.util.Date;
26 import java.util.HashMap;
27 import java.util.HashSet;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.Set;
31 import java.util.TimeZone;
32 import org.joda.time.Interval;
37 import org.sleuthkit.datamodel.AbstractFile;
38 import org.sleuthkit.datamodel.DataSource;
39 import org.sleuthkit.datamodel.TimelineEvent;
40 import org.sleuthkit.datamodel.TimelineEventType;
41 import org.sleuthkit.datamodel.TimelineFilter.RootFilter;
42 import org.sleuthkit.datamodel.TimelineManager;
43 import org.sleuthkit.datamodel.TskCoreException;
44 import java.util.function.Supplier;
47 
51 public class TimelineSummary implements DefaultUpdateGovernor {
52 
57  public interface DataSourceFilterFunction {
58 
67  RootFilter apply(DataSource dataSource) throws NoCurrentCaseException, TskCoreException;
68  }
69 
70  private static final long DAY_SECS = 24 * 60 * 60;
71  private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS = new HashSet<>(
73 
74  private static final Set<TimelineEventType> FILE_SYSTEM_EVENTS
75  = new HashSet<>(Arrays.asList(
76  TimelineEventType.FILE_MODIFIED,
77  TimelineEventType.FILE_ACCESSED,
78  TimelineEventType.FILE_CREATED,
79  TimelineEventType.FILE_CHANGED));
80 
82  private final Supplier<TimeZone> timeZoneProvider;
84 
88  public TimelineSummary() {
90  () -> TimeZone.getTimeZone(UserPreferences.getTimeZoneForDisplays()),
92  }
93 
102  public TimelineSummary(SleuthkitCaseProvider caseProvider, Supplier<TimeZone> timeZoneProvider, DataSourceFilterFunction filterFunction) {
103  this.caseProvider = caseProvider;
104  this.timeZoneProvider = timeZoneProvider;
105  this.filterFunction = filterFunction;
106  }
107 
108  @Override
109  public boolean isRefreshRequired(ModuleContentEvent evt) {
110  return true;
111  }
112 
113  @Override
114  public boolean isRefreshRequired(AbstractFile file) {
115  return true;
116  }
117 
118  @Override
120  return (evt != null && INGEST_JOB_EVENTS.contains(evt));
121  }
122 
123  @Override
125  return INGEST_JOB_EVENTS;
126  }
127 
140  public TimelineSummaryData getData(DataSource dataSource, int recentDaysNum) throws SleuthkitCaseProviderException, TskCoreException, NoCurrentCaseException {
141  TimeZone timeZone = this.timeZoneProvider.get();
142  TimelineManager timelineManager = this.caseProvider.get().getTimelineManager();
143 
144  // get a mapping of days from epoch to the activity for that day
145  Map<Long, DailyActivityAmount> dateCounts = getTimelineEventsByDay(dataSource, timelineManager, timeZone);
146 
147  // get minimum and maximum usage date by iterating through
148  Long minDay = null;
149  Long maxDay = null;
150  for (long daysFromEpoch : dateCounts.keySet()) {
151  minDay = (minDay == null) ? daysFromEpoch : Math.min(minDay, daysFromEpoch);
152  maxDay = (maxDay == null) ? daysFromEpoch : Math.max(maxDay, daysFromEpoch);
153  }
154 
155  // if no min date or max date, no usage; return null.
156  if (minDay == null || maxDay == null) {
157  return null;
158  }
159 
160  Date minDate = new Date(minDay * 1000 * DAY_SECS);
161  Date maxDate = new Date(maxDay * 1000 * DAY_SECS);
162 
163  // The minimum recent day will be within recentDaysNum from the maximum day
164  // (+1 since maxDay included) or the minimum day of activity
165  long minRecentDay = Math.max(maxDay - recentDaysNum + 1, minDay);
166 
167  // get most recent days activity
168  List<DailyActivityAmount> mostRecentActivityAmt = getMostRecentActivityAmounts(dateCounts, minRecentDay, maxDay);
169 
170  return new TimelineSummaryData(minDate, maxDate, mostRecentActivityAmt, dataSource);
171  }
172 
183  private List<DailyActivityAmount> getMostRecentActivityAmounts(Map<Long, DailyActivityAmount> dateCounts, long minRecentDay, long maxDay) {
184  List<DailyActivityAmount> mostRecentActivityAmt = new ArrayList<>();
185 
186  for (long curRecentDay = minRecentDay; curRecentDay <= maxDay; curRecentDay++) {
187  DailyActivityAmount prevCounts = dateCounts.get(curRecentDay);
188  DailyActivityAmount countsHandleNotFound = prevCounts != null
189  ? prevCounts
190  : new DailyActivityAmount(new Date(curRecentDay * DAY_SECS * 1000), 0, 0);
191 
192  mostRecentActivityAmt.add(countsHandleNotFound);
193  }
194  return mostRecentActivityAmt;
195  }
196 
209  private Map<Long, DailyActivityAmount> getTimelineEventsByDay(DataSource dataSource, TimelineManager timelineManager, TimeZone timeZone)
210  throws TskCoreException, NoCurrentCaseException {
211  RootFilter rootFilter = this.filterFunction.apply(dataSource);
212 
213  // get events for data source
214  long curRunTime = System.currentTimeMillis();
215  List<TimelineEvent> events = timelineManager.getEvents(new Interval(1, curRunTime), rootFilter);
216 
217  // get counts of events per day (left is file system events, right is everything else)
218  Map<Long, DailyActivityAmount> dateCounts = new HashMap<>();
219  for (TimelineEvent evt : events) {
220  long curSecondsFromEpoch = evt.getTime();
221  long curDaysFromEpoch = Instant.ofEpochMilli(curSecondsFromEpoch * 1000)
222  .atZone(timeZone.toZoneId())
223  .toLocalDate()
224  .toEpochDay();
225 
226  DailyActivityAmount prevAmt = dateCounts.get(curDaysFromEpoch);
227  long prevFileEvtCount = prevAmt == null ? 0 : prevAmt.getFileActivityCount();
228  long prevArtifactEvtCount = prevAmt == null ? 0 : prevAmt.getArtifactActivityCount();
229  Date thisDay = prevAmt == null ? new Date(curDaysFromEpoch * 1000 * DAY_SECS) : prevAmt.getDay();
230 
231  boolean isFileEvt = FILE_SYSTEM_EVENTS.contains(evt.getEventType());
232  long curFileEvtCount = prevFileEvtCount + (isFileEvt ? 1 : 0);
233  long curArtifactEvtCount = prevArtifactEvtCount + (isFileEvt ? 0 : 1);
234 
235  dateCounts.put(curDaysFromEpoch, new DailyActivityAmount(thisDay, curFileEvtCount, curArtifactEvtCount));
236  }
237 
238  return dateCounts;
239  }
240 
244  public static class TimelineSummaryData {
245 
246  private final Date minDate;
247  private final Date maxDate;
248  private final List<DailyActivityAmount> histogramActivity;
249  private final DataSource dataSource;
250 
261  TimelineSummaryData(Date minDate, Date maxDate, List<DailyActivityAmount> recentDaysActivity, DataSource dataSource) {
262  this.minDate = minDate;
263  this.maxDate = maxDate;
264  this.histogramActivity = (recentDaysActivity == null) ? Collections.emptyList() : Collections.unmodifiableList(recentDaysActivity);
265  this.dataSource = dataSource;
266  }
267 
271  public Date getMinDate() {
272  return minDate;
273  }
274 
278  public Date getMaxDate() {
279  return maxDate;
280  }
281 
286  public List<DailyActivityAmount> getMostRecentDaysActivity() {
287  return histogramActivity;
288  }
289 
293  public DataSource getDataSource() {
294  return dataSource;
295  }
296  }
297 
301  public static class DailyActivityAmount {
302 
303  private final Date day;
304  private final long fileActivityCount;
305  private final long artifactActivityCount;
306 
314  DailyActivityAmount(Date day, long fileActivityCount, long artifactActivityCount) {
315  this.day = day;
316  this.fileActivityCount = fileActivityCount;
317  this.artifactActivityCount = artifactActivityCount;
318  }
319 
323  public Date getDay() {
324  return day;
325  }
326 
330  public long getFileActivityCount() {
331  return fileActivityCount;
332  }
333 
337  public long getArtifactActivityCount() {
338  return artifactActivityCount;
339  }
340 
341  }
342 }
static final Set< IngestManager.IngestJobEvent > INGEST_JOB_EVENTS
TimelineSummary(SleuthkitCaseProvider caseProvider, Supplier< TimeZone > timeZoneProvider, DataSourceFilterFunction filterFunction)
TimelineSummaryData getData(DataSource dataSource, int recentDaysNum)
List< DailyActivityAmount > getMostRecentActivityAmounts(Map< Long, DailyActivityAmount > dateCounts, long minRecentDay, long maxDay)
Map< Long, DailyActivityAmount > getTimelineEventsByDay(DataSource dataSource, TimelineManager timelineManager, TimeZone timeZone)

Copyright © 2012-2021 Basis Technology. Generated on: Thu Jul 8 2021
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.