e-mission/e-mission-docs

Repackaging AWARE features as cordova plugins

xubowenhaoren opened this issue · 139 comments

Hello,

After a lengthy trial-and-error of integrating emission into AWARE, we find out that it would be more flexible to merge the AWARE sync adapters and sensors to emission directly. What suggestions do you have for us to get started on the migration?

The high level goal is that we want to modify the AWARE code as little as possible so that we can pull updates from them as they occur. This implies that the ideal structure will involve a wrapper that represents the cordova bits.

First, let us consider the anatomy of an AWARE plugin.
@xubowenhaoren pointed to the battery plugin as the easiest to understand.

https://github.com/denzilferreira/aware-client/blob/master/aware-core/src/main/java/com/aware/Battery.java

It looks like:

  1. AWARE does not use SQLite directly but uses the ContentProvider interface instead. The rows are constructed as ContentValues objects and then put into the resolver

    context.getContentResolver().insert(Battery_Data.CONTENT_URI, rowData);
    
  2. Each ContentProvider (and by extension, each sensor), has its own SyncAdapter associated with it. The sync request is set here ContentResolver.requestSync(request);

So probably the easiest approach is to continue using the ContentProvider architecture for AWARE. In terms of syncing, you can choose to sync to an AWARE server and then merge the data on the server side, or you can write a new SyncAdapter that syncs the data to an e-mission server.

Note that every sensor in AWARE has its own sync adapter. But the base class appears to be
https://github.com/denzilferreira/aware-client/blob/master/aware-core/src/main/java/com/aware/syncadapters/AwareSyncAdapter.java
Modifying the onPerformSync method of that adapter (or any of the methods it calls, such as offloadData) should make it possible to choose where the data goes to the server.

But that decision is independent of pluginizing the sensor. IMHO, the easiest way to pluginize this code is to leave it untouched and to write a wrapper that inherits from CordovaPlugin. You could either define an external method to start the data collection, which would be consistent with accepting consent and then turning on tracking, or you could just implement the init method of the plugin and have it create the BatteryService and start it.

You could also choose to do this in two steps:

  1. Only pluginize the sensor, and continue to send the data to an AWARE server
  2. After getting that to work, implement a new sync adapter to e-mission if necessary

That will also get you familiar with cordova and reduce the unknown factor.

There are many examples of cordova plugins on the internet. Including the Cordova battery plugin https://github.com/apache/cordova-plugin-battery-status that you can look at for an example of the the cordova plugin interface.

This defines an externally callable method called "start" which sets up the listeners. In your case, that would call onCreate on the AWARE battery service. You would then call start from the javascript during onboarding and everything will Just Work.
https://github.com/apache/cordova-plugin-battery-status/blob/master/src/android/BatteryListener.java#L57

I think cordova also has a user guide or some kind of similar documentation.

You mentioned that each e-mission plugin only runs after the user consents. So in the repackaging, do I need to check for user contents as well?

It is up to you. You are building your own version of the app and can do whatever you want. Theoretically, you can call markConsented on the e-mission plugin without showing anything to the user since it is just a javascript call.

So it is possible using the e-mission plugin to skip the consent. I just chose to believe that most people will be careful of consent and gave them the tools to handle it properly. e.g. the markConsented method.

So basically if I didn't have the method, then people would have to build it in and everybody is lazy. I made it easier (the default) to start tracking only after consent, and harder to remove it (have to edit onboarding).

@ariasfelipe also wrote a shake detection plugin that used local processing on the phone to detect shakes. This is a much more scalable solution because otherwise the volume of accelerometer data is too high. He demoed it, but I am not sure how well it works in the real world.
https://github.com/e-mission/e-mission-shake-detect/

@xubowenhaoren if you just want an example of wrapping native code in a plugin, I would not recommend the shake detect code, since it is much more complex than you need.
You should use the step-by-step cordova guide
https://cordova.apache.org/docs/en/latest/guide/hybrid/plugins/
combined with the simplistic example
https://github.com/ModusCreateOrg/cordova-plugin-example
or other tutorials on the internet
https://blogs.sap.com/2017/08/18/step-by-step-to-create-a-custom-cordova-plugin-for-android-and-consume-it-in-your-ui5-application/

The shake detect code does allow the user to configure the prompt that is displayed when the shake is detected. What kind of local processing are you planning to do, and what do you want as the resulting output?

I want the output to match current AWARE standards, making a functionally equivalent migration. Therefore, I would likely need to follow AWARE's approach of wrapping data. Here is one of their data providers.

with the approach outlined in #501 (comment) you will continue to use the AWARE data format. In fact, you will continue to use their local storage and their server (at least in step 1). So I am not sure what this comment means.

Note that in #501 (comment), I am talking about wrapping code, not data

Update 1:

You don't care about the AWARE implementation details. Once the AWARE services are launched in the background, they will continue to do their stuff in their way. If that changes in the new rewrite, you are not affected.

All you need to do is to wrap the code and launch the aware services properly from the init or exec function of the plugin. Nothing else.

Update 2:

In other words, remember all the lectures on software modularity :) focus on the interface and not the implementation.

Thanks for the clarification. It seems like you've made structural changes to the e-mission-docs and I cannot find your notes on how to add a new plugin to a client. The closest I could find is this deployment guide.

@xubowenhaoren I don't think I ever had notes on how to add a new plugin to a client.

Apache Cordova is standard, well-documented technology, and I don't want to duplicate their documentation.

I have added links to the Apache Cordova documentation on creating and managing plugins
#501 (comment)
along with a link to a third party guide.

Now, looking back at what you initially suggested last time and what I've done at this point, I'm afraid that my approach has been somewhat misdirected. What I've done is that I tried to copy over AWARE codes, change them to override e-mission methods and fix any dependency issues along the way. With that said, there are certainly AWARE modules (e.g. SSL certificate helpers) that I don't know how to handle yet.

I think this is NOT what you meant by "creating wrappers" to codes (not data).

About the user consent: before we start collectin the user data, we would ask the participant to sign a consent form. This is done before the participants install their apps, and it's very unlikely that the consent criteria need to change during the study. So we don't need to handle consents in this repackaging of the app.

I suggest that you create a cordova plugin with the following interface.

in www/aware.js:

/*global cordova, module*/

var exec = require("cordova/exec")

var Aware = {
}

in src/android/AwarePlugin.java something like this

import aware.....AwareService;

public class AwarePlugin extends CordovaPlugin {
    public static final String TAG = "AwarePlugin";

    @Override
    public void pluginInitialize() {
        AwareService service = new AwareService();
        service.start();
    }
}

AWARE clients read aware-core status and modify aware-core settings via a central Aware.settings method. In this repackaging, this would not be directly accessible. Should I then expose Aware.settings in aware.js as JS methods, then use the AwarePlugin.java interface to call the native Aware.settings method?

Yes. In that case, you would want to do something like

in www/aware.js (similar to datacollection.js)

/*global cordova, module*/

var exec = require("cordova/exec")

var Aware = {
    getConfig: function () {
        return new Promise(function(resolve, reject) {
            exec(resolve, reject, "Aware", "getConfig", []);
        });
    },
    setConfig: function (newConfig) {
        return new Promise(function(resolve, reject) {
            exec(resolve, reject, "Aware", "setConfig", [newConfig]);
        });
    },
}

in src/android/AwarePlugin.java (similar to DataCollectionPlugin.java)

import aware.....AwareService;

public class AwarePlugin extends CordovaPlugin {
    public static final String TAG = "AwarePlugin";

    @Override
    public void pluginInitialize() {
        AwareService service = new AwareService();
        service.start();
    }

    @Override
    public boolean execute(String action, JSONArray data, final CallbackContext callbackContext) throws JSONException {
        if (action.equals("getConfig")) {
            // call AWARE code to get settings
        } else if (action.equals("setConfig")) {
           // call AWARE code to set settings
        }
    }
}

I believe that this is the main AWARE service which launches all the other services depending on the settings.
https://github.com/denzilferreira/aware-client/blob/master/aware-core/src/main/java/com/aware/Aware.java

Hi,

I realize that the whole aware-core service can be incorporated conveniently via a maven import. Could you point me to an example showing how I can achieve this in a Cordova plugin.xml?

searching for "plugin.xml maven", I find the cordova plugin documentation
https://cordova.apache.org/docs/en/dev/plugin_ref/spec.html
which includes the example

On Android (as of cordova-android@4.0.0), framework tags are used to include Maven dependencies, or to include bundled library projects.

Thanks! But here we have a follow-up problem. Since now we import aware-core services via maven, how should we register the services (e.g. Aware.java) in the Cordova plugin.xml?

With the FQN,

so you would do something like

        <service
            android:name="com.aware....FooService"
            android:enabled="true"
            android:exported="false">
        </service>

Another follow-up: in line 119, you are linking the project source codes and res files in the project.xml. Do I need to do this for the aware-core files?

No. Those are only for source code that will be compiled as part of the plugin, not complied libraries.

Hello,

I wish to add a (placeholder) UI page that will eventually be replaced by the functional Aware UI. Where can I add this page as a fragment on the current e-mission UI? Is it the main.js?

Probably the easiest is to add a new tab. You can look at examples of current tabs or the angular router documentation.

  <!-- Diary Tab -->
  <ion-tab title="Diary" icon="ion-map" href="#/root/main/diary">
    <ion-nav-view name="main-diary"></ion-nav-view>
  </ion-tab>

In the Diary Tab above, what does the href attribute refer to? Also, after I put in the web UI files in www/js and www/templates, what do I need to put in main.html to refer to those files?

@xubowenhaoren this is not e-mission specific, this is standard angular-ui-router syntax. There are hundreds of tutorials on angular-ui-router. Please check them out.

To check if I get this right:

The main.js has a list of angular.module, each with a $stateProvider$.
The main.html defines the actual order of these modules.

Take the root.main.accessmap as an example. In main.js, it is defined as

    .state('root.main.accessmap', {
    url: '/accessmap',
    views: {
      'main-accessmap': {
        templateUrl: 'templates/accessmap/accessmap.html',
        controller: 'AccessMapCtrl'
      }
    },
  })

The templateUrl specifies the HTML location of the page while controller defines the JS elements (e.g. buttons) in js/accessmap/accessmap.js.

As a self-answer to my earlier question , href is for ionic to navigate through pages and not workspace file locations; name in ion-nav-view corresponds to the views in main.js.

Please address any mistakes or missing pieces.

I tried to add a placeholder Aware UI page with minimal modification to the AccessMap page. I added aware.js and aware.html. I also added the following config in main.js:

  // aware page
    .state('root.main.aware', {
    url: '/aware',
    views: {
      'main-aware': {
        templateUrl: 'templates/aware/aware.html',
        controller: 'AwareCtrl'
      }
    },
  })

I then followed steps to build a debug Cordova app here. However, when running cordova prepare, I only got the following:

Running command: /Users/bowenxu2/Documents/GitHub/e-mission-phone/hooks/after_prepare/010_add_platform_class.js /Users/bowenxu2/Documents/GitHub/e-mission-phone
add to body class: platform-android
add to body class: platform-ios
Running command: /Users/bowenxu2/Documents/GitHub/e-mission-phone/hooks/after_prepare/015_copy_icon_to_drawable.js /Users/bowenxu2/Documents/GitHub/e-mission-phone
About to copy file /Users/bowenxu2/Documents/GitHub/e-mission-phone/platforms/android/res/mipmap-hdpi/icon.png -> /Users/bowenxu2/Documents/GitHub/e-mission-phone/platforms/android/res/drawable-hdpi/icon.png
... Other res copying logs ... 
Running command: /Users/bowenxu2/Documents/GitHub/e-mission-phone/package-hooks/ios9_allow_http.sh /Users/bowenxu2/Documents/GitHub/e-mission-phone

That was it. No Creating Cordova project for the Android platform: of any kind.

I am running ionic 3.19.1 and Cordova 8.1.2. What am I missing?

You are not reading the e-mission-phone specific instructions which cover using the same codebase for UI development as well as building a native app. https://github.com/e-mission/e-mission-phone#installation
in particular the ./bin/configure_xml_and_json.js step

After some debugging, I found that the issue is most closely related to the local Aware plugin that I'm working on.

However, I think I messed up my local Cordova project so that even after running cordova plugin rm edu.berkeley.eecs.emission.cordova.aware, the dependency compile "com.github.denzilferreira:aware-client:$aware_libs" still shows up when I try cordova build android. I will clean up and reset the local project and try again..

To detect where exactly the error is, I reset my local e-mission workspace to match the latest opentoall branch. I followed the steps in the installation guide.

Then I replaced config.xml and package.json with my own (only changes are the package names/id). I also added the google-services.json and GoogleService-Info.plist that were tested to work in 2019. Note that I did this after running

./bin/configure_xml_and_json.js cordovabuild

As suggested by this guide here.

However, even with no other modification, the build had failed:

