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

Fix crash on scan state restore. #1131

Open
wants to merge 4 commits into
base: master
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
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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)
Expand Down
45 changes: 37 additions & 8 deletions lib/src/main/java/org/altbeacon/beacon/service/ScanState.java
Original file line number Diff line number Diff line change
Expand Up @@ -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<Region, RangeState> mRangedRegionState = new HashMap<Region, RangeState>();
private transient MonitoringStatus mMonitoringStatus;
private Set<BeaconParser> mBeaconParsers = new HashSet<BeaconParser>();
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;
Expand Down Expand Up @@ -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());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we maybe delete STATUS_PRESERVATION_FILE_NAME if it is too big or corrupted?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the code does something similar as is because as soon as the file is persisted (typically after app processing a few milliseconds later) the scan state is persisted with the newly emptier state, overwriting the problematic file.

}
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...");
Expand Down Expand Up @@ -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 {
Expand Down