Edit, 18.04.2016: Edited to add note about the fixing of this issue in AMPA 12.2.1.0.68.

This post will detaliate and explain how we fixed a rather strange issue that we encountered in a MAF application that we were developing.

First, some context about the environment that we were working in.

We were building a time booking application using Oracle MAF 12.1.3. For persistency we used AMPA 12.1.3.2.32.

[dt_quote type=”blockquote” font_size=”big” animation=”none” background=”plain”]Note: The issue in the current version of AMPA is fixed, in version 12.2.1.0.68.  The framework does not send this analytics events automatically anymore. Furthermore, there is also a switch to add or to ignore location data from the payload of the events.[/dt_quote] [dt_quote type=”blockquote” font_size=”big” animation=”none” background=”plain”]AMPA will soon be renamed to CDM and will be fully supported by Oracle as a product in a feature release later this year. Read the blog post announcing this here [/dt_quote]

All was going well, except that, from time to time, we kept on getting some “app freezes” for a pretty fixed time when we switched from one feature to another. There was apparently not a well-defined moment when this was reproduced so we had a look into the logs to get a grasp of what was going on.

First thing that caught our attention was that the moment when the app freezed was preceded by a thrown exception whose message was “Cannot determine current location:”. Of course, we did not have our location services ON because our app did not require that. But why was the application trying to retrieve the position of the device?

We immediately saw from the error that this has to do with MCS (Mobile Cloud Service) and since we don’t use it in this particular case, the logical assumption was that AMPA was using it. We just needed to detect when and why. Since AMPA’s source is openly available we headed to the source code to find more.

A simple search by the text of the exception message was enough to lead us to a method called getLocation in a class called MCSManager.

The code of the method is simple and is shown below:

  protected Location getLocation()
  {
    Location location = null;
    DeviceManager dmgr = DeviceManagerFactory.getDeviceManager();
    //        if (dmgr.getOs().equalsIgnoreCase("ios")){
    try
    {
      // increase max age and disable high accuracy so position gets computed much faster
      // for example, in buildings with large wifi networks (like oracle offices)
      //           this.currentLocation = dmgr.getCurrentPosition(6000, true);
      location = dmgr.getCurrentPosition(600000, false);
    }
    catch (Exception e)
    {
      // cur pos not available, van happen in Android simulator
      sLog.severe("Cannot determine current location: " + e.getLocalizedMessage());
    }
    return location;
  }

We can see that it tries to get the position of the device and it also has a timer in miliseconds. Because our devices do not have location services enabled, this timer will be eventually consumed and the error will be thrown. The time specified in this method call as a parameter looks just about the same interval that our app is freezed.

All makes sense here, but where is this called and what is the connection to our “feature switch” event?

By tracing the method calls, we learned that on each feature change, in the “activate()” method of the LifeCycleListener, AMPA is building an event that the feature was changed. In the payload of this event, there are various properties sent about the device (os, model, network connection, manufacturer, etc.). Among these properties which are sent, there is also the location of the device which is obtained by using the above discussed method, getLocation. The snipped of the code responsible for this is shown below:

public Map<String, Object> getSessionContext(){
//...Other properties added here...
    Location loc = getLocation();
    if (loc != null)
    {
      // lat/lng must be sent as string, otherwise error returned!
      props.put("longitude", loc.getLongitude() + "");
      props.put("latitude", loc.getLatitude() + "");
    }
//...
}
[dt_quote type=”blockquote” font_size=”big” animation=”none” background=”plain”]You can further inspect this method by looking in the class oracle.ateam.sample.mobile.util.MCSManager[/dt_quote]

Since we do not use MCS for our application, we don’t really need this MCSManager initialization here. So we have to find a way to bypass it so our application would not stall anymore.

By default, AMPA will create its own Application Lifecycle listener. This is oracle.ateam.sample.mobile.lifecycle.InitDBLifeCycleListener. We can see that the offending code is found in activate() and deactivate() methods of the class.

  public void activate()
  {
    MCSManager.getInstance().startSession();
  }
  public void deactivate()
  {
    MCSManager.getInstance().endSession();
    DBConnectionFactory.closeConnectionIfNeeded();
    TaskExecutor.shutDown();
  }

Specifically, we would like to get rid of the MCSManager.getInstance().startSession() and MCSManager.getInstance().endSession() method calls without affecting other behavior of the application.

For this, we need to create our own class for Lifecycle listener and extend from this, then override the methods activate() and deactivate().

We name our class LifeCycleListenerImpl and configure it so that AMPA knows about it. This is done in maf-application.xml on the application tab like shown in the screenshot (click on the picture to enlarge).

lifecycle

For the activate method(), we just need to create the method and let it empty, without calling super().

For the deactivate, we can see that there is also some other functionality besides our not-desired code. So we can simply copy the code that we need and leave the offending one out.

public void activate(){
//Empty. Do not call super()
}
  public void deactivate()
  {
    DBConnectionFactory.closeConnectionIfNeeded();
    TaskExecutor.shutDown();
  }

This solved our problem and we got no more app freezes.