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

⬆️ [phone] Upgrade to the latest everything + some RFC 🎉 #838

Closed
shankari opened this issue Dec 7, 2022 · 48 comments · Fixed by e-mission/e-mission-phone#952
Closed

Comments

@shankari
Copy link
Contributor

shankari commented Dec 7, 2022

At long last, after clearing the decks with the label changes

I'm ready to start the annual ritual of upgrading to the most recent version of everything.
I will also plan to make the experimental trip start feature the default #785
and add silent push notifications to android to see if it can address background operation issues #677

@TTalex @asiripanich @lgharib

@shankari
Copy link
Contributor Author

shankari commented Dec 17, 2022

I looked into the android 12 changes during the last upgrade.
The relevant changes that we know are:

  • hibernation changes: already handled with setting "unused app restrictions". Need to test on android 12
    • we should also make sure that the text for the setting is correct
  • foreground service launch restrictions:
    • We should then be able to start the app when we do need the fine grained location access. But what happens if the service is killed while we are on the trip? That is not a geofence transition, and we are in the background. Or do they argue that this never happens.

    • Although to be fair, even now, if the foreground service is killed during a trip, we won't really restart the foreground service until the trip ends, unless the trip is longer than an hour. Current locations for TripDiaryStateMachineForegroundService.startProperly are:

      • setNewState
      • `onServiceDisconnected
      • TripDiaryStateMachineReceiver
    • There is also an option to turn off battery optimizations for the app. We may have to fallback to that if the foreground service can indeed be killed during a trip (and I don't see any guarantees that it won't be).

    • "high priority FCM notifications" can also start foreground services from the background, although the system can lower the priority of the FCM notification if it does not

  • If your app requests the ACCESS_FINE_LOCATION runtime permission, you should also request the ACCESS_COARSE_LOCATION permission to handle the case where the user grants approximate location access to your app. You should include both permissions in a single runtime request.
  • Specifying android:allowBackup="false" does disable backups to Google Drive, but doesn’t disable D2D transfers for the app. (should we allow backup?)
  • changes to the handling of third-party cookies to provide more security and privacy and offer users more transparency and control in WebView (we don't use third party cookies)

Changes to investigate further:

  • data handling attribution access: detects whether private data app access happens in your app or in an SDK. We don't use any closed source SDKs, so we should not care about this. Looks like if we want to use attribution tags, they have to be defined in the manifest. But do we need to use attribution tags?
  • safer component exporting: If your app targets Android 12 or higher and contains activities, services, or broadcast receivers that use intent filters, you must explicitly declare the android:exported attribute for these app components.
  • pending intent mutability: all our intents should be defined as immutable so that "other apps cannot modify the intent to adjust the result of invoking the intent."
  • motion sensor rate limiting: does this affect the motion activity API and the motion activity transition latency?

@shankari
Copy link
Contributor Author

for iOS, apple has approved our app, but has raised some questions on other apps on the same platform.

Potential changes:

  • switch to provisional notifications?: Not sure what the desired user flow should be - we ask for provisional notifications; they say "no", what should we do?
    • verify that this does not affect silent push notifications
    • we generate two kinds of visible notifications
      • push notifications
      • local notifications if the settings are incorrect
    • looks like if the user says that they don't want to see the push notifications, we will also not be able to display local notifications. Should check whether the setting does affect all app notifications.
    • if it does, we may want to have an onboarding question around whether the user wants to see daily notifications and only show them if they say yes. If they say "no", we can still send the local notifications, which are more important for baseline background data collection.
    • we still always have the risk that the user originally accepts the notifications but then turns them off, and I am not sure how to deal with that or if there is any way to do so
  • Also, it looks like with iOS 14+, we don't have to get people to turn on the "always" permission in the app, we can prompt right after the "while in use". But is that really the best option from a UX perspective? wouldn't it be easier to have a single popup than two separate popups?
    https://medium.com/swlh/location-permission-in-ios-13-f9e10917c05e

@shankari shankari changed the title ⬆️ Upgrade to the latest everything 🎉 ⬆️ Upgrade to the latest everything + some RFC 🎉 Dec 17, 2022
@shankari
Copy link
Contributor Author

@TTalex @lgharib @asiripanich @sebastianbarry @JGreenlee @jf87 @ericafenyo: I have some notes here on the changes required to support the new APIs and would like feedback on the user interface for the new changes:

  • android: allow backups to the cloud?
  • android: turn off app battery optimizations, or just see whether the foreground service is not killed during the trip
  • iOS: switch to provisional notifications? what do we do if the user says "no" when the provisional notification is displayed? How can we separate the local notifications to alert that settings are wrong from the notifications to remind users to label apps?
  • iOS: should we change the way we prompt for "always" location on iOS4?

@shankari
Copy link
Contributor Author

Most recent versions of cordova seem to be:

Template has been updated; let's see if we need to change anything to match it https://cordova.apache.org/news/2021/10/31/template-release.html

@shankari
Copy link
Contributor Author

shankari commented Dec 17, 2022

@asiripanich @TTalex @lgharib @ericafenyo @jf87 I plan to remove the following plugins, which are (to my knowledge) currently unused, and are getting more complex to maintain

  • cordova-jwt-auth: all projects that I am aware of login with a long "token"/opcode/secret key and not email/OpenID etc
  • cordova-plugin-ionic:
    • since we are not using the ionic-deploy functionality any more. It was always unclear whether that constituted "code download"
    • it is not currently authorized by NREL cyber
    • having separate branches without a lot of oversight just led to a lot of divergent, throw-away code
    • NREL cyber will not allow us to deploy code written by others without review and merge anyway
    • so we are going to switch to a small number of features, checked into master as modules, and enabled/disabled via a simple static config file
  • e-mission-transition-notify: again, currently unused (nobody likes being interrupted at various times of the day/night) and sometimes causes crashes on android

@JGreenlee
Copy link

JGreenlee commented Dec 20, 2022

@shankari
By the nature of the application, I think there is going to be an inherent cost for background location processing.

Android: I've seen that other apps with similar needs (Life360 is a good example) do take the route of disabling battery optimization. It might just have to be that way.
The trickiest part of the issue, I think, is that battery optimization can vary a bit from vendor to vendor (ie Samsung handles differently than Pixel) so we can't predict if/when a process is killed

For android I see a few location priority options in the API - not sure what we currently use but if PRIORITY_BALANCED_POWER_ACCURACY is good enough, then we may be considered more 'compliant' with Android's recommendations. Fewer times of the OS annoying users with warnings about the battery drain

iOS: provisional notifications whenever possible, seems worth implementing. I can't comment much else bc I'm still learning what the app pushes and when

Agreed that having two popups to get location permission is annoying - but either way we can't reduce the number of clicks to less than 2. All things considered I think the best route from a UX perspective is to just comply with apple's recommended flow. It will be what iOS users become used to as other apps adopt it. If they deny, we just send them back and and ask them to allow from the system prefs

@shankari
Copy link
Contributor Author

shankari commented Dec 20, 2022

@JGreenlee I agree that there is an inherent cost of background location processing. Chapter 4 of my PhD thesis (https://www2.eecs.berkeley.edu/Pubs/TechRpts/2019/EECS-2019-180.pdf) explores various background sensing options in detail, and section 7.2 of my thesis performs a rigorous evaluation of of the power/accuracy tradeoff by using various different sensing regimes on identical phones. I would encourage you to read them first.

Note that for stock android phones, we currently do not require users to disable background optimizations. For non-stock android phones, we may need to have users disable optimizations (e.g. https://dontkillmyapp.com/) but the instructions are different for each make and model.

The question here is: "do we need to have users disable optimizations on stock android as well?". I would like to avoid doing that, because the fewer settings we ask users to modify, the better. If the foreground service (the persistent notification) is only rarely killed while the user is on a trip, then we don't need to ask users to change. If it is killed often, or if the foreground service is killed when the user removes the notification in android 13 (https://developer.android.com/guide/components/foreground-services#user-dismiss-notification), then we will have to ask users to change the setting.

So to reiterate: Should we err on the side of better data collection by asking the user to change this additional setting, or should we err on the side of usability by not asking them to change the setting and hoping that the service is not killed very often?

@TTalex
Copy link

TTalex commented Dec 21, 2022

On the subject of plugins:
No issue from our side with removing the three unused ones. Especially if it frees some maintainability time.

By the way, one overall feedback I've got from the team is to try to reduce the interdependencies between plugins. And to try to isolate native parts as much as possible. This is linked to a specific need of "modulability", where only some parts of the app are used.

On the subject of battery optimizations:
I tend to agree with usability being more important than data collection. As long as it doesn't completely break data collection. Something only data will tell.
So as far as default values goes, I would rather go with not asking users to change the settings. Maybe keep it as an option for power users.

On the subject of Android cloud backups:
I'm not sure that I fully understand this part. What is / can be part of a backup ? If it's user data, we try to avoid using google services when we can, so no issues with disabling backups.

I hope that helps.

@shankari
Copy link
Contributor Author

@TTalex

  • wrt plugins: I agree that the plugins are not as modular as one would like.
    • However many/most of the plugins are actually libraries that the other plugins depend on (e.g. logging/usercache), so the app will not really function without them. At this point, it is probably best to assume that all e-mission plugins (except for the trip-end-detection) are required and to make plans to include their functionality. If the team has concrete suggestions on which plugin functionality they would like to remove, I'm happy to discuss whether it is possible.
    • At least from an NREL perspective, it is not a very high priority to modularize the plugins any further. Our funding is focused on providing an easy to use platform for deployment partners. So our priorities are around improving app performance, usability and related tools. When we do tackle modularity, we will do so in the order of (1) app UI, (2) server and (3) native code. If we are able to get TCF funding to work on "commercialization" with industry partners, we will be able to prioritize developer, as opposed to deployer, concerns. Of course, we are happy to accept community contributions wrt modularity in any order 😄
  • Android cloud backups: I would also prefer to not backup apps to the cloud. On iOS, I have turned this off for many versions (https://github.com/e-mission/cordova-usercache/blob/master/src/ios/BEMBuiltinUserCache.m#L137). I can now turn it off on android as well. But then, if the user gets a new phone, and chooses to "restore apps" on the new phone, this app will not be restored. I am OK with that, but wanted to check with the rest of you on user expectations.....

@TTalex
Copy link

TTalex commented Dec 22, 2022

Sounds good to me, thanks for the details.

From my side, starting from scratch with new phones is what we're currently doing in our use cases. This has not been an issue for users as far as I know (note: the sample size is limited).

@shankari shankari changed the title ⬆️ Upgrade to the latest everything + some RFC 🎉 ⬆️ [phone] Upgrade to the latest everything + some RFC 🎉 Feb 7, 2023
@shankari
Copy link
Contributor Author

shankari commented Feb 9, 2023

After finishing up

  • two posters for TRB and two papers for journals, and
  • a bunch of server-side fixes related to exporting data and dealing with "big jumps" and
  • some new spatial analysis that we hope to integrate into the public dashboard and
  • lots of design on the new survey additions + diary/trip unification
    I am finally ready to pick this up again

@shankari
Copy link
Contributor Author

Fixed some pending issues with the native + fixed CI issues.
The next step is to make it so that we can install CocoaPods correctly.
CocoaPods are designed to be installed with "the default version of ruby on OSX"
However, in practice, it is "the default version of ruby on the most recent version of OSX"
If we have an older version of OSX, the default version of ruby is too old
I've filed an issue in the CocoaPods repo to update the documentation.
CocoaPods/CocoaPods#11763

In the meanwhile, seeing if we can also lock down the version of ruby

@shankari
Copy link
Contributor Author

Next step, let's upgrade all the dependencies in setup/export_shared_dep_versions.sh
New versions are:

export NVM_VERSION=0.39.3
export NODE_VERSION=19.5.0
export NPM_VERSION=9.3.1
# make sure that this is a stable version from
# so that https://github.com/postmodern/ruby-versions
# ideally, this would be the same version as the CI
# Looks like brew supports only major and minor, not patch version
export RUBY_VERSION=3.0
export COCOAPODS_VERSION=1.11.3
export GRADLE_VERSION=7.6
export OSX_EXP_VERSION=12

shankari added a commit to shankari/e-mission-phone that referenced this issue Feb 11, 2023
shankari added a commit to shankari/e-mission-phone that referenced this issue Feb 17, 2023
Per e-mission/e-mission-docs#838 (comment)
This is the first step in which I remove all the controls related to it

```
$ grep -ri notify www/js/control | wc -l
       0
