Autopsy  4.17.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
GeolocationSummary.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.io.IOException;
22 import java.util.ArrayList;
23 import java.util.Arrays;
24 import java.util.Collections;
25 import java.util.HashMap;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.Set;
29 import java.util.concurrent.ArrayBlockingQueue;
30 import java.util.concurrent.BlockingQueue;
31 import java.util.stream.Collectors;
32 import java.util.stream.Stream;
33 import org.apache.commons.lang3.tuple.Pair;
42 import org.sleuthkit.datamodel.BlackboardArtifact;
43 import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
44 import org.sleuthkit.datamodel.DataSource;
45 
53  public static class CityRecordCount {
54 
55  private final CityRecord cityRecord;
56  private final int count;
57 
65  CityRecordCount(CityRecord cityRecord, int count) {
66  this.cityRecord = cityRecord;
67  this.count = count;
68  }
69 
75  return cityRecord;
76  }
77 
81  public int getCount() {
82  return count;
83  }
84  }
85 
90  public static class CityData {
91 
92  private final CityCountsList mostCommon;
93  private final CityCountsList mostRecent;
94  private final Long mostRecentSeen;
95 
103  CityData(CityCountsList mostCommon, CityCountsList mostRecent, Long mostRecentSeen) {
104  this.mostCommon = mostCommon;
105  this.mostRecent = mostRecent;
106  this.mostRecentSeen = mostRecentSeen;
107  }
108 
113  return mostCommon;
114  }
115 
120  return mostRecent;
121  }
122 
127  public Long getMostRecentSeen() {
128  return mostRecentSeen;
129  }
130  }
131 
137  public static class CityCountsList {
138 
139  private final List<CityRecordCount> counts;
140  private final int otherCount;
141 
150  CityCountsList(List<CityRecordCount> counts, int otherCount) {
151  this.counts = Collections.unmodifiableList(new ArrayList<>(counts));
152  this.otherCount = otherCount;
153  }
154 
159  public List<CityRecordCount> getCounts() {
160  return counts;
161  }
162 
167  public int getOtherCount() {
168  return otherCount;
169  }
170  }
171 
172  // taken from GeoFilterPanel: all of the GPS artifact types.
173  @SuppressWarnings("deprecation")
174  private static final List<ARTIFACT_TYPE> GPS_ARTIFACT_TYPES = Arrays.asList(
175  BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_BOOKMARK,
176  BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_LAST_KNOWN_LOCATION,
177  BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_ROUTE,
178  BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_SEARCH,
179  BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_TRACK,
180  BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_TRACKPOINT,
181  BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF
182  );
183 
184  // all GPS types
185  private static final Set<Integer> GPS_ARTIFACT_TYPE_IDS = GPS_ARTIFACT_TYPES.stream()
186  .map(artifactType -> artifactType.getTypeID())
187  .collect(Collectors.toSet());
188 
190  private final java.util.logging.Logger logger;
191  private final SupplierWithException<ClosestCityMapper, IOException> cityMapper;
192 
196  public interface SupplierWithException<T, E extends Throwable> {
197 
204  T get() throws E;
205  }
206 
211  this(() -> ClosestCityMapper.getInstance(), SleuthkitCaseProvider.DEFAULT, Logger.getLogger(GeolocationSummary.class.getName()));
212  }
213 
223  this.cityMapper = cityMapper;
224  this.provider = provider;
225  this.logger = logger;
226  }
227 
231  public List<ARTIFACT_TYPE> getGeoTypes() {
232  return GPS_ARTIFACT_TYPES;
233  }
234 
235  @Override
236  public Set<Integer> getArtifactTypeIdsForRefresh() {
237  return GPS_ARTIFACT_TYPE_IDS;
238  }
239 
251  private boolean greaterThanOrEqual(Long minTime, Long time) {
252  if (minTime != null && time != null && time >= minTime) {
253  return true;
254  } else {
255  return false;
256  }
257  }
258 
259  private static final Pair<Integer, Integer> EMPTY_COUNT = Pair.of(0, 0);
260 
271  private Pair<Integer, Integer> getCounts(List<MapWaypoint> points, Long minTime) {
272 
273  if (points == null) {
274  return EMPTY_COUNT;
275  }
276 
277  return points.stream().reduce(
278  EMPTY_COUNT,
279  (total, w) -> Pair.of(total.getLeft() + 1, total.getRight() + (greaterThanOrEqual(minTime, w.getTimestamp()) ? 1 : 0)),
280  (pair1, pair2) -> Pair.of(pair1.getLeft() + pair2.getLeft(), pair1.getRight() + pair2.getRight()));
281  }
282 
283 
284  private static final long DAY_SECS = 24 * 60 * 60;
285 
300  public CityData getCityCounts(DataSource dataSource, int daysCount, int maxCount)
301  throws SleuthkitCaseProviderException, GeoLocationDataException, InterruptedException, IOException {
302 
303  ClosestCityMapper closestCityMapper = ClosestCityMapper.getInstance();
304 
305  List<MapWaypoint> dataSourcePoints = getPoints(dataSource);
306 
307  Map<CityRecord, List<MapWaypoint>> allCityPoints = new HashMap<>();
308  List<MapWaypoint> others = new ArrayList<>();
309  Long mostRecent = null;
310 
311  for (MapWaypoint pt : dataSourcePoints) {
312  CityRecord city = closestCityMapper.findClosest(new CityRecord(null, null, null, pt.getX(), pt.getY()));
313  Long curTime = pt.getTimestamp();
314  if (curTime != null && (mostRecent == null || curTime > mostRecent)) {
315  mostRecent = curTime;
316  }
317 
318  if (city == null) {
319  others.add(pt);
320  } else {
321  List<MapWaypoint> cityPoints = allCityPoints.get(city);
322  if (cityPoints == null) {
323  cityPoints = new ArrayList<>();
324  allCityPoints.put(city, cityPoints);
325  }
326 
327  cityPoints.add(pt);
328  }
329  }
330 
331  final Long mostRecentMinTime = (mostRecent == null) ? null : mostRecent - daysCount * DAY_SECS;
332 
333  // pair left is total count and right is count within range (or mostRecent is null)
334  Map<CityRecord, Pair<Integer, Integer>> allCityCounts = allCityPoints.entrySet().stream()
335  .collect(Collectors.toMap((e) -> e.getKey(), (e) -> getCounts(e.getValue(), mostRecentMinTime)));
336 
337  List<CityRecordCount> mostCommonCounts = allCityCounts.entrySet().stream()
338  .map(e -> new CityRecordCount(e.getKey(), e.getValue().getLeft()))
339  .sorted((a, b) -> -Integer.compare(a.getCount(), b.getCount()))
340  .limit(maxCount)
341  .collect(Collectors.toList());
342 
343  List<CityRecordCount> mostRecentCounts = allCityCounts.entrySet().stream()
344  .map(e -> new CityRecordCount(e.getKey(), e.getValue().getRight()))
345  .sorted((a, b) -> -Integer.compare(a.getCount(), b.getCount()))
346  .limit(maxCount)
347  .collect(Collectors.toList());
348 
349  Pair<Integer, Integer> otherCounts = getCounts(others, mostRecentMinTime);
350  int otherMostCommonCount = otherCounts.getLeft();
351  int otherMostRecentCount = otherCounts.getRight();
352 
353  return new CityData(
354  new CityCountsList(mostCommonCounts, otherMostCommonCount),
355  new CityCountsList(mostRecentCounts, otherMostRecentCount),
356  mostRecentMinTime);
357  }
358 
362  private static class PointFetcher extends AbstractWaypointFetcher {
363 
364  private final BlockingQueue<List<MapWaypoint>> asyncResult;
365 
374  public PointFetcher(BlockingQueue<List<MapWaypoint>> asyncResult, GeoFilter filters) {
375  super(filters);
376  this.asyncResult = asyncResult;
377  }
378 
379  @Override
380  public void handleFilteredWaypointSet(Set<MapWaypoint> mapWaypoints, List<Set<MapWaypoint>> tracks, List<Set<MapWaypoint>> areas, boolean wasEntirelySuccessful) {
381  Stream<List<Set<MapWaypoint>>> stream = Stream.of(
382  Arrays.asList(mapWaypoints),
383  tracks == null ? Collections.emptyList() : tracks,
384  areas == null ? Collections.emptyList() : areas);
385 
386  List<MapWaypoint> wayPoints = stream
387  .flatMap((List<Set<MapWaypoint>> list) -> list.stream())
388  .flatMap((Set<MapWaypoint> set) -> set.stream())
389  .collect(Collectors.toList());
390 
391  // push to blocking queue to continue
392  try {
393  asyncResult.put(wayPoints);
394  } catch (InterruptedException ignored) {
395  // ignored cancellations
396  }
397  }
398  }
399 
409  private List<MapWaypoint> getPoints(DataSource dataSource) throws SleuthkitCaseProviderException, GeoLocationDataException, InterruptedException {
410  // make asynchronous callback synchronous (the callback nature will be handled in a different level)
411  // see the following: https://stackoverflow.com/questions/20659961/java-synchronous-callback
412  final BlockingQueue<List<MapWaypoint>> asyncResult = new ArrayBlockingQueue<>(1);
413 
414  GeoFilter geoFilter = new GeoFilter(true, false, 0, Arrays.asList(dataSource), GPS_ARTIFACT_TYPES);
415 
417  Arrays.asList(dataSource),
419  true,
420  -1,
421  false,
422  new PointFetcher(asyncResult, geoFilter));
423 
424  return asyncResult.take();
425  }
426 }
CityData getCityCounts(DataSource dataSource, int daysCount, int maxCount)
static List< Waypoint > getAllWaypoints(SleuthkitCase skCase)
final SupplierWithException< ClosestCityMapper, IOException > cityMapper
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
PointFetcher(BlockingQueue< List< MapWaypoint >> asyncResult, GeoFilter filters)
Pair< Integer, Integer > getCounts(List< MapWaypoint > points, Long minTime)
GeolocationSummary(SupplierWithException< ClosestCityMapper, IOException > cityMapper, SleuthkitCaseProvider provider, java.util.logging.Logger logger)
void handleFilteredWaypointSet(Set< MapWaypoint > mapWaypoints, List< Set< MapWaypoint >> tracks, List< Set< MapWaypoint >> areas, boolean wasEntirelySuccessful)

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