diff --git a/CHANGELOG.md b/CHANGELOG.md index 2eff571c..1dd6f026 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ -#### 2.19.6-beta2 / 2023-01-06 +### 2.19.6-beta3 2023-03-04 +- Fix crash on scan state restore (#1131, David G. Young) - Fix BluetoothMedic crashes on Android 12+ when permissions are missing and depracate medic power cycling no longer working on Android 13 (#1121, David G. Young) diff --git a/lib/src/main/java/org/altbeacon/beacon/service/ScanState.java b/lib/src/main/java/org/altbeacon/beacon/service/ScanState.java index b3d81ce7..54a454de 100644 --- a/lib/src/main/java/org/altbeacon/beacon/service/ScanState.java +++ b/lib/src/main/java/org/altbeacon/beacon/service/ScanState.java @@ -38,11 +38,17 @@ public class ScanState implements Serializable { private static final String STATUS_PRESERVATION_FILE_NAME = "android-beacon-library-scan-state"; private static final String TEMP_STATUS_PRESERVATION_FILE_NAME = "android-beacon-library-scan-state-temp"; public static int MIN_SCAN_JOB_INTERVAL_MILLIS = 300000; // 5 minutes + private static long SANITY_FILE_SIZE_LIMIT = 100000; // ~100k + private static long SANITY_REGION_LIMIT = 1000; + private static long SANITY_PARSER_LIMIT = 1000; private Map mRangedRegionState = new HashMap(); private transient MonitoringStatus mMonitoringStatus; private Set mBeaconParsers = new HashSet(); - private ExtraDataBeaconTracker mExtraBeaconDataTracker = new ExtraDataBeaconTracker(); + // Don't persist extra beacon tracker because this could grow to be very very large if a huge + // number of beacons are in the vicinity, or if a smaller number rotate MACs or identifiers + // and appear to be a large number + private transient ExtraDataBeaconTracker mExtraBeaconDataTracker = new ExtraDataBeaconTracker(); private long mForegroundBetweenScanPeriod; private long mBackgroundBetweenScanPeriod; private long mForegroundScanPeriod; @@ -140,13 +146,26 @@ public static ScanState restore(Context context) { FileInputStream inputStream = null; ObjectInputStream objectInputStream = null; try { - inputStream = context.openFileInput(STATUS_PRESERVATION_FILE_NAME); - objectInputStream = new ObjectInputStream(inputStream); - scanState = (ScanState) objectInputStream.readObject(); - scanState.mContext = context; - } catch (FileNotFoundException fnfe) { + File file = context.getFileStreamPath(STATUS_PRESERVATION_FILE_NAME); + if (file.length() > SANITY_FILE_SIZE_LIMIT) { + // make sure file size is reasonable. If over 100k, do not restore + // See issue #1129 + LogManager.e(TAG, "Refusing to restore file of size "+file.length()); + } + else { + inputStream = context.openFileInput(STATUS_PRESERVATION_FILE_NAME); + objectInputStream = new ObjectInputStream(inputStream); + scanState = (ScanState) objectInputStream.readObject(); + scanState.mContext = context; + } + + } + catch (FileNotFoundException fnfe) { LogManager.w(TAG, "Serialized ScanState does not exist. This may be normal on first run."); } + catch (IllegalStateException ise) { + LogManager.e(TAG, "Exception deserializing", ise); + } catch (IOException | ClassNotFoundException | ClassCastException e) { if (e instanceof InvalidClassException) { LogManager.d(TAG, "Serialized ScanState has wrong class. Just ignoring saved state..."); @@ -185,8 +204,18 @@ public static ScanState restore(Context context) { public void save() { synchronized (ScanState.class) { - // TODO: need to limit how big this object is somehow. - // Impose limits on ranged and monitored regions? + if (mRangedRegionState.size() > SANITY_REGION_LIMIT) { + LogManager.e(TAG, "Refusing to save scan state with excessive region count: "+mRangedRegionState.size()); + return; + } + if (mMonitoringStatus.regions().size() > SANITY_REGION_LIMIT) { + LogManager.e(TAG, "Refusing to save scan state with excessive region count: "+mMonitoringStatus.regions().size()); + return; + } + if (mBeaconParsers.size() > SANITY_PARSER_LIMIT) { + LogManager.e(TAG, "Refusing to save scan state with excessive parser count: "+mBeaconParsers.size()); + return; + } FileOutputStream outputStream = null; ObjectOutputStream objectOutputStream = null; try {