$ grep -ri notify www/templates/control/ | wc -l
       0
```

Testing done:
Profile screen loads correctly
shankari added a commit to shankari/cordova-jwt-auth that referenced this issue Mar 5, 2023
…or authentication

This is the first step for removing support for other methods of authentication.

This is as per the community decision in the issue:
e-mission/e-mission-docs#838 (comment)

Testing done:
- Checked that the functions are not invoked from the UI

```
$ grep -r signIn www/js
$ grep -r getJWT www/js
$ grep -r launchPromptedAuth www/js
$ git status
```

- Launched the app on android, no errors
shankari added a commit to shankari/e-mission-phone that referenced this issue Mar 9, 2023
As outlined in e-mission/e-mission-docs#838 (comment)
- we are not using the ionic-deploy functionality any more. It was always unclear whether that constituted "code download"
- it is not currently authorized by NREL cyber
- having separate branches without a lot of oversight just led to a lot of divergent, throw-away code
- NREL cyber will not allow us to deploy code written by others without review and merge anyway
- so we are going to switch to a small number of features, checked into master as modules, and enabled/disabled via a simple static config file

This is the last of the three cleanups in the issue.
The other two were fixed in
e-mission#925
and
e-mission/cordova-jwt-auth#46

We can now start the real API upgrade
@shankari
Copy link
Contributor Author

shankari commented Mar 11, 2023

Upgrade to latest versions of the dev toolchain:

  • xcode 14.2
  • android CLI 9477386
  • android SDK packages to their latest versions
  • cordova-android 11.0.0
  • other cordova packages to their latest versions

@shankari
Copy link
Contributor Author

How about we first make the change to just turn off the background optimizations so that we have a working solution by the end of the month?

We can then make these changes in a separate branch

@shankari
Copy link
Contributor Author

ok, I am going to revert my changes so far, and just turn off background optimizations for now. That is a simple fix that I can implement in a day and will get us over this hump.

Then, we should implement these changes and potentially some other changes to the native code (like iOS trip end detection) and do another round of MobilityNet data collection that includes the OPGeofence and the new iOS trip end detection.

@shankari
Copy link
Contributor Author

Turning off battery optimizations:

The user turns off battery optimizations for your app. You can help users find this option by sending them to your app's App info page in system settings. To do so, invoke an intent that contains the ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS intent action.

You can use PowerManager.isIgnoringBatteryOptimizations() to determine if an application is already ignoring optimizations.  You can use ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS to ask the user to put you on this list.

Should we use ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS in the AppInfo page, or ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS?

@shankari
Copy link
Contributor Author

shankari commented Mar 15, 2023

Difference between them:

Most apps should invoke an intent that contains the ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS.

Apps that satisfy an acceptable use case can instead invoke an intent that contains the ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS intent action to let the user add the app to the exemption list directly, without going to system settings.

Acceptable use cases include: https://developer.android.com/training/monitoring-device-state/doze-standby#exemption-cases

  1. Instant messaging, chat, or calling app; enterprise VOIP apps., if it can't use FCM because of technical dependency on another messaging service or Doze and App Standby break the core function of the app. <----> We don't qualify
  2. Safety app: Apps that keep their users and their families safe. <-----> I guess we could rebrand, but that is not really the core
  3. Task automation app: App's core function is scheduling automated actions, such as for instant messaging, voice calling, or new photo management. <------> We don't qualify
  4. Peripheral device companion app. <---------> definitely don't qualify

Going with ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS

@shankari
Copy link
Contributor Author

Note that ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS was added in API 23, so it's good that we are bumping up our min SDK in this release 😄
https://developer.android.com/reference/android/provider/Settings#ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS

@shankari
Copy link
Contributor Author

While trying to launch the optimization settings, running into this infinite loop

03-15 16:46:58.941 32041 32325 W System.err:    at org.apache.cordova.CordovaInterfaceImpl.setActivityResultCa
llback(CordovaInterfaceImpl.java:80)
03-15 16:46:58.941 32041 32325 W System.err:    at edu.berkeley.eecs.emission.cordova.tracker.verification.Sen
sorControlForegroundDelegate.onActivityResult(SensorControlForegroundDelegate.java:517)
03-15 16:46:58.941 32041 32325 W System.err:    at edu.berkeley.eecs.emission.cordova.tracker.DataCollectionPl
ugin.onActivityResult(DataCollectionPlugin.java:228)
03-15 16:46:58.941 32041 32325 W System.err:    at org.apache.cordova.CordovaInterfaceImpl.setActivityResultCa
llback(CordovaInterfaceImpl.java:80)
03-15 16:46:58.942 32041 32325 W System.err:    at edu.berkeley.eecs.emission.cordova.tracker.verification.Sen
sorControlForegroundDelegate.onActivityResult(SensorControlForegroundDelegate.java:517)
03-15 16:46:58.942 32041 32325 W System.err:    at edu.berkeley.eecs.emission.cordova.tracker.DataCollectionPl
ugin.onActivityResult(DataCollectionPlugin.java:228)
03-15 16:46:58.942 32041 32325 W System.err:    at org.apache.cordova.CordovaInterfaceImpl.setActivityResultCa
llback(CordovaInterfaceImpl.java:80)
03-15 16:46:58.942 32041 32325 W System.err:    at edu.berkeley.eecs.emission.cordova.tracker.verification.Sen
sorControlForegroundDelegate.checkAndPromptIgnoreBatteryOptimizations(SensorControlForegroundDelegate.java:439
)
03-15 16:46:58.942 32041 32325 W System.err:    at edu.berkeley.eecs.emission.cordova.tracker.DataCollectionPl
ugin.execute(DataCollectionPlugin.java:124)
03-15 16:46:58.942 32041 32325 W System.err:    at org.apache.cordova.CordovaPlugin.execute(CordovaPlugin.java
:98)
03-15 16:46:58.942 32041 32325 W System.err:    at org.apache.cordova.PluginManager.exec(PluginManager.java:14
6)
03-15 16:46:58.942 32041 32325 W System.err:    at org.apache.cordova.CordovaBridge.jsExec(CordovaBridge.java:
59)
03-15 16:46:58.942 32041 32325 W System.err:    at org.apache.cordova.engine.SystemExposedJsApi.exec(SystemExp

@shankari
Copy link
Contributor Author

The chain is

        // This will be a NOP if we are not handling the correct activity intent
                mControlDelegate.onActivityResult(requestCode, resultCode, data);
      Log.i(cordova.getActivity(), TAG, "Battery optimizations enforced, asking user to ignore");
      this.cordovaCallback = cordovaCallback;
      cordova.setActivityResultCallback(plugin);
      openBatteryOptimizationScreen(cordovaCallback);
    @Override
    public void setActivityResultCallback(CordovaPlugin plugin) {
        // Cancel any previously pending activity.
        if (activityResultCallback != null) {
            activityResultCallback.onActivityResult(activityResultRequestCode, AppCompatActivity.RESULT_CANCELED, null);
        }
        activityResultCallback = plugin;
    }
  public void onActivityResult(int requestCode, int resultCode, Intent data) {
    Activity mAct = cordova.getActivity();
    cordova.setActivityResultCallback(null);

So setActivityResultCallBack calls onActivityResult which calls setActivityResultCallback which calls onActivityResult and so on.

I'm pretty sure that the issue is that I am setting the callback twice while setting it up - once in checkAndPromptIgnoreBatteryOptimizations and once in openBatteryOptimizationScreen. Since we only call openBatteryOptimizationScreen in one place, inlining the call...

@shankari
Copy link
Contributor Author

That seems to work.
The main test now is to ensure that, with this fix, if the foreground service is killed, it is automatically restarted.

To kill the foreground service, we use

adb shell am force-stop edu.berkeley.eecs.emission

That kills the entire app, including the foreground service

Before killing After killing
Screenshot_1678930819 Screenshot_1678930852

Now, the foreground service should automatically restart in the next periodic sync

03-14 07:58:03.849 10615 10765 I TripDiaryStateMachineRcvr: START PERIODIC ACTIVITY
03-14 07:58:03.880 10615 10765 D TripDiaryStateMachineForegroundService: In checkForegroundNotification, found
 0 active notifications
03-14 07:58:03.895 10615 10765 D TripDiaryStateMachineForegroundService: Did not find foreground notification
with ID 6646464 in list []
03-14 07:58:03.981 10615 10765 D TripDiaryStateMachineForegroundService: startProperly called with context = a
03-14 07:58:04.055 10615 10615 D TripDiaryStateMachineForegroundService: onStartCommand called with intent = I
ntent { cmp=edu.berkeley.eecs.emission/.cordova.tracker.location.TripDiaryStateMachineForegroundService } flag
03-14 07:58:04.099 10615 10615 D TripDiaryStateMachineForegroundService: onStartCommand called on oreo+, with
msg Ready for your next trip starting foreground service

03-15 18:13:11.812 19490 19533 I TripDiaryStateMachineRcvr: START PERIODIC ACTIVITY
03-15 18:13:11.822 19490 19533 D TripDiaryStateMachineForegroundService: In checkForegroundNotification, found 0 active notifications
03-15 18:13:11.840 19490 19533 D TripDiaryStateMachineForegroundService: Did not find foreground notification with ID 6646464 in list []
...

I do see this warning. Not sure if this matters given that the location access is from a separate intent service.
Will need to kill the foreground service on a regular device and try it, or see if we can generate a geofence exit via adb

03-15 18:13:12.303  1416  3422 W ActivityManager: Foreground service started from background can not have location/camera/microphone access: service edu.berkeley.eecs.emission/.cordova.tracker.location.TripDiaryStateMach
ineForegroundService

@shankari
Copy link
Contributor Author

I can also use ADB to directly generate broadcasts to services/broadcast receiver. Note that this requires root access and will only work on images with google_apis ONLY. It will not work on google_apis_playstore

$ adb root
restarting adbd as root

Calling initialize

$ ~/Library/Android/sdk/platform-tools/adb shell am broadcast -a local.transition.initialize -n edu.berkeley.eecs.emission/.cordova.tracker.location.TripDiaryStateMachineReceiver
Broadcasting: Intent { act=local.transition.initialize flg=0x400000 cmp=edu.berkeley.eecs.emission/.cordova.tracker.location.TripDiaryStateMachineReceiver }
Broadcast completed: result=0
03-15 19:55:35.305 13700 13700 I TripDiaryStateMachineRcvr: TripDiaryStateMachineReciever onReceive(android.app.ReceiverRestrictedContext@a4427a7, Intent { act=local.transition.initialize flg=0x400010 cmp=edu.berkeley.eecs.emission/.cordova.tracker.location.TripDiaryStateMachineReceiver }) called

03-15 19:55:35.329 13700 13700 D TripDiaryStateMachineForegroundService: startProperly called with context = android.app.ReceiverRestrictedContext@a4427a7
03-15 19:55:35.330   601  3004 I ActivityManager: Background started FGS: Allowed [callingPackage: edu.berkeley.eecs.emission; callingUid: 10148; uidState: RCVR; intent: Intent { cmp=edu.berkeley.eecs.emission/.cordova.tracker.location.TripDiaryStateMachineForegroundService }; code:SYSTEM_ALLOW_LISTED; tempAllowListReason <,reasonCode:SYSTEM_ALLOW_LISTED,duration:9223372036854775807,callingUid:-1>; targetSdkVersion:32; callerTargetSdkVersion:32; startForegroundCount:0; bindFromPackage:null]

03-15 19:55:35.425   601  3000 W ActivityManager: Foreground service started from background can not have location/camera/microphone access: service edu.berkeley.eecs.emission/.cordova.tracker.location.TripDiaryStateMachineForegroundService

03-15 19:55:35.533 13700 13737 D CreateGeofenceAction: Last location would have been Location[fused 37.722760, -122.164390 hAcc=8.423 et=+6m59s198ms alt=0.0 vAcc=0.5] if we hadn't reset it
03-15 19:55:35.537 13700 13737 I CreateGeofenceAction: isValidLocation = true. Yay!
03-15 19:55:35.542 13700 13737 D CreateGeofenceAction: Last location is Location[fused 37.722760,-122.164390 h
Acc=8.423 et=+6m59s198ms alt=0.0 vAcc=0.5] using it

We get that message about the foreground service started from the background, but it doesn't seem to affect the geofencing creation at least.

Let's now exit the geofence and start location tracking

03-15 20:03:04.766 17616 17616 D TripDiaryStateMachineService: TripDiaryStateMachineReceiver handleTripStart(local.transition.exited_geofence) called
03-15 20:03:04.769 17616 17616 D CreateGeofenceAction: Removing geofence with ID = DYNAMIC_EXIT_GEOFENCE
03-15 20:03:04.775 17616 17616 D ActivityRecognitionActions: Starting activity recognition with interval = 30000

03-15 20:03:04.784 17616 17616 D LocationTrackingAction: default request is Request[@1h BALANCED_POWER_ACCURACY, minUpdateInterval=10m, waitForAccurateLocation]
03-15 20:03:04.789 17616 17616 D LocationTrackingAction: after applying config, value is Request[@30s HIGH_ACCURACY, minUpdateInterval=5s, waitForAccurateLocation]
03-15 20:03:04.794 17616 17616 D LocationTrackingAction: requesting location updatesRequest[@30s HIGH_ACCURACY, minUpdateInterval=5s, waitForAccurateLocation]
03-15 20:03:04.798 17616 17616 D LocationTrackingAction: default request is Request[@1h BALANCED_POWER_ACCURACY, minUpdateInterval=10m, waitForAccurateLocation]
03-15 20:03:04.802 17616 17616 D LocationTrackingAction: after applying config, value is Request[@30s HIGH_ACCURACY, minUpdateInterval=5s, waitForAccurateLocation]
03-15 20:03:04.807 17616 17616 D TripDiaryStateMachineService: handleAction(local.state.waiting_for_trip_start, local.transition.exited_geofence) completed, waiting for async operations to complete

And we do get the locations read correctly

03-15 20:03:38.364 17616 17616 D LocationChangeIntentService: onCreate called
03-15 20:03:38.378 17616 18569 D LocationChangeIntentService: FINALLY! Got location update, intent is Intent { cmp=edu.berkeley.eecs.emission/.cordova.tracker.location.LocationChangeIntentService (has extras) }
03-15 20:03:38.393 17616 18569 D LocationChangeIntentService: Read locations [Location[fused 37.719410,-122.178100 hAcc=5.0 et=+19m3s88ms alt=0.0 vAcc=0.5 bear=348.0 bAcc=30.0]] from intent

03-15 20:06:47.129 17616 17616 D LocationChangeIntentService: onCreate called
03-15 20:06:47.988 17616 20255 D LocationChangeIntentService: Read locations [Location[fused 37.591778,-122.056140 hAcc=74.051 et=+22m11s891ms alt=0.0 vAcc=0.5]] from intent
...

So it looks like it all works fine on Android 12 (the "Foreground service started from background" does not seem to be significant)

Will kill the app on the physical device and verify

@shankari
Copy link
Contributor Author

It's great that we can send broadcast notifications to test, but it seems like that might be a problem if we refactor to a bound service.

I have not been able to figure out how to make calls to a bound service using adb, but we can call an intent service like so on a rooted shell (using the googleapis without the play services)

$ adb shell am startservice -a 2 edu.berkeley.eecs.emission/.cordova.tracker.location.GeofenceExitIntentService

We do have to figure out the exact format of the various intent services, though

03-15 20:58:41.100 17616 17616 D GeofenceExitIntentService: onCreate called
03-15 20:58:41.114 17616 17616 D GeofenceExitIntentService: onStartCommand called with intent Intent { act=2 cmp=edu.berkeley.eecs.emission/.cordova.tracker.location.GeofenceExitIntentService } flags 0 startId 1
03-15 20:58:41.120 17616 17616 D GeofenceExitIntentService: onStart called with startId 1
03-15 20:58:41.125 17616 15089 D GeofenceExitIntentService: geofence exit intent action = 2
03-15 20:58:41.130 17616 15089 D GeofenceExitIntentService: parsedEvent = null
03-15 20:58:41.133 17616 15089 E AndroidRuntime: FATAL EXCEPTION: IntentService[GeofenceExitIntentService]
03-15 20:58:41.133 17616 15089 E AndroidRuntime: Process: edu.berkeley.eecs.emission, PID: 17616
03-15 20:58:41.133 17616 15089 E AndroidRuntime: java.lang.NullPointerException: Attempt to invoke virtual method 'int com.google.android.gms.location.GeofencingEvent.getGeofenceTransition()' on a null object reference
03-15 20:58:41.133 17616 15089 E AndroidRuntime:        at edu.berkeley.eecs.emission.cordova.tracker.location.GeofenceExitIntentService.onHandleIntent(GeofenceExitIntentService.java:57)
03-15 20:58:41.133 17616 15089 E AndroidRuntime:        at android.app.IntentService$ServiceHandler.handleMessage(IntentService.java:78)
03-15 20:58:41.133 17616 15089 E AndroidRuntime:        at android.os.Handler.dispatchMessage(Handler.java:106)
03-15 20:58:41.133 17616 15089 E AndroidRuntime:        at android.os.Looper.loopOnce(Looper.java:201)
03-15 20:58:41.133 17616 15089 E AndroidRuntime:        at android.os.Looper.loop(Looper.java:288)
03-15 20:58:41.133 17616 15089 E AndroidRuntime:        at android.os.HandlerThread.run(HandlerThread.java:67)

@shankari
Copy link
Contributor Author

  • Force killed the app on the physical phone
  • Foreground service was restarted in a couple of hours
  • Took two trips today without starting app activity; both were recorded

Everything seems to be working properly

@shankari
Copy link
Contributor Author

Final check for an issue that was failing yesterday. In the background checks, we treat the optimization issue as a location failure and go to the start state. So when the user does turn on optimization, we are need to restart the FSM. Made that change and verified that it works.

restart_after_optimization.mov

@shankari
Copy link
Contributor Author

At this point, I think that most of the high level changes are done. The pending issues are:

  • data handling attribution access: detects whether private data app access happens in your app or in an SDK. We don't use any closed source SDKs, so we should not care about this. Looks like if we want to use attribution tags, they have to be defined in the manifest. But do we need to use attribution tags?
  • Specifying android:allowBackup="false" does disable backups to Google Drive, but doesn’t disable D2D transfers for the app. (should we allow backup?)

Neither of these sounds like a showstopper. I am going to push out a staging version with all the changes so far.

shankari added a commit to shankari/e-mission-data-collection that referenced this issue Mar 17, 2023
This allows us to start foreground services from the background
Which allows us to keep our original model and kick the can down the road
e-mission/e-mission-docs#838 (comment)

I have explored options to do it the right way, but am concerned about testing
overhead given our short time frame.

This change:
- adds interface functions to check and fix battery optimizations
- implements the check with the powermanager call
- implements the fix by launching the optimization window

Testing done:
- Turn off the data optimizations
- Kill the app (including the foreground service) using adb
e-mission/e-mission-docs#838 (comment)

- Send the initialize transition using adb
    - Foreground service is restarted
e-mission/e-mission-docs#838 (comment)

- Kill the app on a physical device using adb
- Foreground service restarting in a few hours
- Location tracking worked even without app being launched
e-mission/e-mission-docs#838 (comment)
shankari added a commit to shankari/e-mission-phone that referenced this issue Mar 17, 2023
This is a companion commit to
e-mission/e-mission-data-collection@60f6fa4
from
e-mission/e-mission-data-collection#202

This allows us to start foreground services from the background
Which allows us to keep our original model and kick the can down the road
e-mission/e-mission-docs#838 (comment)

I have explored options to do it the right way, but am concerned about testing
overhead given our short time frame.

This change:
- adds interface functions to check and fix battery optimizations
- implements the check with the powermanager call
- implements the fix by launching the optimization window

Testing done:
- Turn off the data optimizations
- Kill the app (including the foreground service) using adb
e-mission/e-mission-docs#838 (comment)

- Send the initialize transition using adb
    - Foreground service is restarted
e-mission/e-mission-docs#838 (comment)
shankari added a commit to shankari/e-mission-phone that referenced this issue Mar 17, 2023
The `google_apis_playstore` images are signed and cannot be rooted
If we want to send broadcast messages using adb for testing, we need root support
e-mission/e-mission-docs#838 (comment)

Add a couple of rootable system images as well so we can test appropriately
@shankari
Copy link
Contributor Author

I can confirm that the iOS warning no longer appears during upload
I am also able to upload the android version to the play store, so the new API version is recognized

Pending tasks:

  • adapt to the new QR code library
  • check whether push notifications are still working

@shankari
Copy link
Contributor Author

Ok, so this is turning out to be more complicated than I had thought.
Adapting to the new QR code library is fairly easy, but it doesn't seem to work.
There are no instructions, and on calling scan, the little notification that we are using the phone shows up, but we can't actually see any way to scan.

Checking the documentation on the original plugin; otherwise will have to fall back to the phonegap plugin

@shankari
Copy link
Contributor Author

shankari commented Mar 17, 2023

I looked at the documentation on the original plugin, and I needed to call show to show the video preview.
But even showing the preview doesn't work.
This may be because this codebase is super fancy, and opens up the video preview behind the webview.
And our HTML elements are not transparent, so they cover the video.

// Make the webview transparent so the video preview is visible behind it.
QRScanner.show();
// Be sure to make any opaque HTML elements transparent here to avoid
// covering the video.

Configures the native webview to have a transparent background, then sets the background of the and DOM elements to transparent, allowing the webview to re-render with the transparent background.

I'm not going to spend time figuring out which element is not transparent and then making it be transparent (and then restoring when we stop scanning).

Switching back to the old phonegap plugin instead

shankari added a commit to shankari/e-mission-phone that referenced this issue Mar 17, 2023
We tried to migrate from `phonegap-plugin-barcodescanner` to the better
supported `cordova-plugin-barcode-qrscanner`. However, I was not able to get
the new plugin to work. The OS indicated that the app was accessing the camera,
but we were not able to see the preview, even after I added a `show` to the code.

e-mission/e-mission-docs#838 (comment)
e-mission/e-mission-docs#838 (comment)

There doesn't seem to be an option to file issues or to communicate with the creator.
So let's fall back to the older `phonegap-plugin-barcodescanner`
We restore the `android_change_compile_implementation` hook so that we can
change the `compile` to `implementation` and get the plugin to compile.

Bonus fix: Add the `NSCameraUsageDescription` via the config.xml so that we
don't get a permission issue while running on iOS.

Testing done:
- Builds on both android and iOS
- Scanning launches on both android and iOS
- QR codes are scanned on both android and iOS
shankari added a commit to e-mission/e-mission-devapp that referenced this issue Apr 23, 2023
To be consistent with 2023 phone upgrades
Related issue: e-mission/e-mission-docs#838
Related PR: e-mission/e-mission-phone#952

Since the rest of the app is not the same, I applied the changes by diffing the
config.xml and package.json and the setup directory with the corresponding
locations in the e-mission-phone repo

Additional minor changes:
+ comment out the `configure_xml_and_json` step, since we don't have the
cordovabuild versions in this release
+ change the package name to `edu.berkeley.eecs.emission.devapp`
+ bump up the versions

Testing done:
- devapp builds successfully
- after making phone app changes, devapp displays phone app successfully
shankari added a commit to shankari/e-mission-phone that referenced this issue Apr 23, 2023
Related issue: e-mission/e-mission-docs#838
Related PR for cordovabuild: e-mission#952
Related PR for devapp: e-mission/e-mission-devapp#18

Main changes:
- need to add "Ventura" to the list of releases to fix:

    ```
    /Users/kshankar/e-mission/phone-rciti-branch/node_modules/macos-release/index.js:27
        const [name, version] = nameMap.get(release);
                                ^

    TypeError: undefined is not iterable (cannot read property Symbol(Symbol.iterator))
        at macosRelease (/Users/kshankar/e-mission/phone-rciti-branch/node_modules/macos-release/index.js:27:26)
        at osName (/Users/kshankar/e-mission/phone-rciti-branch/node_modules/os-name/index.js:21:18)
        at new Insight (/Users/kshankar/e-mission/phone-rciti-branch/node_modules/insight/lib/index.js:37:13)
        at Object.<anonymous> (/Users/kshankar/e-mission/phone-rciti-branch/node_modules/phonegap/node_modules/cordova/src/telemetry.js:26:15)
        at Module._compile (node:internal/modules/cjs/loader:1246:14)
        at Module._extensions..js (node:internal/modules/cjs/loader:1300:10)
        at Module.load (node:internal/modules/cjs/loader:1103:32)
        at Module._load (node:internal/modules/cjs/loader:942:12)
        at Module.require (node:internal/modules/cjs/loader:1127:19)
        at require (node:internal/modules/helpers:112:18)
    ```
    - we do this by copying over a file that includes Ventura
        per https://stackoverflow.com/questions/68318289/ionic-fails-building-in-macos-12-monterey
    - I tried to do this in a more principled fashion by upgrading to the most recent version of cordova
        - but we are already at the most recent version of cordova, and it still doesn't work
    - I then tried to compare it to the same file in the cordovabuild version and it was the same

        ```
           $ grep Ventura ~/e-mission/native_code_upgrade/node_modules/macos-release/index.js | wc -l
           0
        ```
    - on further invesigation, it turns out that the issue is not that the more
      recent version of cordova has a newer version of the macos-release
      library, but that it doesn't use `Insight` (at least by default), which
      is the module that calls it
    - phonegap, which we need for `phonegap serve`, does
        ```
        $ grep -r "new Insight" node_modules/
        node_modules//cordova/node_modules/insight/readme.md:const insight = new Insight({
        node_modules//cordova/node_modules/insight/readme.md:const insight = new Insight({
        node_modules//cordova/node_modules/insight/lib/push.js: const insight = new Insight(message);
        node_modules//insight/readme.md:const insight = new Insight({
        node_modules//insight/readme.md:const insight = new Insight({
        node_modules//insight/lib/push.js:  const insight = new Insight(msg);
        node_modules//phonegap/node_modules/cordova/src/telemetry.js:var insight = new Insight({
        node_modules//phonegap/lib/cli/analytics.js:var insight = new Insight({
        ```
    - so there is really no alternative to hot-patching the file

- add the cordova `browser` platform to fix:

    ```
    CordovaError: No platforms added to this project. Please use `phonegap platform add <platform>`.
        at Object.preProcessOptions (/Users/kshankar/e-mission/phone-rciti-branch/node_modules/phonegap/node_modules/cordova-lib/src/cordova/util.js:313:15)
        at /Users/kshankar/e-mission/phone-rciti-branch/node_modules/phonegap/node_modules/cordova-lib/src/cordova/prepare.js:49:40
        at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    ```
    - incidentally, if we don't add the platform, cordova tries to add it itself, and that completely messes up the `node_modules`

    There are 720 packages

    ```
    $ ls -al node_modules/ | wc -l
         720
    ```

    we try to add the browser, which fails

    ```
    Using cordova-fetch for cordova-browser@^6.0.0

    (node:24473) Warning: Accessing non-existent property 'browser' of module exports inside circular dependency
    (Use `node --trace-warnings ...` to show where the warning was created)

    Failed to fetch platform cordova-browser@^6.0.0
    Probably this is either a connection problem, or platform spec is incorrect.
    Check your connection and platform name/version/URL.
    Could not determine package name from output:
    added 24 packages, changed 1 package, and audited 131 packages in 10s
    ```

    we end up with 111 packages, and `phonegap` is uninstalled as well

    ```
    $ ls -al node_modules | wc -l
         111
    ```

    - and the `phonegap` command is not found either

- add shelljs to the dependencies to fix

    ```
     [warning] Unable to load PlatformApi from platform. Error: Cannot find module 'shelljs'
     [warning] Require stack:
     [warning] - /Users/kshankar/e-mission/phone-rciti-branch/platforms/browser/cordova/Api.js
     [warning] - /Users/kshankar/e-mission/phone-rciti-branch/node_modules/phonegap/node_modules/cordova-lib/src/cordova/util.js
     [warning] - /Users/kshankar/e-mission/phone-rciti-branch/node_modules/phonegap/node_modules/cordova-lib/src/platforms/platforms.js
     [warning] - /Users/kshankar/e-mission/phone-rciti-branch/node_modules/phonegap/node_modules/cordova-lib/src/plugman/install.js
     [warning] - /Users/kshankar/e-mission/phone-rciti-branch/node_modules/phonegap/node_modules/cordova-lib/src/plugman/plugman.js
     [warning] - /Users/kshankar/e-mission/phone-rciti-branch/node_modules/phonegap/node_modules/cordova-lib/cordova-lib.js
     [warning] - /Users/kshankar/e-mission/phone-rciti-branch/node_modules/phonegap/node_modules/cordova/cordova.js
     [warning] - /Users/kshankar/e-mission/phone-rciti-branch/node_modules/phonegap/lib/cordova/index.js
     [warning] - /Users/kshankar/e-mission/phone-rciti-branch/node_modules/phonegap/lib/phonegap/cordova.js
     [warning] - /Users/kshankar/e-mission/phone-rciti-branch/node_modules/phonegap/lib/phonegap.js
     [warning] - /Users/kshankar/e-mission/phone-rciti-branch/node_modules/phonegap/lib/main.js
     [warning] - /Users/kshankar/e-mission/phone-rciti-branch/node_modules/phonegap/lib/cli.js
     [warning] - /Users/kshankar/e-mission/phone-rciti-branch/node_modules/phonegap/bin/phonegap.js
    ```

Testing done:

```
$ rm -rf node_modules
$ bash setup/setup_serve.sh
$ npm run serve
```

the serve process starts up and the devapp is able to connect to it
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

Successfully merging a pull request may close this issue.

3 participants