:transformClassesWithDexBuilderForDebugcom.android.builder.dexing.DexArchiveBuilderException: com.android.builder.dexing.DexArchiveBuilderException: Failed to process /Users/bowenxu2/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.6/9180733b7df8542621dc12e21e87557e8c99b8cb/gson-2.8.6.jar
	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
	at java.util.concurrent.ForkJoinTask.getThrowableException(ForkJoinTask.java:593)
	at java.util.concurrent.ForkJoinTask.reportException(ForkJoinTask.java:677)
	at java.util.concurrent.ForkJoinTask.join(ForkJoinTask.java:720)
	at com.android.ide.common.internal.WaitableExecutor.waitForTasksWithQuickFail(WaitableExecutor.java:146)
	at com.android.build.gradle.internal.transforms.DexArchiveBuilderTransform.transform(DexArchiveBuilderTransform.java:235)
	at com.android.build.gradle.internal.pipeline.TransformTask$2.call(TransformTask.java:222)
	at com.android.build.gradle.internal.pipeline.TransformTask$2.call(TransformTask.java:218)
	at com.android.builder.profile.ThreadRecorder.record(ThreadRecorder.java:102)
	at com.android.build.gradle.internal.pipeline.TransformTask.transform(TransformTask.java:213)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:73)
	at org.gradle.api.internal.project.taskfactory.DefaultTaskClassInfoStore$IncrementalTaskAction.doExecute(DefaultTaskClassInfoStore.java:173)
	at org.gradle.api.internal.project.taskfactory.DefaultTaskClassInfoStore$StandardTaskAction.execute(DefaultTaskClassInfoStore.java:134)
	at org.gradle.api.internal.project.taskfactory.DefaultTaskClassInfoStore$StandardTaskAction.execute(DefaultTaskClassInfoStore.java:121)
	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter$1.run(ExecuteActionsTaskExecuter.java:122)
	at org.gradle.internal.progress.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:336)
	at org.gradle.internal.progress.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:328)
	at org.gradle.internal.progress.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:197)
	at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:107)
	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeAction(ExecuteActionsTaskExecuter.java:111)
	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:92)
	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:70)
	at org.gradle.api.internal.tasks.execution.SkipUpToDateTaskExecuter.execute(SkipUpToDateTaskExecuter.java:63)
	at org.gradle.api.internal.tasks.execution.ResolveTaskOutputCachingStateExecuter.execute(ResolveTaskOutputCachingStateExecuter.java:54)
	at org.gradle.api.internal.tasks.execution.ValidatingTaskExecuter.execute(ValidatingTaskExecuter.java:58)
	at org.gradle.api.internal.tasks.execution.SkipEmptySourceFilesTaskExecuter.execute(SkipEmptySourceFilesTaskExecuter.java:88)
	at org.gradle.api.internal.tasks.execution.ResolveTaskArtifactStateTaskExecuter.execute(ResolveTaskArtifactStateTaskExecuter.java:52)
	at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:52)
	at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:54)
	at org.gradle.api.internal.tasks.execution.ExecuteAtMostOnceTaskExecuter.execute(ExecuteAtMostOnceTaskExecuter.java:43)
	at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:34)
	at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker$1.run(DefaultTaskGraphExecuter.java:248)
	at org.gradle.internal.progress.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:336)
	at org.gradle.internal.progress.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:328)
	at org.gradle.internal.progress.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:197)
	at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:107)
	at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker.execute(DefaultTaskGraphExecuter.java:241)
	at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker.execute(DefaultTaskGraphExecuter.java:230)
	at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$TaskExecutorWorker.processTask(DefaultTaskPlanExecutor.java:124)
	at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$TaskExecutorWorker.access$200(DefaultTaskPlanExecutor.java:80)
	at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$TaskExecutorWorker$1.execute(DefaultTaskPlanExecutor.java:105)
	at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$TaskExecutorWorker$1.execute(DefaultTaskPlanExecutor.java:99)
	at org.gradle.execution.taskgraph.DefaultTaskExecutionPlan.execute(DefaultTaskExecutionPlan.java:625)
	at org.gradle.execution.taskgraph.DefaultTaskExecutionPlan.executeWithTask(DefaultTaskExecutionPlan.java:580)
	at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$TaskExecutorWorker.run(DefaultTaskPlanExecutor.java:99)
	at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:63)
	at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:46)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:55)
	at java.lang.Thread.run(Thread.java:748)
Caused by: com.android.builder.dexing.DexArchiveBuilderException: Failed to process /Users/bowenxu2/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.6/9180733b7df8542621dc12e21e87557e8c99b8cb/gson-2.8.6.jar
	at com.android.build.gradle.internal.transforms.DexArchiveBuilderTransform.launchProcessing(DexArchiveBuilderTransform.java:550)
	at com.android.build.gradle.internal.transforms.DexArchiveBuilderTransform.lambda$convertToDexArchive$1(DexArchiveBuilderTransform.java:488)
	at java.util.concurrent.ForkJoinTask$AdaptedCallable.exec(ForkJoinTask.java:1424)
	at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
	at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1056)
	at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1692)
	at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)
Caused by: com.android.builder.dexing.DexArchiveBuilderException: Error while dexing module-info.class

	at com.android.builder.dexing.DxDexArchiveBuilder.getExceptionToRethrow(DxDexArchiveBuilder.java:143)
	at com.android.builder.dexing.DxDexArchiveBuilder.convert(DxDexArchiveBuilder.java:89)
	at com.android.build.gradle.internal.transforms.DexArchiveBuilderTransform.launchProcessing(DexArchiveBuilderTransform.java:545)
	... 6 more
Caused by: com.android.dx.cf.iface.ParseException: unsupported class file version 53.0
	at com.android.dx.cf.direct.DirectClassFile.parse0(DirectClassFile.java:499)
	at com.android.dx.cf.direct.DirectClassFile.parse(DirectClassFile.java:420)
	at com.android.dx.cf.direct.DirectClassFile.parseToInterfacesIfNecessary(DirectClassFile.java:402)
	at com.android.dx.cf.direct.DirectClassFile.getMagic(DirectClassFile.java:253)
	at com.android.builder.dexing.DxDexArchiveBuilder.dex(DxDexArchiveBuilder.java:99)
	at com.android.builder.dexing.DxDexArchiveBuilder.convert(DxDexArchiveBuilder.java:86)
	... 7 more

 FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':transformClassesWithDexBuilderForDebug'.
> com.android.build.api.transform.TransformException: com.android.builder.dexing.DexArchiveBuilderException: com.android.builder.dexing.DexArchiveBuilderException: Failed to process /Users/bowenxu2/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.6/9180733b7df8542621dc12e21e87557e8c99b8cb/gson-2.8.6.jar

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.

* Get more help at https://help.gradle.org

BUILD FAILED in 4s
35 actionable tasks: 2 executed, 33 up-to-date
(node:42514) UnhandledPromiseRejectionWarning: Error: /Users/bowenxu2/Documents/GitHub/e-mission-phone/platforms/android/gradlew: Command failed with exit code 1 Error output:
com.android.builder.dexing.DexArchiveBuilderException: com.android.builder.dexing.DexArchiveBuilderException: Failed to process /Users/bowenxu2/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.6/9180733b7df8542621dc12e21e87557e8c99b8cb/gson-2.8.6.jar
	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
	at java.util.concurrent.ForkJoinTask.getThrowableException(ForkJoinTask.java:593)
	at java.util.concurrent.ForkJoinTask.reportException(ForkJoinTask.java:677)
	at java.util.concurrent.ForkJoinTask.join(ForkJoinTask.java:720)
	at com.android.ide.common.internal.WaitableExecutor.waitForTasksWithQuickFail(WaitableExecutor.java:146)
	at com.android.build.gradle.internal.transforms.DexArchiveBuilderTransform.transform(DexArchiveBuilderTransform.java:235)
	at com.android.build.gradle.internal.pipeline.TransformTask$2.call(TransformTask.java:222)
	at com.android.build.gradle.internal.pipeline.TransformTask$2.call(TransformTask.java:218)
	at com.android.builder.profile.ThreadRecorder.record(ThreadRecorder.java:102)
	at com.android.build.gradle.internal.pipeline.TransformTask.transform(TransformTask.java:213)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:73)
	at org.gradle.api.internal.project.taskfactory.DefaultTaskClassInfoStore$IncrementalTaskAction.doExecute(DefaultTaskClassInfoStore.java:173)
	at org.gradle.api.internal.project.taskfactory.DefaultTaskClassInfoStore$StandardTaskAction.execute(DefaultTaskClassInfoStore.java:134)
	at org.gradle.api.internal.project.taskfactory.DefaultTaskClassInfoStore$StandardTaskAction.execute(DefaultTaskClassInfoStore.java:121)
	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter$1.run(ExecuteActionsTaskExecuter.java:122)
	at org.gradle.internal.progress.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:336)
	at org.gradle.internal.progress.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:328)
	at org.gradle.internal.progress.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:197)
	at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:107)
	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeAction(ExecuteActionsTaskExecuter.java:111)
	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:92)
	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:70)
	at org.gradle.api.internal.tasks.execution.SkipUpToDateTaskExecuter.execute(SkipUpToDateTaskExecuter.java:63)
	at org.gradle.api.internal.tasks.execution.ResolveTaskOutputCachingStateExecuter.execute(ResolveTaskOutputCachingStateExecuter.java:54)
	at org.gradle.api.internal.tasks.execution.ValidatingTaskExecuter.execute(ValidatingTaskExecuter.java:58)
	at org.gradle.api.internal.tasks.execution.SkipEmptySourceFilesTaskExecuter.execute(SkipEmptySourceFilesTaskExecuter.java:88)
	at org.gradle.api.internal.tasks.execution.ResolveTaskArtifactStateTaskExecuter.execute(ResolveTaskArtifactStateTaskExecuter.java:52)
	at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:52)
	at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:54)
	at org.gradle.api.internal.tasks.execution.ExecuteAtMostOnceTaskExecuter.execute(ExecuteAtMostOnceTaskExecuter.java:43)
	at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:34)
	at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker$1.run(DefaultTaskGraphExecuter.java:248)
	at org.gradle.internal.progress.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:336)
	at org.gradle.internal.progress.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:328)
	at org.gradle.internal.progress.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:197)
	at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:107)
	at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker.execute(DefaultTaskGraphExecuter.java:241)
	at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker.execute(DefaultTaskGraphExecuter.java:230)
	at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$TaskExecutorWorker.processTask(DefaultTaskPlanExecutor.java:124)
	at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$TaskExecutorWorker.access$200(DefaultTaskPlanExecutor.java:80)
	at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$TaskExecutorWorker$1.execute(DefaultTaskPlanExecutor.java:105)
	at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$TaskExecutorWorker$1.execute(DefaultTaskPlanExecutor.java:99)
	at org.gradle.execution.taskgraph.DefaultTaskExecutionPlan.execute(DefaultTaskExecutionPlan.java:625)
	at org.gradle.execution.taskgraph.DefaultTaskExecutionPlan.executeWithTask(DefaultTaskExecutionPlan.java:580)
	at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$TaskExecutorWorker.run(DefaultTaskPlanExecutor.java:99)
	at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:63)
	at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:46)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:55)
	at java.lang.Thread.run(Thread.java:748)
Caused by: com.android.builder.dexing.DexArchiveBuilderException: Failed to process /Users/bowenxu2/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.6/9180733b7df8542621dc12e21e87557e8c99b8cb/gson-2.8.6.jar
	at com.android.build.gradle.internal.transforms.DexArchiveBuilderTransform.launchProcessing(DexArchiveBuilderTransform.java:550)
	at com.android.build.gradle.internal.transforms.DexArchiveBuilderTransform.lambda$convertToDexArchive$1(DexArchiveBuilderTransform.java:488)
	at java.util.concurrent.ForkJoinTask$AdaptedCallable.exec(ForkJoinTask.java:1424)
	at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
	at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1056)
	at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1692)
	at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)
Caused by: com.android.builder.dexing.DexArchiveBuilderException: Error while dexing module-info.class

	at com.android.builder.dexing.DxDexArchiveBuilder.getExceptionToRethrow(DxDexArchiveBuilder.java:143)
	at com.android.builder.dexing.DxDexArchiveBuilder.convert(DxDexArchiveBuilder.java:89)
	at com.android.build.gradle.internal.transforms.DexArchiveBuilderTransform.launchProcessing(DexArchiveBuilderTransform.java:545)
	... 6 more
Caused by: com.android.dx.cf.iface.ParseException: unsupported class file version 53.0
	at com.android.dx.cf.direct.DirectClassFile.parse0(DirectClassFile.java:499)
	at com.android.dx.cf.direct.DirectClassFile.parse(DirectClassFile.java:420)
	at com.android.dx.cf.direct.DirectClassFile.parseToInterfacesIfNecessary(DirectClassFile.java:402)
	at com.android.dx.cf.direct.DirectClassFile.getMagic(DirectClassFile.java:253)
	at com.android.builder.dexing.DxDexArchiveBuilder.dex(DxDexArchiveBuilder.java:99)
	at com.android.builder.dexing.DxDexArchiveBuilder.convert(DxDexArchiveBuilder.java:86)
	... 7 more


FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':transformClassesWithDexBuilderForDebug'.
> com.android.build.api.transform.TransformException: com.android.builder.dexing.DexArchiveBuilderException: com.android.builder.dexing.DexArchiveBuilderException: Failed to process /Users/bowenxu2/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.6/9180733b7df8542621dc12e21e87557e8c99b8cb/gson-2.8.6.jar

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.

* Get more help at https://help.gradle.org

BUILD FAILED in 4s
    at ChildProcess.whenDone (/Users/bowenxu2/Documents/GitHub/e-mission-phone/platforms/android/cordova/node_modules/cordova-common/src/superspawn.js:169:23)
    at ChildProcess.emit (events.js:189:13)
    at maybeClose (internal/child_process.js:970:16)
    at Process.ChildProcess._handle.onexit (internal/child_process.js:259:5)
(node:42514) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)

Did you search the previous issues?
https://github.com/e-mission/e-mission-docs/search?q=DexArchiveBuilderException&type=Issues
This has been reported earlier and there is a workaround listed there.
I have also checked in that workaround into the data collection plugin. Check the release notes for details. You can't just use the most recent version.

We figured that the aware-client relies on jitpack.io as the maven URL. We have figured a workaround:

In the project build.gradle, add maven { url 'https://jitpack.io' } to all repositories. Here is our example:

buildscript {
    repositories {
        jcenter()
        maven {
            url "https://maven.google.com"
        }
        maven {
          url 'https://jitpack.io'
        }
    }

    // Switch the Android Gradle plugin version requirement depending on the
    // installed version of Gradle. This dependency is documented at
    // http://tools.android.com/tech-docs/new-build-system/version-compatibility
    // and https://issues.apache.org/jira/browse/CB-8143
    dependencies {
        classpath 'com.android.tools.build:gradle:3.0.0'
    }
}

// Allow plugins to declare Maven dependencies via build-extras.gradle.
allprojects {
    repositories {
        jcenter()
        maven {
            url "https://maven.google.com"
        }
        maven {
          url 'https://jitpack.io'
        }
    }
}

The next step is to automate this step in the plugin so that this can happen without a manual change in the project level.

here's an example of an extra gradle file included in the plugin that can be included in the overall project gradle file

https://github.com/e-mission/cordova-server-communication/tree/master/src/android/httplib.gradle

Now you have to figure out how to make it work for this gradle command.

Issue we had today:

error: style attribute 'android:attr/dialogCornerRadius' not found.
error: resource android:attr/fontVariationSettings not found.
error: resource android:attr/ttcIndex not found.
error: resource android:attr/lineHeight not found.
error: resource android:attr/textFontWeight not found.

What we tried: In build.gradle, add compileSdkVersion 28. It didn't work.

What to expect: aware-client switched to use AndroidX last year. The mismatch between AndroidX and legacy android appcompact could be the issue. There is an option in Android Studio to auto-convert all dependencies to AndroidX. I will try this and update it later.

Auto-conversion to either AndroidX or appcompact didn't work. I will update more findings later.

It looks like it may be related to the version of the support libraries
apache/cordova-android#591

Looking through the places where we include the support libraries, it looks like we do indeed include a bunch of support libraries with various versions of + in there. Including one from my plugin - e.g. edu.berkeley.eecs.emission.cordova.unifiedlogger.

However, without the AWARE plugin, there are no build issues - see working CI in the new repo
https://github.com/covid19database/phone-app
Does the AWARE plugin introduce a newer version of the support library?

Can you see which versions of the support library are in the gradle file and try to pare it down to one?

plugins//cordova-plugin-local-notification/plugin.xml:        <framework src="com.android.support:support-v4:26.+" />
plugins//cordova-plugin-email-composer/plugin.xml:        <framework src="com.android.support:support-v4:24.1.1+" />
plugins//edu.berkeley.eecs.emission.cordova.unifiedlogger/plugin.xml:    <framework src="com.android.support:support-v4:+" />
plugins//edu.berkeley.eecs.emission.cordova.auth/src/android/openid-config.gradle:    resolutionStrategy.force 'com.android.support:support-v4:26.1.0'

Does the AWARE plugin introduce a newer version of the support library?

Yes. Here is a snippet from the aware-core build.gradle:

    implementation "androidx.gridlayout:gridlayout:1.0.0"
    implementation "androidx.cardview:cardview:1.0.0"

    implementation "androidx.annotation:annotation:1.1.0"
    implementation "androidx.core:core:1.2.0-rc01"
    implementation "com.google.android.material:material:1.2.0-alpha02"
    implementation "androidx.appcompat:appcompat:1.1.0"

We can see that the aware-core is indeed using Android. To be specific, in the artifact mapping table,
com.android.support:support-compat is indeed androidx.core:core.

I tried to force the dependencies in the Cordova project build.gradle to stick to SDK 28, but Android Studio still gave the following warnings about mixed usage.

    implementation fileTree(dir: 'libs', include: '*.jar')
    // SUB-PROJECT DEPENDENCIES START
    implementation(project(path: "CordovaLib"))
    compile "com.android.support:support-v13:28.0.3"
    compile "me.leolin:ShortcutBadger:1.1.17@aar"
    compile "com.google.firebase:firebase-messaging:17.0.0"
    compile "com.android.support:support-v4:28.1.1+"
    compile "com.android.support:support-v4:+"
    compile "com.google.android.gms:play-services-auth:17.0.0"
    compile "net.openid:appauth:0.7.0"
    compile "com.auth0.android:jwtdecode:1.1.1"
    compile "com.google.code.gson:gson:2.8.5"
    compile "com.google.android.gms:play-services-location:17.0.0"
    compile "com.github.denzilferreira:aware-client:master-SNAPSHOT"

image

It looks like I might need to change the plugin gradle files or find a "global" way to force the usage of either AndroidX or the legacy ones.

aha! did you try this or similar solutions?
https://stackoverflow.com/a/55801213/4040267

Before I had the opportunity to try that solution, I erroneously used both "Migrate to AndroidX" and "Migrate to appcompact" in Android Studio. To recover from that mistake, I thought it was best to start over from a clean e-mission project.

Thus I cloned a fresh copy and ran the procedures on the guide. I was aware of the gson issue, so in my package.json, I had

  "dependencies": {
    ...
    "e-mission-data-collection": "git+https://github.com/e-mission/e-mission-data-collection.git#v1.4",
    ...
  }

However, when I ran cordova prepare, I saw the following error:

Discovered plugin "edu.berkeley.eecs.emission.cordova.datacollection" in config.xml. Adding it to the project
Failed to restore plugin "edu.berkeley.eecs.emission.cordova.datacollection" from config.xml. You might need to try adding it again. Error: Error: npm: Command failed with exit code 1 Error output:
npm ERR! code E404
npm ERR! 404 'edu.berkeley.eecs.emission.cordova.datacollection' is not in the npm registry.
npm ERR! 404 You should bug the author to publish it
npm ERR! 404 (or use the name yourself!)
npm ERR! 404 
npm ERR! 404 Note that you can also install from a
npm ERR! 404 tarball, folder, http url, or git url.
npm ERR! 404 
npm ERR! 404  'edu.berkeley.eecs.emission.cordova.datacollection@latest' is not in the npm registry.
npm ERR! 404 You should bug the author to publish it (or use the name yourself!)
npm ERR! 404 
npm ERR! 404 Note that you can also install from a
npm ERR! 404 tarball, folder, http url, or git url.

npm ERR! A complete log of this run can be found in:
npm ERR!     /Users/bowenxu2/.npm/_logs/2020-04-16T05_06_44_530Z-debug.log

Do I need to also change some option in config.xml?

plugins are specified in both config.xml and package.json. which is why configure_xml_and_json copies them both over. Did you follow the instructions exactly?

plugins are specified in both config.xml and package.json. which is why configure_xml_and_json copies them both over. Did you follow the instructions exactly?

I previously had custom config.xml and package.json. I have updated them correctly with the new data collection plugin. Thanks for the tip!

aha! did you try this or similar solutions?
https://stackoverflow.com/a/55801213/4040267

I tried. Logs below.

$ cordova plugin add cordova-plugin-androidx
Plugin doesn't support this project's cordova-android version. cordova-android: 6.4.0, failed version requirement: >=8.0.0
Skipping 'cordova-plugin-androidx' for android

$ cordova platform update android@8.0.0
Using cordova-fetch for cordova-android@8.0.0
Updating android project...
(node:72372) UnhandledPromiseRejectionWarning: An in-place platform update is not supported. 
The `platforms` folder is always treated as a build artifact in the CLI workflow.
To update your platform, you have to remove, then add your android platform again.
Make sure you save your plugins beforehand using `cordova plugin save`, and save 
a copy of the platform first if you had manual changes in it.
	cordova plugin save
	cordova platform rm android
	cordova platform add android

$ cordova plugin save
$ cordova platform rm android
$ cordova platform add android@8.0.0
... (no errors)

$ cordova plugin add cordova-plugin-androidx
Plugin "cordova-plugin-androidx" already installed on android.
Plugin "cordova-plugin-androidx" already installed on ios.
Adding cordova-plugin-androidx to package.json
Saved plugin info for "cordova-plugin-androidx" to config.xml

$ cordova plugin add cordova-plugin-androidx-adapter
Installing "cordova-plugin-androidx-adapter" for android
Installing "cordova-plugin-androidx-adapter" for ios
Adding cordova-plugin-androidx-adapter to package.json
Saved plugin info for "cordova-plugin-androidx-adapter" to config.xml

Then I opened up CordovaLib in Android Studio and tried to build.

FAILURE: Build completed with 2 failures.

1: Task failed with an exception.
-----------
* Where:
Script '/Users/bowenxu2/Documents/GitHub/e-mission-phone/platforms/android/CordovaLib/cordova.gradle' line: 121

* What went wrong:
A problem occurred evaluating project ':app'.
> Unable to determine Android SDK directory.

* Try:
Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Exception is:
org.gradle.api.GradleScriptException: A problem occurred evaluating project ':app'.
	at org.gradle.groovy.scripts.internal.DefaultScriptRunnerFactory$ScriptRunnerImpl.run(DefaultScriptRunnerFactory.java:92)
	at org.gradle.configuration.DefaultScriptPluginFactory$ScriptPluginImpl$2.run(DefaultScriptPluginFactory.java:206)
	at org.gradle.configuration.ProjectScriptTarget.addConfiguration(ProjectScriptTarget.java:77)
	at org.gradle.configuration.DefaultScriptPluginFactory$ScriptPluginImpl.apply(DefaultScriptPluginFactory.java:211)
	at org.gradle.configuration.BuildOperationScriptPlugin$1$1.run(BuildOperationScriptPlugin.java:69)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:402)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:394)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$1.execute(DefaultBuildOperationExecutor.java:165)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:250)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:158)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:92)
	at org.gradle.internal.operations.DelegatingBuildOperationExecutor.run(DelegatingBuildOperationExecutor.java:31)
	at org.gradle.configuration.BuildOperationScriptPlugin$1.execute(BuildOperationScriptPlugin.java:66)
	at org.gradle.configuration.BuildOperationScriptPlugin$1.execute(BuildOperationScriptPlugin.java:63)
	at org.gradle.configuration.internal.DefaultUserCodeApplicationContext.apply(DefaultUserCodeApplicationContext.java:48)
	at org.gradle.configuration.BuildOperationScriptPlugin.apply(BuildOperationScriptPlugin.java:63)
	at org.gradle.configuration.project.BuildScriptProcessor$1.run(BuildScriptProcessor.java:44)
	at org.gradle.internal.Factories$1.create(Factories.java:25)
	at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.withMutableState(DefaultProjectStateRegistry.java:200)
	at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.withMutableState(DefaultProjectStateRegistry.java:186)
	at org.gradle.configuration.project.BuildScriptProcessor.execute(BuildScriptProcessor.java:41)
	at org.gradle.configuration.project.BuildScriptProcessor.execute(BuildScriptProcessor.java:26)
	at org.gradle.configuration.project.ConfigureActionsProjectEvaluator.evaluate(ConfigureActionsProjectEvaluator.java:34)
	at org.gradle.configuration.project.LifecycleProjectEvaluator$EvaluateProject$1.run(LifecycleProjectEvaluator.java:106)
	at org.gradle.internal.Factories$1.create(Factories.java:25)
	at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:183)
	at org.gradle.internal.work.StopShieldingWorkerLeaseService.withLocks(StopShieldingWorkerLeaseService.java:40)
	at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.withProjectLock(DefaultProjectStateRegistry.java:226)
	at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.withMutableState(DefaultProjectStateRegistry.java:220)
	at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.withMutableState(DefaultProjectStateRegistry.java:186)
	at org.gradle.configuration.project.LifecycleProjectEvaluator$EvaluateProject.run(LifecycleProjectEvaluator.java:95)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:402)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:394)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$1.execute(DefaultBuildOperationExecutor.java:165)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:250)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:158)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:92)
	at org.gradle.internal.operations.DelegatingBuildOperationExecutor.run(DelegatingBuildOperationExecutor.java:31)
	at org.gradle.configuration.project.LifecycleProjectEvaluator.evaluate(LifecycleProjectEvaluator.java:67)
	at org.gradle.api.internal.project.DefaultProject.evaluate(DefaultProject.java:695)
	at org.gradle.api.internal.project.DefaultProject.evaluate(DefaultProject.java:143)
	at org.gradle.execution.TaskPathProjectEvaluator.configure(TaskPathProjectEvaluator.java:35)
	at org.gradle.execution.TaskPathProjectEvaluator.configureHierarchy(TaskPathProjectEvaluator.java:62)
	at org.gradle.configuration.DefaultBuildConfigurer.configure(DefaultBuildConfigurer.java:41)
	at org.gradle.initialization.DefaultGradleLauncher$ConfigureBuild.run(DefaultGradleLauncher.java:302)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:402)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:394)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$1.execute(DefaultBuildOperationExecutor.java:165)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:250)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:158)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:92)
	at org.gradle.internal.operations.DelegatingBuildOperationExecutor.run(DelegatingBuildOperationExecutor.java:31)
	at org.gradle.initialization.DefaultGradleLauncher.configureBuild(DefaultGradleLauncher.java:210)
	at org.gradle.initialization.DefaultGradleLauncher.doBuildStages(DefaultGradleLauncher.java:151)
	at org.gradle.initialization.DefaultGradleLauncher.getConfiguredBuild(DefaultGradleLauncher.java:129)
	at org.gradle.internal.invocation.GradleBuildController$2.execute(GradleBuildController.java:67)
	at org.gradle.internal.invocation.GradleBuildController$2.execute(GradleBuildController.java:64)
	at org.gradle.internal.invocation.GradleBuildController$3.create(GradleBuildController.java:82)
	at org.gradle.internal.invocation.GradleBuildController$3.create(GradleBuildController.java:75)
	at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:183)
	at org.gradle.internal.work.StopShieldingWorkerLeaseService.withLocks(StopShieldingWorkerLeaseService.java:40)
	at org.gradle.internal.invocation.GradleBuildController.doBuild(GradleBuildController.java:75)
	at org.gradle.internal.invocation.GradleBuildController.configure(GradleBuildController.java:64)
	at org.gradle.tooling.internal.provider.runner.ClientProvidedPhasedActionRunner.run(ClientProvidedPhasedActionRunner.java:62)
	at org.gradle.launcher.exec.ChainingBuildActionRunner.run(ChainingBuildActionRunner.java:35)
	at org.gradle.launcher.exec.ChainingBuildActionRunner.run(ChainingBuildActionRunner.java:35)
	at org.gradle.launcher.exec.BuildOutcomeReportingBuildActionRunner.run(BuildOutcomeReportingBuildActionRunner.java:58)
	at org.gradle.tooling.internal.provider.ValidatingBuildActionRunner.run(ValidatingBuildActionRunner.java:32)
	at org.gradle.launcher.exec.BuildCompletionNotifyingBuildActionRunner.run(BuildCompletionNotifyingBuildActionRunner.java:39)
	at org.gradle.launcher.exec.RunAsBuildOperationBuildActionRunner$3.call(RunAsBuildOperationBuildActionRunner.java:49)
	at org.gradle.launcher.exec.RunAsBuildOperationBuildActionRunner$3.call(RunAsBuildOperationBuildActionRunner.java:44)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:416)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:406)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$1.execute(DefaultBuildOperationExecutor.java:165)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:250)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:158)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:102)
	at org.gradle.internal.operations.DelegatingBuildOperationExecutor.call(DelegatingBuildOperationExecutor.java:36)
	at org.gradle.launcher.exec.RunAsBuildOperationBuildActionRunner.run(RunAsBuildOperationBuildActionRunner.java:44)
	at org.gradle.launcher.exec.InProcessBuildActionExecuter$1.transform(InProcessBuildActionExecuter.java:49)
	at org.gradle.launcher.exec.InProcessBuildActionExecuter$1.transform(InProcessBuildActionExecuter.java:46)
	at org.gradle.composite.internal.DefaultRootBuildState.run(DefaultRootBuildState.java:78)
	at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:46)
	at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:31)
	at org.gradle.launcher.exec.BuildTreeScopeBuildActionExecuter.execute(BuildTreeScopeBuildActionExecuter.java:42)
	at org.gradle.launcher.exec.BuildTreeScopeBuildActionExecuter.execute(BuildTreeScopeBuildActionExecuter.java:28)
	at org.gradle.tooling.internal.provider.ContinuousBuildActionExecuter.execute(ContinuousBuildActionExecuter.java:78)
	at org.gradle.tooling.internal.provider.ContinuousBuildActionExecuter.execute(ContinuousBuildActionExecuter.java:52)
	at org.gradle.tooling.internal.provider.SubscribableBuildActionExecuter.execute(SubscribableBuildActionExecuter.java:59)
	at org.gradle.tooling.internal.provider.SubscribableBuildActionExecuter.execute(SubscribableBuildActionExecuter.java:36)
	at org.gradle.tooling.internal.provider.SessionScopeBuildActionExecuter.execute(SessionScopeBuildActionExecuter.java:68)
	at org.gradle.tooling.internal.provider.SessionScopeBuildActionExecuter.execute(SessionScopeBuildActionExecuter.java:38)
	at org.gradle.tooling.internal.provider.GradleThreadBuildActionExecuter.execute(GradleThreadBuildActionExecuter.java:37)
	at org.gradle.tooling.internal.provider.GradleThreadBuildActionExecuter.execute(GradleThreadBuildActionExecuter.java:26)
	at org.gradle.tooling.internal.provider.ParallelismConfigurationBuildActionExecuter.execute(ParallelismConfigurationBuildActionExecuter.java:43)
	at org.gradle.tooling.internal.provider.ParallelismConfigurationBuildActionExecuter.execute(ParallelismConfigurationBuildActionExecuter.java:29)
	at org.gradle.tooling.internal.provider.StartParamsValidatingActionExecuter.execute(StartParamsValidatingActionExecuter.java:60)
	at org.gradle.tooling.internal.provider.StartParamsValidatingActionExecuter.execute(StartParamsValidatingActionExecuter.java:32)
	at org.gradle.tooling.internal.provider.SessionFailureReportingActionExecuter.execute(SessionFailureReportingActionExecuter.java:55)
	at org.gradle.tooling.internal.provider.SessionFailureReportingActionExecuter.execute(SessionFailureReportingActionExecuter.java:41)
	at org.gradle.tooling.internal.provider.SetupLoggingActionExecuter.execute(SetupLoggingActionExecuter.java:48)
	at org.gradle.tooling.internal.provider.SetupLoggingActionExecuter.execute(SetupLoggingActionExecuter.java:32)
	at org.gradle.launcher.daemon.server.exec.ExecuteBuild.doBuild(ExecuteBuild.java:67)
	at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:36)
	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
	at org.gradle.launcher.daemon.server.exec.WatchForDisconnection.execute(WatchForDisconnection.java:37)
	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
	at org.gradle.launcher.daemon.server.exec.ResetDeprecationLogger.execute(ResetDeprecationLogger.java:26)
	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
	at org.gradle.launcher.daemon.server.exec.RequestStopIfSingleUsedDaemon.execute(RequestStopIfSingleUsedDaemon.java:34)
	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
	at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.call(ForwardClientInput.java:74)
	at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.call(ForwardClientInput.java:72)
	at org.gradle.util.Swapper.swap(Swapper.java:38)
	at org.gradle.launcher.daemon.server.exec.ForwardClientInput.execute(ForwardClientInput.java:72)
	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
	at org.gradle.launcher.daemon.server.exec.LogAndCheckHealth.execute(LogAndCheckHealth.java:55)
	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
	at org.gradle.launcher.daemon.server.exec.LogToClient.doBuild(LogToClient.java:62)
	at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:36)
	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
	at org.gradle.launcher.daemon.server.exec.EstablishBuildEnvironment.doBuild(EstablishBuildEnvironment.java:81)
	at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:36)
	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
	at org.gradle.launcher.daemon.server.exec.StartBuildOrRespondWithBusy$1.run(StartBuildOrRespondWithBusy.java:50)
	at org.gradle.launcher.daemon.server.DaemonStateCoordinator$1.run(DaemonStateCoordinator.java:295)
	at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:63)
	at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:46)
	at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:55)
Caused by: java.lang.RuntimeException: Unable to determine Android SDK directory.
	at cordova_b854vvclv68kswzvar16ge5n9.getAndroidSdkDir(/Users/bowenxu2/Documents/GitHub/e-mission-phone/platforms/android/CordovaLib/cordova.gradle:121)
	at cordova_b854vvclv68kswzvar16ge5n9.getAvailableBuildTools(/Users/bowenxu2/Documents/GitHub/e-mission-phone/platforms/android/CordovaLib/cordova.gradle:43)
	at cordova_b854vvclv68kswzvar16ge5n9.doFindLatestInstalledBuildTools(/Users/bowenxu2/Documents/GitHub/e-mission-phone/platforms/android/CordovaLib/cordova.gradle:52)
	at cordova_b854vvclv68kswzvar16ge5n9$_run_closure1$_closure13.doCall(/Users/bowenxu2/Documents/GitHub/e-mission-phone/platforms/android/CordovaLib/cordova.gradle:192)
	at cordova_b854vvclv68kswzvar16ge5n9$_run_closure1$_closure13.doCall(/Users/bowenxu2/Documents/GitHub/e-mission-phone/platforms/android/CordovaLib/cordova.gradle)
	at org.gradle.internal.extensibility.DefaultExtraPropertiesExtension.methodMissing(DefaultExtraPropertiesExtension.java:83)
	at org.gradle.internal.metaobject.BeanDynamicObject$GroovyObjectAdapter.invokeOpaqueMethod(BeanDynamicObject.java:579)
	at org.gradle.internal.metaobject.BeanDynamicObject$MetaClassAdapter.invokeMethod(BeanDynamicObject.java:506)
	at org.gradle.internal.metaobject.BeanDynamicObject.tryInvokeMethod(BeanDynamicObject.java:191)
	at org.gradle.internal.metaobject.ConfigureDelegate.invokeMethod(ConfigureDelegate.java:56)
	at build_276wgwbbmkl5cfo7tgv5i3r8m.run(/Users/bowenxu2/Documents/GitHub/e-mission-phone/platforms/android/app/build.gradle:114)
	at org.gradle.groovy.scripts.internal.DefaultScriptRunnerFactory$ScriptRunnerImpl.run(DefaultScriptRunnerFactory.java:90)
	... 128 more

==============================================================================

2: Task failed with an exception.
-----------
* What went wrong:
A problem occurred configuring project ':app'.
> compileSdkVersion is not specified.

* Try:
Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Exception is:
org.gradle.api.ProjectConfigurationException: A problem occurred configuring project ':app'.
	at org.gradle.configuration.project.LifecycleProjectEvaluator.wrapException(LifecycleProjectEvaluator.java:79)
	at org.gradle.configuration.project.LifecycleProjectEvaluator.addConfigurationFailure(LifecycleProjectEvaluator.java:72)
	at org.gradle.configuration.project.LifecycleProjectEvaluator.access$600(LifecycleProjectEvaluator.java:53)
	at org.gradle.configuration.project.LifecycleProjectEvaluator$EvaluateProject$1.run(LifecycleProjectEvaluator.java:108)
	at org.gradle.internal.Factories$1.create(Factories.java:25)
	at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:183)
	at org.gradle.internal.work.StopShieldingWorkerLeaseService.withLocks(StopShieldingWorkerLeaseService.java:40)
	at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.withProjectLock(DefaultProjectStateRegistry.java:226)
	at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.withMutableState(DefaultProjectStateRegistry.java:220)
	at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.withMutableState(DefaultProjectStateRegistry.java:186)
	at org.gradle.configuration.project.LifecycleProjectEvaluator$EvaluateProject.run(LifecycleProjectEvaluator.java:95)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:402)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:394)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$1.execute(DefaultBuildOperationExecutor.java:165)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:250)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:158)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:92)
	at org.gradle.internal.operations.DelegatingBuildOperationExecutor.run(DelegatingBuildOperationExecutor.java:31)
	at org.gradle.configuration.project.LifecycleProjectEvaluator.evaluate(LifecycleProjectEvaluator.java:67)
	at org.gradle.api.internal.project.DefaultProject.evaluate(DefaultProject.java:695)
	at org.gradle.api.internal.project.DefaultProject.evaluate(DefaultProject.java:143)
	at org.gradle.execution.TaskPathProjectEvaluator.configure(TaskPathProjectEvaluator.java:35)
	at org.gradle.execution.TaskPathProjectEvaluator.configureHierarchy(TaskPathProjectEvaluator.java:62)
	at org.gradle.configuration.DefaultBuildConfigurer.configure(DefaultBuildConfigurer.java:41)
	at org.gradle.initialization.DefaultGradleLauncher$ConfigureBuild.run(DefaultGradleLauncher.java:302)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:402)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:394)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$1.execute(DefaultBuildOperationExecutor.java:165)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:250)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:158)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:92)
	at org.gradle.internal.operations.DelegatingBuildOperationExecutor.run(DelegatingBuildOperationExecutor.java:31)
	at org.gradle.initialization.DefaultGradleLauncher.configureBuild(DefaultGradleLauncher.java:210)
	at org.gradle.initialization.DefaultGradleLauncher.doBuildStages(DefaultGradleLauncher.java:151)
	at org.gradle.initialization.DefaultGradleLauncher.getConfiguredBuild(DefaultGradleLauncher.java:129)
	at org.gradle.internal.invocation.GradleBuildController$2.execute(GradleBuildController.java:67)
	at org.gradle.internal.invocation.GradleBuildController$2.execute(GradleBuildController.java:64)
	at org.gradle.internal.invocation.GradleBuildController$3.create(GradleBuildController.java:82)
	at org.gradle.internal.invocation.GradleBuildController$3.create(GradleBuildController.java:75)
	at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:183)
	at org.gradle.internal.work.StopShieldingWorkerLeaseService.withLocks(StopShieldingWorkerLeaseService.java:40)
	at org.gradle.internal.invocation.GradleBuildController.doBuild(GradleBuildController.java:75)
	at org.gradle.internal.invocation.GradleBuildController.configure(GradleBuildController.java:64)
	at org.gradle.tooling.internal.provider.runner.ClientProvidedPhasedActionRunner.run(ClientProvidedPhasedActionRunner.java:62)
	at org.gradle.launcher.exec.ChainingBuildActionRunner.run(ChainingBuildActionRunner.java:35)
	at org.gradle.launcher.exec.ChainingBuildActionRunner.run(ChainingBuildActionRunner.java:35)
	at org.gradle.launcher.exec.BuildOutcomeReportingBuildActionRunner.run(BuildOutcomeReportingBuildActionRunner.java:58)
	at org.gradle.tooling.internal.provider.ValidatingBuildActionRunner.run(ValidatingBuildActionRunner.java:32)
	at org.gradle.launcher.exec.BuildCompletionNotifyingBuildActionRunner.run(BuildCompletionNotifyingBuildActionRunner.java:39)
	at org.gradle.launcher.exec.RunAsBuildOperationBuildActionRunner$3.call(RunAsBuildOperationBuildActionRunner.java:49)
	at org.gradle.launcher.exec.RunAsBuildOperationBuildActionRunner$3.call(RunAsBuildOperationBuildActionRunner.java:44)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:416)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:406)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$1.execute(DefaultBuildOperationExecutor.java:165)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:250)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:158)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:102)
	at org.gradle.internal.operations.DelegatingBuildOperationExecutor.call(DelegatingBuildOperationExecutor.java:36)
	at org.gradle.launcher.exec.RunAsBuildOperationBuildActionRunner.run(RunAsBuildOperationBuildActionRunner.java:44)
	at org.gradle.launcher.exec.InProcessBuildActionExecuter$1.transform(InProcessBuildActionExecuter.java:49)
	at org.gradle.launcher.exec.InProcessBuildActionExecuter$1.transform(InProcessBuildActionExecuter.java:46)
	at org.gradle.composite.internal.DefaultRootBuildState.run(DefaultRootBuildState.java:78)
	at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:46)
	at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:31)
	at org.gradle.launcher.exec.BuildTreeScopeBuildActionExecuter.execute(BuildTreeScopeBuildActionExecuter.java:42)
	at org.gradle.launcher.exec.BuildTreeScopeBuildActionExecuter.execute(BuildTreeScopeBuildActionExecuter.java:28)
	at org.gradle.tooling.internal.provider.ContinuousBuildActionExecuter.execute(ContinuousBuildActionExecuter.java:78)
	at org.gradle.tooling.internal.provider.ContinuousBuildActionExecuter.execute(ContinuousBuildActionExecuter.java:52)
	at org.gradle.tooling.internal.provider.SubscribableBuildActionExecuter.execute(SubscribableBuildActionExecuter.java:59)
	at org.gradle.tooling.internal.provider.SubscribableBuildActionExecuter.execute(SubscribableBuildActionExecuter.java:36)
	at org.gradle.tooling.internal.provider.SessionScopeBuildActionExecuter.execute(SessionScopeBuildActionExecuter.java:68)
	at org.gradle.tooling.internal.provider.SessionScopeBuildActionExecuter.execute(SessionScopeBuildActionExecuter.java:38)
	at org.gradle.tooling.internal.provider.GradleThreadBuildActionExecuter.execute(GradleThreadBuildActionExecuter.java:37)
	at org.gradle.tooling.internal.provider.GradleThreadBuildActionExecuter.execute(GradleThreadBuildActionExecuter.java:26)
	at org.gradle.tooling.internal.provider.ParallelismConfigurationBuildActionExecuter.execute(ParallelismConfigurationBuildActionExecuter.java:43)
	at org.gradle.tooling.internal.provider.ParallelismConfigurationBuildActionExecuter.execute(ParallelismConfigurationBuildActionExecuter.java:29)
	at org.gradle.tooling.internal.provider.StartParamsValidatingActionExecuter.execute(StartParamsValidatingActionExecuter.java:60)
	at org.gradle.tooling.internal.provider.StartParamsValidatingActionExecuter.execute(StartParamsValidatingActionExecuter.java:32)
	at org.gradle.tooling.internal.provider.SessionFailureReportingActionExecuter.execute(SessionFailureReportingActionExecuter.java:55)
	at org.gradle.tooling.internal.provider.SessionFailureReportingActionExecuter.execute(SessionFailureReportingActionExecuter.java:41)
	at org.gradle.tooling.internal.provider.SetupLoggingActionExecuter.execute(SetupLoggingActionExecuter.java:48)
	at org.gradle.tooling.internal.provider.SetupLoggingActionExecuter.execute(SetupLoggingActionExecuter.java:32)
	at org.gradle.launcher.daemon.server.exec.ExecuteBuild.doBuild(ExecuteBuild.java:67)
	at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:36)
	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
	at org.gradle.launcher.daemon.server.exec.WatchForDisconnection.execute(WatchForDisconnection.java:37)
	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
	at org.gradle.launcher.daemon.server.exec.ResetDeprecationLogger.execute(ResetDeprecationLogger.java:26)
	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
	at org.gradle.launcher.daemon.server.exec.RequestStopIfSingleUsedDaemon.execute(RequestStopIfSingleUsedDaemon.java:34)
	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
	at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.call(ForwardClientInput.java:74)
	at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.call(ForwardClientInput.java:72)
	at org.gradle.util.Swapper.swap(Swapper.java:38)
	at org.gradle.launcher.daemon.server.exec.ForwardClientInput.execute(ForwardClientInput.java:72)
	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
	at org.gradle.launcher.daemon.server.exec.LogAndCheckHealth.execute(LogAndCheckHealth.java:55)
	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
	at org.gradle.launcher.daemon.server.exec.LogToClient.doBuild(LogToClient.java:62)
	at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:36)
	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
	at org.gradle.launcher.daemon.server.exec.EstablishBuildEnvironment.doBuild(EstablishBuildEnvironment.java:81)
	at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:36)
	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
	at org.gradle.launcher.daemon.server.exec.StartBuildOrRespondWithBusy$1.run(StartBuildOrRespondWithBusy.java:50)
	at org.gradle.launcher.daemon.server.DaemonStateCoordinator$1.run(DaemonStateCoordinator.java:295)
	at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:63)
	at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:46)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:55)
	at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.IllegalStateException: compileSdkVersion is not specified.
	at com.google.common.base.Preconditions.checkState(Preconditions.java:507)
	at com.android.build.gradle.BasePlugin.createAndroidTasks(BasePlugin.java:693)
	at com.android.builder.profile.ThreadRecorder.record(ThreadRecorder.java:81)
	at com.android.build.gradle.BasePlugin.lambda$createTasks$4(BasePlugin.java:651)
	at com.android.build.gradle.internal.crash.CrashReporting$afterEvaluate$1.execute(crash_reporting.kt:37)
	at com.android.build.gradle.internal.crash.CrashReporting$afterEvaluate$1.execute(crash_reporting.kt)
	at org.gradle.configuration.internal.DefaultListenerBuildOperationDecorator$BuildOperationEmittingAction$1$1.run(DefaultListenerBuildOperationDecorator.java:150)
	at org.gradle.configuration.internal.DefaultUserCodeApplicationContext.reapply(DefaultUserCodeApplicationContext.java:58)
	at org.gradle.configuration.internal.DefaultListenerBuildOperationDecorator$BuildOperationEmittingAction$1.run(DefaultListenerBuildOperationDecorator.java:147)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:402)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:394)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$1.execute(DefaultBuildOperationExecutor.java:165)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:250)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:158)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:92)
	at org.gradle.configuration.internal.DefaultListenerBuildOperationDecorator$BuildOperationEmittingAction.execute(DefaultListenerBuildOperationDecorator.java:144)
	at org.gradle.internal.event.BroadcastDispatch$ActionInvocationHandler.dispatch(BroadcastDispatch.java:91)
	at org.gradle.internal.event.BroadcastDispatch$ActionInvocationHandler.dispatch(BroadcastDispatch.java:80)
	at org.gradle.internal.event.AbstractBroadcastDispatch.dispatch(AbstractBroadcastDispatch.java:42)
	at org.gradle.internal.event.BroadcastDispatch$SingletonDispatch.dispatch(BroadcastDispatch.java:230)
	at org.gradle.internal.event.BroadcastDispatch$SingletonDispatch.dispatch(BroadcastDispatch.java:149)
	at org.gradle.internal.event.AbstractBroadcastDispatch.dispatch(AbstractBroadcastDispatch.java:58)
	at org.gradle.internal.event.BroadcastDispatch$CompositeDispatch.dispatch(BroadcastDispatch.java:324)
	at org.gradle.internal.event.BroadcastDispatch$CompositeDispatch.dispatch(BroadcastDispatch.java:234)
	at org.gradle.internal.event.ListenerBroadcast.dispatch(ListenerBroadcast.java:140)
	at org.gradle.internal.event.ListenerBroadcast.dispatch(ListenerBroadcast.java:37)
	at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:93)
	at com.sun.proxy.$Proxy36.afterEvaluate(Unknown Source)
	at org.gradle.configuration.project.LifecycleProjectEvaluator$NotifyAfterEvaluate$1.execute(LifecycleProjectEvaluator.java:190)
	at org.gradle.configuration.project.LifecycleProjectEvaluator$NotifyAfterEvaluate$1.execute(LifecycleProjectEvaluator.java:187)
	at org.gradle.api.internal.project.DefaultProject.stepEvaluationListener(DefaultProject.java:1424)
	at org.gradle.configuration.project.LifecycleProjectEvaluator$NotifyAfterEvaluate.run(LifecycleProjectEvaluator.java:196)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:402)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:394)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$1.execute(DefaultBuildOperationExecutor.java:165)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:250)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:158)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:92)
	at org.gradle.internal.operations.DelegatingBuildOperationExecutor.run(DelegatingBuildOperationExecutor.java:31)
	at org.gradle.configuration.project.LifecycleProjectEvaluator$EvaluateProject$1.run(LifecycleProjectEvaluator.java:111)
	... 108 more

==============================================================================

* Get more help at https://help.gradle.org

I also noticed that the gradle files are much different. They are too long so I attached them.
Archive.zip

Note that the issue java.lang.RuntimeException: Unable to determine Android SDK directory. shouldn't happen; I checked my SDK directory and it was correctly configured.

@xubowenhaoren, upgrading to cordova-android@8.0.0 is not that easy because it introduces incompatibilities in the build process.

$ cordova platform update android@8.0.0

As I said during our conversation, I have upgraded the COVID-19 version (https://github.com/covid19database/phone-app) to the latest version, but not had the time to port the changes over to e-mission phone yet. I started porting yesterday, but just got a bunch of higher priority work on my plate today, so it probably won't get done until tonight/tomorrow morning.

If you want to manually make the changes there yourself, then feel free to look at the changes in that repo and copy them over!

Besides upgraded plugin versions in config.xml and package.json, what are the major changes that I need to make? It's pretty hard to track 1000+ commits.

most of the 1000+ commits are already from e-mission-phone since https://github.com/covid19database/phone-app started with e-mission as the base. You just need to find the new commits.

Last night, I created a fork of e-mission-phone and pulled all the changes from the phone-app https://github.com/covid19database/e-mission-phone
That should make it much easier for you to see the differences.

I just observed an unexpected behavior for using source setup/setup_android_native.sh in the COVID-19 version. If the user already has Gradle 4.1, then

Setting up gradle using SDKMan

Stop! gradle 4.1 is already installed.
Saving session...
...copying shared history...
...saving history...truncating history files...
...completed.

[Process completed]

The shell script does not progress forward and run source setup/setup_shared_native.sh. I am using macOS 10.15.3.

P.S. I know that this is not the right place to submit this "issue". However, I didn't find the issue tab in the COVID-19 version.

Since the CI is successful, this is a problem with your setup.

  • Did you look at the successful logs to see what the next successful step should be and try to run it in your environment?
  • Did you try running with set -e commented out?
  • Did you try running the setup script step by step to see what fails?

You can also delete the sdkman directory rm ~/.sdkman/ if you think that is the problem - the script will set it up again for you.

Commenting out set -e worked fine. I guess it's safe to say that it's not a bug but a feature to "exit on error" :)

I am happy to report that the combination of steps below results in a successful APK build, which includes my custom AWARE plugin using AndroidX.

  1. Run the commands:
cordova plugin add cordova-plugin-androidx
cordova plugin add cordova-plugin-androidx-adapter
cordorva plugin add /path/to/custom-aware-plugin/
  1. Then, open up platforms/android in Android Studio. This is important.

  2. OPTIONAL: In both the app level and the android level build.gradle, in both repositories, add

maven { url "https://jitpack.io" }

You only need this step if your custom plugin requires master-SNAPSHOT.

  1. In gradle.properties, check you have the following:
cdvMinSdkVersion=24
cdvTargetSdkVersion=28
android.useAndroidX=true
android.enableJetifier=true
  1. Then, build from the Android Studio, not from the command line. In my testing, this prevents Cordova changing your custom gradle.properties.

  2. If the build is successful, in your app-level build.gradle, you should only see AndroidX implementations and no legacy support ones. This is automatic. If you still see legacy packages, you did something wrong.

dependencies {
    implementation fileTree(dir: 'libs', include: '*.jar')
    // SUB-PROJECT DEPENDENCIES START
    implementation(project(path: ":CordovaLib"))
    implementation "androidx.legacy:legacy-support-v4:1.0.0"
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.71"
    implementation "androidx.annotation:annotation:1.0.0"
    implementation "com.google.android.gms:play-services-auth:11.0.1"
    implementation "net.openid:appauth:0.7.0"
    implementation "com.auth0.android:jwtdecode:1.1.1"
    implementation "com.google.code.gson:gson:2.8.5"
    implementation "com.google.android.gms:play-services-location:11.0.1"
    implementation "com.github.denzilferreira:aware-client:master-SNAPSHOT"
    // SUB-PROJECT DEPENDENCIES END
}

Thanks a lot for the help. I will try to update the COVID-19 README later.

Great!

Then, build from the Android Studio, not from the command line. In my testing, this prevents Cordova changing your custom gradle.properties.

Yes, cordova will recreate your android environment every time.

For the long-term, you should create a build hook for customizing the gradle. This build hook should either be installed by the plugin (preferred) or be included in the project (sub-optimal but will work).

Then you should be able to use the command line for everything, including enabling your own CI.
Note also that the changes to use the most recent versions + enable CI are also in a branch of e-mission-phone now.

Which is the more stable branch, the COVID-19 tracking or the e-mission upgrade_to_latest_cordova branch? I hope to stick to a reliable platform to develop and test the Aware plugin first.

Depends on what Berkeley chooses to do wrt apps going forward.
But the instability of the main repo should not have any effect on you.
You can freeze your fork to any version and only pull when necessary.

Question 1: I tried to add a custom Aware UI. I modified and added some js and HTML files, according to my understanding of the Angular framework here and your doc.

However, when I try directly building the app in Android Studio, it looked like my mods were not "imported". I then tried npx cordova build android. Although the build had failed, a subsequent build in Android Studio incorporated my mods.

What are the extra steps in npx cordova build android that re-import the resource files? If I continue to build using Android Studio, how can I re-import the resource files without running npx cordova build android (if this is possible at all)?

Question 2: Although I was able to build the APK, apparently my first attempt at creating a UI wasn't successful. Logs below:

2020-04-23 02:06:38.359 5062-5062/edu.berkeley.eecs.covid19 D/SystemWebChromeClient: http://localhost/lib/jquery/dist/jquery.min.js: Line 2 : Uncaught Error: [$injector:modulerr] Failed to instantiate module emission due to:
    Error: [$injector:modulerr] Failed to instantiate module emission.main due to:
    Error: [$injector:modulerr] Failed to instantiate module emission.main.aware due to:
    Error: [$injector:nomod] Module 'emission.main.aware' is not available! You either misspelled the module name or forgot to load it. If registering a module ensure that you specify the dependencies as the second argument.
    http://errors.angularjs.org/1.5.3/$injector/nomod?p0=emission.main.aware
        at http://localhost/lib/ionic/js/ionic.bundle.js:13438:12
        at http://localhost/lib/ionic/js/ionic.bundle.js:15404:17
        at ensure (http://localhost/lib/ionic/js/ionic.bundle.js:15328:38)
        at module (http://localhost/lib/ionic/js/ionic.bundle.js:15402:14)
        at http://localhost/lib/ionic/js/ionic.bundle.js:17894:22
        at forEach (http://localhost/lib/ionic/js/ionic.bundle.js:13691:20)
        at loadModules (http://localhost/lib/ionic/js/ionic.bundle.js:17878:5)
        at http://localhost/lib/ionic/js/ionic.bundle.js:17895:40
        at forEach (http://localhost/lib/ionic/js/ionic.bundle.js:13691:20)
        at loadModules (http://localhost/lib/ionic/js/ionic.bundle.js:17878:5)

Could you look into my commit and point out what I need to fix?

However, when I try directly building the app in Android Studio, it looked like my mods were not "imported". I then tried npx cordova build android. Although the build had failed, a subsequent build in Android Studio incorporated my mods.

The javascript changes are only incorporated into the project when you run npx cordova build. The javascript files are copied into multiple locations, and I haven't figured out which ones are used by the project when it runs. I test javascript changes by using npx cordova

You need to reproduce the changes that you needed to build using Android Studio into the cordova build process (#501 (comment)) so that you don't need to keep going back to Android Studio build. Using android studio is best when you are making native code changes.

Could you look into my commit and point out what I need to fix?

This is an angular issue, nothing to do with the e-mission UI or the e-mission plugins. Have you tried to post on stackoverflow with the error?

Could you look into my commit and point out what I need to fix?

This is an angular issue, nothing to do with the e-mission UI or the e-mission plugins. Have you tried to post on stackoverflow with the error?

Fixed - need to register my aware.js file in main.html. Should I add this to the e-mission doc?

I am a bit reluctant to duplicate angular information in the e-mission docs. I don't want people to think that they can file angular issues in the e-mission docs and expect me to answer them.

All the comments in the current doc are about e-mission specific functionality (e.g. consent, state detection, etc).

If angular decides to change the way in which js files are included, I don't want to duplicate that information.

I'd be open to having a note saying that people should lookup angular usage instructions with this as an example.

Per your previous suggestion here, I started working on automating the adaptations.

First, regarding the gradle.properties:

After you add the Cordova AndroidX adapters, namely

cordova plugin add cordova-plugin-androidx
cordova plugin add cordova-plugin-androidx-adapter

, and make sure that the two occurrences of <preference name="android-minSdkVersion" value="24" /> in config.cordovabuild.xml are set correctly, running npx cordova build android will give you the correct gradle.propertiesconfig.

Meanwhile, I also tried to automate the build.gradle customization. I followed the guide here.

Here is my current build-extras.gradle:

buildscript {
    repositories {
        maven { url "https://jitpack.io" }
    }
}
allprojects {
    repositories {
        maven { url "https://jitpack.io" }
    }
}

Here is a part of my plugin.xml that reflects this:

    <platform name="android">
        <framework src="com.github.denzilferreira:aware-client:master-SNAPSHOT" />
        <framework src="build-extras.gradle" custom="true" type="gradleReference" />
        <config-file parent="/*" target="res/xml/config.xml">
        ...

But when I try to run cordova plugin add, it prompts me that the file directory is wrong:

Error during processing of action! Attempting to revert...
Failed to install 'edu.berkeley.eecs.emission.cordova.aware': CordovaError: Uh oh!
"/Users/bowenxu2/Documents/GitHub/phone-app/plugins/edu.berkeley.eecs.emission.cordova.aware/build-extras.gradle" not found!
    at copyFile (/Users/bowenxu2/Documents/GitHub/phone-app/platforms/android/cordova/lib/pluginHandlers.js:208:36)
    at copyNewFile (/Users/bowenxu2/Documents/GitHub/phone-app/platforms/android/cordova/lib/pluginHandlers.js:236:5)
    at install (/Users/bowenxu2/Documents/GitHub/phone-app/platforms/android/cordova/lib/pluginHandlers.js:80:17)
    at ActionStack.process (/Users/bowenxu2/Documents/GitHub/phone-app/node_modules/cordova-common/src/ActionStack.js:56:25)
    at PluginManager.doOperation (/Users/bowenxu2/Documents/GitHub/phone-app/node_modules/cordova-common/src/PluginManager.js:114:20)
    at PluginManager.addPlugin (/Users/bowenxu2/Documents/GitHub/phone-app/node_modules/cordova-common/src/PluginManager.js:144:17)
    at /Users/bowenxu2/Documents/GitHub/phone-app/platforms/android/cordova/Api.js:210:74
    at _fulfilled (/Users/bowenxu2/Documents/GitHub/phone-app/node_modules/q/q.js:854:54)
    at /Users/bowenxu2/Documents/GitHub/phone-app/node_modules/q/q.js:883:30
    at Promise.promise.promiseDispatch (/Users/bowenxu2/Documents/GitHub/phone-app/node_modules/q/q.js:816:13)

Note: the actual path should be /Users/bowenxu2/Documents/GitHub/e-mission-aware-battery/build-extras.gradle.

when you add a plugin cordova copies it to your phone directory. Otherwise how would you be able to use plugins directly from Github. When you add your own plugin even if it is local, it copies it from /Users/bowenxu2/Documents/GitHub/e-mission-aware-battery to /Users/bowenxu2/Documents/GitHub/phone-app/plugins/edu.berkeley.eecs.emission.cordova.aware.

You need to remove and re-add the plugin.

Update on my previous comment, there is nothing wrong in my build-extras.gradle.

Taking a closer look at my plugin.xml,

    <platform name="android">
        <framework src="com.github.denzilferreira:aware-client:master-SNAPSHOT" />
        <framework src="build-extras.gradle" custom="true" type="gradleReference" />
        <config-file parent="/*" target="res/xml/config.xml">
        ...

Previously my build-extras.gradle is under the root directory of the project. Moving it to the same directory as the plugin.xml, and it would work.

This is a follow-up issue from the previous meeting, where my custom AwarePlugin's 'pluginInitialize' wasn't called.

I first checked the config.xml. Looks like by default onload wasn't set to true. So I edited it to match with other plugins:

    <feature name="AwarePlugin">
        <param name="android-package" value="edu.berkeley.eecs.emission.cordova.aware.AwarePlugin" />
        <param name="onload" value="true" />
    </feature>

Then a subsequent run resulted in the following error:

2020-04-28 00:51:21.831 15852-15852/edu.berkeley.eecs.covid19 E/AndroidRuntime: FATAL EXCEPTION: main
    Process: edu.berkeley.eecs.covid19, PID: 15852
    java.lang.RuntimeException: Unable to start activity ComponentInfo{edu.berkeley.eecs.covid19/edu.berkeley.eecs.covid19.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'void org.apache.cordova.CordovaPlugin.privateInitialize(java.lang.String, org.apache.cordova.CordovaInterface, org.apache.cordova.CordovaWebView, org.apache.cordova.CordovaPreferences)' on a null object reference
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3387)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3526)
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83)
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2122)
        at android.os.Handler.dispatchMessage(Handler.java:107)
        at android.os.Looper.loop(Looper.java:214)
        at android.app.ActivityThread.main(ActivityThread.java:7695)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:516)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:950)
     Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void org.apache.cordova.CordovaPlugin.privateInitialize(java.lang.String, org.apache.cordova.CordovaInterface, org.apache.cordova.CordovaWebView, org.apache.cordova.CordovaPreferences)' on a null object reference
        at org.apache.cordova.PluginManager.getPlugin(PluginManager.java:171)
        at org.apache.cordova.PluginManager.startupPlugins(PluginManager.java:97)
        at org.apache.cordova.PluginManager.init(PluginManager.java:86)
        at org.apache.cordova.CordovaWebViewImpl.init(CordovaWebViewImpl.java:117)
        at org.apache.cordova.CordovaActivity.init(CordovaActivity.java:149)
        at org.apache.cordova.CordovaActivity.loadUrl(CordovaActivity.java:224)
        at edu.berkeley.eecs.covid19.MainActivity.onCreate(MainActivity.java:39)
        at android.app.Activity.performCreate(Activity.java:7820)
        at android.app.Activity.performCreate(Activity.java:7809)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1318)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3362)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3526) 
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83) 
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135) 
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2122) 
        at android.os.Handler.dispatchMessage(Handler.java:107) 
        at android.os.Looper.loop(Looper.java:214) 
        at android.app.ActivityThread.main(ActivityThread.java:7695) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:516) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:950) 

Could you look at what I did wrong in the AwarePlugin? The repo is here.

well, the error is from here:
https://github.com/apache/cordova-android/blob/46a036ef265951d5371936b37d7296fa47fb28ce/framework/src/org/apache/cordova/PluginManager.java#L171

You could add breakpoints to that code and figure out why ret is null.

I tried building the app again, and this time, I found something else in the error log. While it still has the same exception as last time:

java.lang.RuntimeException: Unable to start activity ComponentInfo{edu.berkeley.eecs.covid19/edu.berkeley.eecs.covid19.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'void org.apache.cordova.CordovaPlugin.privateInitialize(java.lang.String, org.apache.cordova.CordovaInterface, org.apache.cordova.CordovaWebView, org.apache.cordova.CordovaPreferences)' on a null object reference

This time, logcat gave more details:

2020-05-02 11:11:05.599 18721-18721/edu.berkeley.eecs.covid19 W/System.err: java.lang.ClassNotFoundException: edu.berkeley.eecs.emission.cordova.aware.AwarePlugin
2020-05-02 11:11:05.599 18721-18721/edu.berkeley.eecs.covid19 W/System.err:     at java.lang.Class.classForName(Native Method)
2020-05-02 11:11:05.599 18721-18721/edu.berkeley.eecs.covid19 W/System.err:     at java.lang.Class.forName(Class.java:454)
2020-05-02 11:11:05.599 18721-18721/edu.berkeley.eecs.covid19 W/System.err:     at java.lang.Class.forName(Class.java:379)
2020-05-02 11:11:05.600 18721-18721/edu.berkeley.eecs.covid19 W/System.err:     at org.apache.cordova.PluginManager.instantiatePlugin(PluginManager.java:489)
2020-05-02 11:11:05.600 18721-18721/edu.berkeley.eecs.covid19 W/System.err:     at org.apache.cordova.PluginManager.getPlugin(PluginManager.java:169)
2020-05-02 11:11:05.600 18721-18721/edu.berkeley.eecs.covid19 W/System.err:     at org.apache.cordova.PluginManager.startupPlugins(PluginManager.java:97)
2020-05-02 11:11:05.600 18721-18721/edu.berkeley.eecs.covid19 W/System.err:     at org.apache.cordova.PluginManager.init(PluginManager.java:86)
2020-05-02 11:11:05.600 18721-18721/edu.berkeley.eecs.covid19 W/System.err:     at org.apache.cordova.CordovaWebViewImpl.init(CordovaWebViewImpl.java:117)
2020-05-02 11:11:05.600 18721-18721/edu.berkeley.eecs.covid19 W/System.err:     at org.apache.cordova.CordovaActivity.init(CordovaActivity.java:149)
2020-05-02 11:11:05.600 18721-18721/edu.berkeley.eecs.covid19 W/System.err:     at org.apache.cordova.CordovaActivity.loadUrl(CordovaActivity.java:224)
2020-05-02 11:11:05.600 18721-18721/edu.berkeley.eecs.covid19 W/System.err:     at edu.berkeley.eecs.covid19.MainActivity.onCreate(MainActivity.java:39)
2020-05-02 11:11:05.600 18721-18721/edu.berkeley.eecs.covid19 W/System.err:     at android.app.Activity.performCreate(Activity.java:7820)
2020-05-02 11:11:05.600 18721-18721/edu.berkeley.eecs.covid19 W/System.err:     at android.app.Activity.performCreate(Activity.java:7809)
2020-05-02 11:11:05.600 18721-18721/edu.berkeley.eecs.covid19 W/System.err:     at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1318)
2020-05-02 11:11:05.600 18721-18721/edu.berkeley.eecs.covid19 W/System.err:     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3362)
2020-05-02 11:11:05.600 18721-18721/edu.berkeley.eecs.covid19 W/System.err:     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3526)
2020-05-02 11:11:05.600 18721-18721/edu.berkeley.eecs.covid19 W/System.err:     at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83)
2020-05-02 11:11:05.600 18721-18721/edu.berkeley.eecs.covid19 W/System.err:     at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
2020-05-02 11:11:05.600 18721-18721/edu.berkeley.eecs.covid19 W/System.err:     at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
2020-05-02 11:11:05.600 18721-18721/edu.berkeley.eecs.covid19 W/System.err:     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2122)
2020-05-02 11:11:05.600 18721-18721/edu.berkeley.eecs.covid19 W/System.err:     at android.os.Handler.dispatchMessage(Handler.java:107)
2020-05-02 11:11:05.600 18721-18721/edu.berkeley.eecs.covid19 W/System.err:     at android.os.Looper.loop(Looper.java:214)
2020-05-02 11:11:05.600 18721-18721/edu.berkeley.eecs.covid19 W/System.err:     at android.app.ActivityThread.main(ActivityThread.java:7695)
2020-05-02 11:11:05.600 18721-18721/edu.berkeley.eecs.covid19 W/System.err:     at java.lang.reflect.Method.invoke(Native Method)
2020-05-02 11:11:05.600 18721-18721/edu.berkeley.eecs.covid19 W/System.err:     at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:516)
2020-05-02 11:11:05.600 18721-18721/edu.berkeley.eecs.covid19 W/System.err:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:950)
2020-05-02 11:11:05.600 18721-18721/edu.berkeley.eecs.covid19 W/System.err: Caused by: java.lang.ClassNotFoundException: Didn't find class "edu.berkeley.eecs.emission.cordova.aware.AwarePlugin" on path: DexPathList[[zip file "/data/app/edu.berkeley.eecs.covid19-pWvHiJpcPvWAX6vTkZbntw==/base.apk"],nativeLibraryDirectories=[/data/app/edu.berkeley.eecs.covid19-pWvHiJpcPvWAX6vTkZbntw==/lib/arm, /data/app/edu.berkeley.eecs.covid19-pWvHiJpcPvWAX6vTkZbntw==/base.apk!/lib/armeabi, /system/lib, /product/lib]]
2020-05-02 11:11:05.600 18721-18721/edu.berkeley.eecs.covid19 W/System.err:     at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:196)
2020-05-02 11:11:05.600 18721-18721/edu.berkeley.eecs.covid19 W/System.err:     at java.lang.ClassLoader.loadClass(ClassLoader.java:379)
2020-05-02 11:11:05.600 18721-18721/edu.berkeley.eecs.covid19 W/System.err:     at java.lang.ClassLoader.loadClass(ClassLoader.java:312)
2020-05-02 11:11:05.600 18721-18721/edu.berkeley.eecs.covid19 W/System.err: 	... 26 more

I looked up this ClassNotFoundException. This post suggests that the Cordova cannot find my plugin class. That is not the case for me; I can certainly see my plugin in Android Studio after npx cordova build android.

ok, well is the class name for your plugin exactly edu.berkeley.eecs.emission.cordova.aware.AwarePlugin?

Yes. You can see my up-to-date plugin.xml here.

that may well be the value in the plugin, but what about the actual .java file?

The issue: I had other mismatches in my plugin.xml and in my package statement in AwarePlugin.java

How we solved it: We corrected the mismatch. Android Studio is again very helpful showing the (mistakes of) target directory configuration. 

Hello,

I wish to add the following bools.xml to the e-mission app.

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <item name="standalone" type="bool">true</item>
    <item name="accessibility_access" type="bool">true</item>
</resources>

Following your data collection plugin example, I need:

  1. Include the bools.xml in /res/android/values
  2. Include the bools.xml in plugin.xml

@BowenXu I wrote the data collection code ~ 5 years ago. I would suggest that you try things and file issues if they don't work instead of asking me to pre-validate. Computers are better at validating than humans.

Just updated the comment above - that was the correct approach.

Hello,

The following error occurred while I tried to rebase the current workspace to e-mission-phone/tree/upgrade_to_latest_cordova:

$ npx cordova build android
Copying locales: Languages found -> fr,it
Copying locales: I found fr, I will now copy the files.
Copying locales: Copying /Users/bowenxu2/Documents/GitHub/phone-app/locales/fr/values-fr to /Users/bowenxu2/Documents/GitHub/phone-app/platforms/android/res
Copying locales: /Users/bowenxu2/Documents/GitHub/phone-app/platforms/android/res/values-frdoes not exist, I will create it.
Copying locales: /Users/bowenxu2/Documents/GitHub/phone-app/locales/fr/values-frcopied...
Copying locales: Copying /Users/bowenxu2/Documents/GitHub/phone-app/locales/fr/i18n/ to /Users/bowenxu2/Documents/GitHub/phone-app/www/i18n/
Copying locales: /Users/bowenxu2/Documents/GitHub/phone-app/locales/fr/i18n/copied...
Copying locales: I found it, I will now copy the files.
Copying locales: Copying /Users/bowenxu2/Documents/GitHub/phone-app/locales/it/values-it to /Users/bowenxu2/Documents/GitHub/phone-app/platforms/android/res
Copying locales: /Users/bowenxu2/Documents/GitHub/phone-app/platforms/android/res/values-itdoes not exist, I will create it.
Copying locales: /Users/bowenxu2/Documents/GitHub/phone-app/locales/it/values-itcopied...
Copying locales: Copying /Users/bowenxu2/Documents/GitHub/phone-app/locales/it/i18n/ to /Users/bowenxu2/Documents/GitHub/phone-app/www/i18n/
Copying locales: /Users/bowenxu2/Documents/GitHub/phone-app/locales/it/i18n/copied...
Changing Providers: Retrieving application name...
Changing Providers: Your application is edu.berkeley.eecs.covid19
Changing Providers: Updating AndroidManifest.xml...
Changing Providers: Updating syncadapter.xml
Changing Providers: Updating authenticator.xml
Changing Providers: Updating ServerSyncPlugin.java
Changing Providers: Updating android.json
Running command: /Users/bowenxu2/Documents/GitHub/phone-app/hooks/before_prepare/download_translation.js /Users/bowenxu2/Documents/GitHub/phone-app
Changing Providers: /Users/bowenxu2/Documents/GitHub/phone-app/platforms/android/android.json updated...
/usr/bin/git
Downloading locales: /Users/bowenxu2/Documents/GitHub/phone-app/bin/conf/translate_config.json not found, I will extract translate repo from translation_config.json.sample.
Already up to date.
Generating initial manifest for Ionic Deploy...
There was an error generating the initial manifest of files for the deploy plugin.
Ionic Deploy initial manifest successfully generated.
Error: This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). The promise rejected with the reason "undefined".

I thought this was an issue related to that particular branch, so I cleaned up the workspace and switch back to covid19database/phone-app branch. The same error persists.

This may be because you do not have an ionic account. If you don't need the ionic deploy feature, you can try removing cordova-plugin-ionic and see if that fixes the problem.
Note that the covid19database/phone-app fork does not have this plugin
https://github.com/covid19database/phone-app/blob/master/config.cordovabuild.xml

Yes, we confirmed that removing the cordova-plugin-ionic fixed the issue.

Now, we face a new issue with building:

Error: /Users/bowenxu2/Documents/GitHub/phone-app/platforms/android/gradlew: Command failed with exit code 1 Error output:
FAILURE: Build failed with an exception.

* What went wrong:
Could not determine the dependencies of task ':app:preDebugBuild'.
> In project 'app' a resolved Google Play services library dependency depends on another at an exact version (e.g. "[11.0.
  1]", but isn't being resolved to that version. Behavior exhibited by the library will be unknown.
  
  Dependency failing: com.google.firebase:firebase-iid:11.0.1 -> com.google.android.gms:play-services-basement@[11.0.1], b
  ut play-services-basement version was 17.0.0.
  
  The following dependencies are project dependencies that are direct or have transitive dependencies that lead to the art
  ifact with the issue.
  -- Project 'app' depends onto com.google.android.gms:play-services-location@17.0.0
  -- Project 'app' depends onto com.google.android.gms:play-services-tasks@17.0.0
  -- Project 'app' depends onto com.google.android.gms:play-services-auth-api-phone@17.0.0
  -- Project 'app' depends onto com.google.android.gms:play-services-maps@17.0.0
  -- Project 'app' depends onto com.google.android.gms:play-services-base@17.0.0
  -- Project 'app' depends onto com.github.denzilferreira.aware-client:com.aware.plugin.google.fused_location@master-SNAPS
  HOT
  -- Project 'app' depends onto com.google.android.gms:play-services-auth@11.0.1
  -- Project 'app' depends onto com.google.android.gms:play-services-auth@17.0.0
  -- Project 'app' depends onto com.github.denzilferreira.aware-client:com.aware.plugin.openweather@master-SNAPSHOT
  -- Project 'app' depends onto com.github.denzilferreira:aware-client@master-SNAPSHOT
  -- Project 'app' depends onto com.github.denzilferreira.aware-client:com.aware.plugin.google.auth@master-SNAPSHOT
  -- Project 'app' depends onto com.google.firebase:firebase-messaging@11.0.1
  -- Project 'app' depends onto com.google.firebase:firebase-iid@11.0.1
  -- Project 'app' depends onto com.github.denzilferreira.aware-client:com.aware.plugin.google.activity_recognition@master
  -SNAPSHOT
  -- Project 'app' depends onto com.google.android.gms:play-services-basement@17.0.0
  -- Project 'app' depends onto com.google.firebase:firebase-common@11.0.1
  -- Project 'app' depends onto com.google.android.gms:play-services-places-placereport@17.0.0
  -- Project 'app' depends onto com.google.android.gms:play-services-location@11.0.1
  -- Project 'app' depends onto com.google.android.gms:play-services-auth-base@17.0.0
  
  For extended debugging info execute Gradle from the command line with ./gradlew --info :app:assembleDebug to see the dep
  endency paths to the artifact. This error message came from the google-services Gradle plugin, report issues at https://
  github.com/google/play-services-plugins and disable by adding "googleServices { disableVersionCheck = false }" to your b
  uild.gradle file.

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 39s

I am not sure why this was not an issue in the covid19database/phone-app since it included both:

    <framework src="com.google.android.gms:play-services-location:$LOCATION_VERSION"/>
    <preference name="LOCATION_VERSION" default="11.0.1"/>

Given that the top line error relates to firebase and the covid19 app does not have push notifications enabled, you could try removing the phonegap-plugin-push plugin as well to see if that helps.

  Dependency failing: com.google.firebase:firebase-iid:11.0.1 -> com.google.android.gms:play-services-basement@[11.0.1], but play-services-basement version was 17.0.0.

After removing the phonegap-plugin-push plugin, the conflict was gone and the build was successful.

We spotted an error with SQLiteDatabase, but we don't think it's related to either AWARE or e-mission.

2020-05-22 15:07:56.527 3101-19511/? E/SQLiteDatabase: Error inserting flex_time=1601000 job_id=-1 period=3203000 source=16 requires_charging=0 preferred_network_type=1 target_class=com.google.android.gms.measurement.PackageMeasurementTaskService user_id=0 target_package=com.google.android.gms tag=Measurement.PackageMeasurementTaskService.UPLOAD_TASK_TAG task_type=0 required_idleness_state=0 service_kind=0 source_version=201516000 preferred_charging_state=1 required_network_type=0 runtime=1590185276518 retry_strategy={"maximum_backoff_seconds":{"3600":0},"initial_backoff_seconds":{"30":0},"retry_policy":{"0":0}} last_runtime=0 from {P:3101;U:10211}
    android.database.sqlite.SQLiteConstraintException: UNIQUE constraint failed: pending_ops.tag, pending_ops.target_class, pending_ops.target_package, pending_ops.user_id (code 2067 SQLITE_CONSTRAINT_UNIQUE)
        at android.database.sqlite.SQLiteConnection.nativeExecuteForLastInsertedRowId(Native Method)
        at android.database.sqlite.SQLiteConnection.executeForLastInsertedRowId(SQLiteConnection.java:879)
        at android.database.sqlite.SQLiteSession.executeForLastInsertedRowId(SQLiteSession.java:790)
        at android.database.sqlite.SQLiteStatement.executeInsert(SQLiteStatement.java:88)
        at android.database.sqlite.SQLiteDatabase.insertWithOnConflict(SQLiteDatabase.java:1639)
        at android.database.sqlite.SQLiteDatabase.insert(SQLiteDatabase.java:1494)
        at aplm.a(:com.google.android.gms@201516037@20.15.16 (120400-309763488):76)
        at aplb.a(:com.google.android.gms@201516037@20.15.16 (120400-309763488):173)
        at aplb.a(:com.google.android.gms@201516037@20.15.16 (120400-309763488):21)
        at aplb.a(:com.google.android.gms@201516037@20.15.16 (120400-309763488):167)
        at aphk.run(:com.google.android.gms@201516037@20.15.16 (120400-309763488):8)
        at sob.b(:com.google.android.gms@201516037@20.15.16 (120400-309763488):12)
        at sob.run(:com.google.android.gms@201516037@20.15.16 (120400-309763488):7)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
        at sub.run(:com.google.android.gms@201516037@20.15.16 (120400-309763488):0)
        at java.lang.Thread.run(Thread.java:919)
2020-05-22 15:07:58.525 3101-19511/? E/SQLiteDatabase: Error inserting flex_time=1599000 job_id=-1 period=3199000 source=16 requires_charging=0 preferred_network_type=1 target_class=com.google.android.gms.measurement.PackageMeasurementTaskService user_id=0 target_package=com.google.android.gms tag=Measurement.PackageMeasurementTaskService.UPLOAD_TASK_TAG task_type=0 required_idleness_state=0 service_kind=0 source_version=201516000 preferred_charging_state=1 required_network_type=0 runtime=1590185278517 retry_strategy={"maximum_backoff_seconds":{"3600":0},"initial_backoff_seconds":{"30":0},"retry_policy":{"0":0}} last_runtime=0 from {P:3101;U:10211}
    android.database.sqlite.SQLiteConstraintException: UNIQUE constraint failed: pending_ops.tag, pending_ops.target_class, pending_ops.target_package, pending_ops.user_id (code 2067 SQLITE_CONSTRAINT_UNIQUE)
        at android.database.sqlite.SQLiteConnection.nativeExecuteForLastInsertedRowId(Native Method)
        at android.database.sqlite.SQLiteConnection.executeForLastInsertedRowId(SQLiteConnection.java:879)
        at android.database.sqlite.SQLiteSession.executeForLastInsertedRowId(SQLiteSession.java:790)
        at android.database.sqlite.SQLiteStatement.executeInsert(SQLiteStatement.java:88)
        at android.database.sqlite.SQLiteDatabase.insertWithOnConflict(SQLiteDatabase.java:1639)
        at android.database.sqlite.SQLiteDatabase.insert(SQLiteDatabase.java:1494)
        at aplm.a(:com.google.android.gms@201516037@20.15.16 (120400-309763488):76)
        at aplb.a(:com.google.android.gms@201516037@20.15.16 (120400-309763488):173)
        at aplb.a(:com.google.android.gms@201516037@20.15.16 (120400-309763488):21)
        at aplb.a(:com.google.android.gms@201516037@20.15.16 (120400-309763488):167)
        at aphk.run(:com.google.android.gms@201516037@20.15.16 (120400-309763488):8)
        at sob.b(:com.google.android.gms@201516037@20.15.16 (120400-309763488):12)
        at sob.run(:com.google.android.gms@201516037@20.15.16 (120400-309763488):7)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
        at sub.run(:com.google.android.gms@201516037@20.15.16 (120400-309763488):0)
        at java.lang.Thread.run(Thread.java:919)
2020-05-22 15:08:05.255 3101-15486/? E/SQLiteDatabase: Error inserting flex_time=1592000 job_id=-1 period=3186000 source=16 requires_charging=0 preferred_network_type=1 target_class=com.google.android.gms.measurement.PackageMeasurementTaskService user_id=0 target_package=com.google.android.gms tag=Measurement.PackageMeasurementTaskService.UPLOAD_TASK_TAG task_type=0 required_idleness_state=0 service_kind=0 source_version=201516000 preferred_charging_state=1 required_network_type=0 runtime=1590185285240 retry_strategy={"maximum_backoff_seconds":{"3600":0},"initial_backoff_seconds":{"30":0},"retry_policy":{"0":0}} last_runtime=0 from {P:3101;U:10211}
    android.database.sqlite.SQLiteConstraintException: UNIQUE constraint failed: pending_ops.tag, pending_ops.target_class, pending_ops.target_package, pending_ops.user_id (code 2067 SQLITE_CONSTRAINT_UNIQUE)
        at android.database.sqlite.SQLiteConnection.nativeExecuteForLastInsertedRowId(Native Method)
        at android.database.sqlite.SQLiteConnection.executeForLastInsertedRowId(SQLiteConnection.java:879)
        at android.database.sqlite.SQLiteSession.executeForLastInsertedRowId(SQLiteSession.java:790)
        at android.database.sqlite.SQLiteStatement.executeInsert(SQLiteStatement.java:88)
        at android.database.sqlite.SQLiteDatabase.insertWithOnConflict(SQLiteDatabase.java:1639)
        at android.database.sqlite.SQLiteDatabase.insert(SQLiteDatabase.java:1494)
        at aplm.a(:com.google.android.gms@201516037@20.15.16 (120400-309763488):76)
        at aplb.a(:com.google.android.gms@201516037@20.15.16 (120400-309763488):173)
        at aplb.a(:com.google.android.gms@201516037@20.15.16 (120400-309763488):21)
        at aplb.a(:com.google.android.gms@201516037@20.15.16 (120400-309763488):167)
        at aphk.run(:com.google.android.gms@201516037@20.15.16 (120400-309763488):8)
        at sob.b(:com.google.android.gms@201516037@20.15.16 (120400-309763488):12)
        at sob.run(:com.google.android.gms@201516037@20.15.16 (120400-309763488):7)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
        at sub.run(:com.google.android.gms@201516037@20.15.16 (120400-309763488):0)
        at java.lang.Thread.run(Thread.java:919)

There is a prior report of a crash in the Firebase job scheduler
googlearchive/firebase-jobdispatcher-android#94

This caused scheduled jobs to be inconsistently started.

In this case, the error is from PackageMeasurementTaskService. Is that used by AWARE?
If so, does it catch the error, or does it crash? Can we put a breakpoint around there and see how it is dealing with this crash and whether it is the root cause of the infinite loop?

We found out that in our current runtime "permission requesting", some of the permissions were denied (as -1) and the app ended up in an infinite loop:

2020-05-22 15:57:36.678 12027-12027/edu.berkeley.eecs.emission D/BOWEN: permissions[android.permission.ACCESS_COARSE_LOCATION, android.permission.ACCESS_FINE_LOCATION, android.permission.WRITE_EXTERNAL_STORAGE, android.permission.ACCESS_NETWORK_STATE, android.permission.READ_CALL_LOG, android.permission.READ_CONTACTS, android.permission.READ_SMS, android.permission.READ_PHONE_STATE, android.permission.BLUETOOTH, android.permission.BLUETOOTH_ADMIN, android.permission.GET_ACCOUNTS, android.permission.WRITE_SYNC_SETTINGS, android.permission.READ_SYNC_SETTINGS, android.permission.READ_SYNC_STATS, android.permission.CHANGE_WIFI_STATE, android.permission.ACCESS_WIFI_STATE]
2020-05-22 15:57:36.678 12027-12027/edu.berkeley.eecs.emission D/BOWEN: grantResults[0, 0, 0, 0, -1, 0, -1, 0, 0, 0, 0, 0, 0, 0, -1, -1]

Denied permissions include:

Manifest.permission.READ_CALL_LOG,
Manifest.permission.READ_SMS,
Manifest.permission.CHANGE_WIFI_STATE,
Manifest.permission.ACCESS_WIFI_STATE,

So at least for READ_CALL_LOG and READ_SMS, these are "hard restricted permissions" which require a whitelist.

This is a hard restricted permission which cannot be held by an app until the installer on record whitelists the permission. For more details see PackageInstaller.SessionParams.setWhitelistedRestrictedPermissions(Set).

That is probably why the permission doesn't even show up for the app. Just curious, is it showing up for the AWARE app?

Those permissions were included in one native AWARE client and was working in 2019. However, since we don't plan to use the call logging feature this time, we don't need to include those permissions.

I contacted the developer of the AWARE framework last week about the syncing issue. See the discussion here. I have followed his advice for adding the XML files, but that didn't resolve the issue.

@xubowenhaoren e-mission also uses a sync adapter, and I know for a fact that it works. You could see how that syncadapter is configured and deal with these additional sync adapters similarly.

Thanks for the note. I found one of your examples here.

This is AWARE's AndroidManifest.xml.They are using a special android:networkSecurityConfig="@xml/aware_ssl_config".

Here is the aware_ssl_config: https://github.com/denzilferreira/aware-client/blob/c15c72c98add7e1193d601633505dd40ce45d9f7/aware-core/src/main/res/xml/aware_ssl_config.xml

so it looks like the main component here is cleartextTrafficPermitted="true" e-mission works fine without it. This may be because I am using the legacy HTTP library (aha! advantage of not updating!)

C02KT61MFFT0:e-mission-phone shankari$ grep -r cleartextTrafficPermitted platforms/android/ | wc -l
0

Having said that, are you running your AWARE server over http or https?

Also what exactly is the problem with putting it under application. Everything else is under application as well...

so it looks like the current issue is that the ACTION_AWARE_SYNC_DATA broadcast is sent but doesn't appear to be received. I think we need to have a receiver registered for the intent, otherwise, the intent will not be delivered. However, there is no receiver registered in either the AWARE AndroidManifest.xml or the old UW AndroidManifest.xml. For local broadcast notifications, we don't to register the receiver, but we do for the version that may need to launch the process.

Since we can put a breakpoint just before the code that generates the broadcast, that must be in the plugin code. @xubowenhaoren is that correct?

How does regular aware broadcast that intent? Is it different from the way that you send it from the plugin?

ah wait, so if you know that the receiver will be in the foreground, you can register the receiver in code. This is similar to the LocalBroadcastManager but not explicitly local (e.g. https://stackoverflow.com/a/7276808/4040267)

In the core aware code, this seems to happen here:
https://github.com/denzilferreira/aware-client/blob/1e49fa56b32b112066848038e0be80467ad36294/aware-core/src/main/java/com/aware/utils/Aware_Plugin.java#L79

Are you doing that in your plugin?

That is not the only location - there are a few others as well. You may want to add logging for all of them and figure out which ones the regular AWARE client calls. You then need to make the corresponding call from your plugin. Once the ContentProvider is registered properly, everything else should work.

https://github.com/denzilferreira/aware-client/search?q=new+ContextBroadcaster&unscoped_q=new+ContextBroadcaster

Since you can't put breakpoints in the AWARE code, if you want to debug what AWARE is doing under the hood, you could fork the https://github.com/denzilferreira/aware-client and change the jitpack.io line to build from your fork rather than AWARE core. then you can add a bunch of logcats and see whether the receivers are registered or not.