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

[native code] Experiment with entering/exiting airplane mode #1043

Open
shankari opened this issue Jan 22, 2024 · 5 comments
Open

[native code] Experiment with entering/exiting airplane mode #1043

shankari opened this issue Jan 22, 2024 · 5 comments

Comments

@shankari
Copy link
Contributor

Anecdotally, I seem to remember that when I got out of airplane mode, I would frequently miss trips. I do not remember whether the foreground service was missing at the time.

The goals for this issue are to go int and out of airplane mode on both a stock android phone and a custom (Samsung/OnePlus phone) and see what happens under the hood. We can also look at my logs (in the internal issue) and see the FSM state when exiting from airplane mode.

A potential change might be to register for the "leave airplane mode" callback and reset the FSM if needed.
As a preliminary step, we can register for the "leave airplane mode" callback and log the state of the FSM and the foreground service at the time.

@louisg1337
Copy link

Airplane Mode

I dug into the airplane mode a bit and wanted to get my findings out before we get swept away in the GSA project. I did not have enough time (nor a Samsung/OnePlus) to do tests with physical phones, so I did all of my work in a pixel emulator. I also didn't see an internal issue for this, I'm not sure where it is exactly, so I didn't get the chance to look at any logs.

Tests

I did 3 types of tests to see how airplane mode would affect the FSM. All of these tests involved me broadcasting a custom intent that would allow me to view the state of the FSM at any given time.

  1. Control
    For this test, I did a fresh install of the app, and got myself to the home screen. From there I retrieved the state, in which it was local.state.waiting_for_trip_start. I then went to the Android home screen to put the app in the background, and turned on airplane mode. I then pinged for the state and I got local.state.waiting_for_trip_start. I then turn off airplane mode, and went back into the app, and checked the state once again, being local.state.waiting_for_trip_start.

  2. Simulate trip trying to start
    This test I wanted to focus on seeing what would happen if we tried to exit our geofence while in airplane mode. I've read conflicting things online about how some manufacturers may or may not allow geofence services to still work in airplane mode, hence why I tried it. The experiment followed the same suit as above until I got into airplane mode, which is when I broadcasted local.transition.exited_geofence. After the app spins its wheels, we end up being stuck in the local.state.start state, as a result of the error below. Turning off airplane mode and entering the app again does not seem to fix our state by itself.

    02-01 13:25:04.532 13981 13981 I SensorControlBackgroundChecker: location settings are invalid, generating tracking 
    error and visible notification
    
  3. Simulate performPeriodicActivity()
    I also thought I'd try to see what happens if performPeriodicActivity was called and the foreground service tries to restart itself. I did the exact same thing as I did in step 2, except I broadcasted for performPeriodicActivity to be called instead of local.transition.exited_geofence. We end up getting the same results, being that we get stuck in local.state.start.

Potential Solutions

I know it was mentioned above that we could register for that airplane mode callback and restart the FSM, and I think thats a great idea. I have not been able to test it, however, as I am having trouble editing the AndroidManifest, so in turn I can't register any BroadcastReceiver to that event. Each time I try to run the app using npx cordova emulate android the AndroidManifest gets rebuilt. I definitely would appreciate some help on figuring out how I can write to AndroidManifest without it getting overwritten so I can try this out.

Another alternative method that we can do instead is to check in TripDiaryStateMachineService.handleAction if airplane mode is turned on. If it is turned on, then we can ignore any action request or automatically change the action request to something like local.state.waiting_for_trip_start to prevent the state from being stuck in limbo.

Please let me know what you think @shankari, I would appreciate any feedback you may have.

@shankari
Copy link
Contributor Author

shankari commented Feb 2, 2024

@louisg1337 You can edit the AndroidManifest.xml by changing the plugin.xml.

  1. You can change the plugin.xml once, install the plugin (so AndroidManifest.xml is updated) and then change the code
  2. Edit AndroidManifest.xml while working on the code with a visual studio plugin https://marketplace.visualstudio.com/items?itemName=adelphes.android-dev-ext (note that you will need to open the build.gradle file in platforms/android
  3. If you can build the apk without cordova, you can drag and drop it to the emulator. I think you should also be able to use gradelew build manually from platforms/android to build the apk.
  4. Make changes on your personal laptop.

Let us know which works best!

@louisg1337
Copy link

Status Update

Thank you for helping me figure out the AndroidManfiest issue, I appreciate it! I managed to get together a draft PR with both the airplane mode and foreground service disappearance fixes. I decided to combine the two into one PR to 1) get these changes out faster and 2) because I had to edit the same file for both issues.

Code

For the airplane mode change, I implemented the registering for the airplane mode receiver idea. I did run into an issue however, which is that apparently, with the new background execution limits, an app cannot register for implicit intents in the AndroidManifest, besides for these exceptions. The docs on this specific issue can be found here. In that case, I had to instead register the the intent via Context.registerReceiver() as specified by the docs. I did this in DataCollectionPlugin.pluginInitialize() as I figured that would be a good spot to put it, although let me know if theres a better place.

The rest of the change is straight forward with the app listening for the airplane intent in TripDiaryStateMachineReceiver, and then restarting the FSM.

Testing

To test this I did 3 separate tests which were as follows.

  1. Toggle airplane mode on and off as a fresh user (no consent, no permissions) to ensure that the FSM does not start.
  2. Toggle airplane mode on and off as a normal user to see if the new code messes up current state. (i.e. changes the state from local.state.waiting_for_trip_start to something different)
  3. Turn airplane mode on, broadcast exit geofence, and then turn airplane mode back off to see if it corrected the state from local.start.state to local.state.waiting_for_trip_start.

All tests were successful and nothing seemed too weird.

Concern

One concern I did have though was that in TripDiaryStateMachineReceiver when restarting the state all I did was broadcast a transition_initialize to the FSM. I was wondering if maybe it would be better to use this function as it only restarts the FSM if the state is local.start.state. I wanted some input to determine if it was better to just always restart the FSM, or only when we are in that weird state.

Please let me know what you think @shankari, thank you!

@shankari
Copy link
Contributor Author

shankari commented Feb 4, 2024

@louisg1337 thank you for doing this; hopefully it will make the app a lot more stable, at least for people who travel by plane.

Two comments:

  • wrt "Toggle airplane mode on and off as a normal user to see if the new code messes up current state. (i.e. changes the state from local.state.waiting_for_trip_start to something different)" I would expect that the location permission would be off and the state would be start. Then, when you get out of airplane mode, the FSM would be reset and we would get to the waiting for trip start. Just want to check that it is what you observed; it wasn't super clear to me
  • wrt "I was wondering if maybe it would be better to use this function as it only restarts the FSM if the state is local.start.state. I wanted some input to determine if it was better to just always restart the FSM, or only when we are in that weird state." I think it is fine to restart only if it is in the start state. In fact, I think that the FSM is in any other state, initialize essentially functions (or is intended to function) as a NOP. https://github.com/e-mission/e-mission-data-collection/blob/103b787db3347254488083714d178624ec76092a/src/android/location/TripDiaryStateMachineService.java#L167

Once you make that change, I am happy to merge the PR

@louisg1337
Copy link

louisg1337 commented Feb 6, 2024

Sounds great, thank you for the feedback! I just made both changes and they are now pushed to the PR. To also address the first bullet point, yes that is what I observed. Right when you switch to airplane mode the state is still in local.state.waiting_for_trip_start, but the second the app does anything it switches over to local.start.state.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants