To use any of the interfaces specified on the Common Interfaces page you will need to instantiate the manager that implements the interface. In the sections that follow you will find a sequence diagram that is an overview of the events that happen when instantiating a manager. The diagram has been provided to give context to the developer. There is also sample code that you will need to implement in your app as well as some examples of how emulate the implementations for testing.
The example code is shown in four parts
Manager Loader Stub is only the manager loader methods with no executable code. The stub is intended to be used for compilation only.
Loader and manager interfaces to access FCA Car Services. The interfaces are specified on the Common Interface page. Only the IVehicleHVACManager and IVehicleStatusManager interfaces are used in this example.
Loader and manager implementation are mock managers to be used for testing. On an actual HU the implementation will be done by the HU supplier. The example shown here is provided as a simple way to simulate the implementation.
Android Test App implements the managers and provides a UI for testing.
An overview of the events that happen when instantiating a manager. Note, the getManager() call should be performed after the onCreate() method.
The stub code is only used to allow an app to compile. The actual Manager Loader will be provided dynamically by the firmware or a developer when testing.
package fca.loader;
public class ManagerLoader extends java.lang.Object implements fca.loader.ILoader {
public ManagerLoader() {
throw new RuntimeException("Stub");
}
@Override
public boolean getManager(String arg0, ILoaderCallback arg1) {
throw new RuntimeException("Stub");
}
}
The manager interfaces are the same interfaces contained in the FCA Car Service. They were included only to have a small set of sample source code to review.
The ILoader interface has been modified to allow use by a java app. When we add the Android app the interface will be the same as the production interface.
package fca.loader;
public abstract interface ILoader
{
public static abstract interface ILoaderCallback
{
public abstract void onManagerFailure(String paramString);
public abstract void onManagerReady(String paramString, Object paramObject);
}
// Context for Android only, public abstract boolean getManager(String paramString,
// ILoaderCallback paramILoaderCallback, Context paramContext);
public abstract boolean getManager(String paramString, ILoaderCallback paramILoaderCallback);
}
Loader and Manager Implementation
The loader and manager implementations are mock managers to be used for testing. On an actual HU the implementation will be done by the HU supplier. The example shown here is provided as a simple way to simulate the implementation.
Loader and Manager Implementation
On a separate thread instantiates the requested manager based on the interface class name.
package fca.loader;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import fca.managers.IVehicleHVACManager;
import fca.managers.IVehicleStatusManager;
import fca.managers.impl.VehicleHVACManager;
import fca.managers.impl.VehicleStatusManager;
public class ManagerLoader implements ILoader
{
@Override
public boolean getManager(final String paramString, final ILoader.ILoaderCallback paramILoaderCallback)
{
// Sleep to simulate getting Android services.
try
{
Thread.sleep(500);
}
catch (final InterruptedException e)
{
e.printStackTrace();
}
if (paramString.equals(IVehicleHVACManager.class.getName()))
{
final ExecutorService executor = Executors.newSingleThreadExecutor();
executor.submit(() -> {
paramILoaderCallback.onManagerReady(IVehicleHVACManager.class.getName(), new VehicleHVACManager());
});
}
else if (paramString.equals(IVehicleStatusManager.class.getName()))
{
final ExecutorService executor = Executors.newSingleThreadExecutor();
executor.submit(() -> {
paramILoaderCallback.onManagerReady(IVehicleStatusManager.class.getName(), new VehicleStatusManager());
});
}
return true;
}
}
In this example a thread to change the FT_DRV_ATC_TEMP is started when the app registers for the listener. This code can be replaced when using an actual simulator.
For display purposes the unused methods have been removed.
package fca.managers.impl;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import fca.PropertyNotSupported;
import fca.managers.IVehicleHVACManager;
public class VehicleHVACManager implements IVehicleHVACManager
{
private int vehicleTemp = 70;
private FcaIVehicleHVACManagerCallback mFcaIVehicleHVACManagerCallback = null;
@Override
public int getFT_DRV_ATC_TEMP() throws PropertyNotSupported
{
return this.vehicleTemp;
}
@Override
public String getManagerVersion()
{
return "1.0";
}
@Override
public boolean registerFT_DRV_ATC_TEMPChange(final FcaIVehicleHVACManagerCallback callback)
throws PropertyNotSupported
{
this.mFcaIVehicleHVACManagerCallback = callback;
final ExecutorService executor = Executors.newSingleThreadExecutor();
for (int x = 0; x < 10; x++)
{
executor.submit(() -> {
this.vehicleTemp++;
this.mFcaIVehicleHVACManagerCallback.onFT_DRV_ATC_TEMPChange(this.vehicleTemp);
try
{
Thread.sleep(100);
}
catch (final InterruptedException e)
{
System.out.println("Sleep failed " + e.getMessage());
}
});
}
return true;
}
}
In this example a thread to change the Speedometer is started when the app registers for the listener. This code can be replaced when using an actual simulator.
For display purposes the unused methods have been removed.
package fca.managers.impl;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import fca.PropertyNotSupported;
import fca.managers.IVehicleStatusManager;
public class VehicleStatusManager implements IVehicleStatusManager
{
private float vehicleSpeed = 20;
private FcaIVehicleStatusManagerCallback mFcaIVehicleStatusManagerCallback = null;
@Override
public String getManagerVersion()
{
return "1.0";
}
@Override
public float getSpeedometer() throws PropertyNotSupported
{
return this.vehicleSpeed;
}
@Override
public boolean registerSpeedometerChange(final FcaIVehicleStatusManagerCallback callback)
throws PropertyNotSupported
{
this.mFcaIVehicleStatusManagerCallback = callback;
final ExecutorService executor = Executors.newSingleThreadExecutor();
for (int x = 0; x < 30; x++)
{
executor.submit(() -> {
this.vehicleSpeed++;
this.mFcaIVehicleStatusManagerCallback.onSpeedometerChange(this.vehicleSpeed);
try
{
Thread.sleep(100);
}
catch (final InterruptedException e)
{
System.out.println("Sleep failed " + e.getMessage());
}
});
}
return true;
}
}
Loader Manager Test Android App
The Android test app:
TestLoader.java
The Test Loader has been implemented as an Android Activity where the getManager calls are button presses to assist in testing.
package com.example.test.loadermanagertestapp;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
import fca.PropertyNotSupported;
import fca.loader.ILoader;
import fca.loader.ManagerLoader;
import fca.managers.IVehicleHVACManager;
import fca.managers.IVehicleStatusManager;
public class TestLoader extends AppCompatActivity implements ILoader.ILoaderCallback, IVehicleHVACManager.FcaIVehicleHVACManagerCallback,
IVehicleStatusManager.FcaIVehicleStatusManagerCallback {
TextView textViewTemp;
TextView textViewSpeed;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.test_loader);
textViewTemp = findViewById(R.id.text_view_temp);
textViewSpeed = findViewById(R.id.text_view_speed);
}
private IVehicleHVACManager mVehicleHVACManager = null;
private IVehicleStatusManager mVehicleStatusManager = null;
@Override
public void onManagerFailure(final String managerName) {
System.out.println("Manager failure " + managerName);
}
@Override
public void onManagerReady(final String managerName, final Object o) {
if (managerName.equals(IVehicleHVACManager.class.getName())) {
this.mVehicleHVACManager = (IVehicleHVACManager) o;
try {
if (IVehicleHVACManager.version.equalsIgnoreCase(this.mVehicleHVACManager.getManagerVersion())) {
System.out.println("FT_DRV_ATC_TEMP " + this.mVehicleHVACManager.getFT_DRV_ATC_TEMP());
this.mVehicleHVACManager.registerFT_DRV_ATC_TEMPChange(this);
} else {
System.out.println("Manager version " + this.mVehicleHVACManager.getManagerVersion()
+ " and interface version " + IVehicleHVACManager.version + " are not the same");
}
} catch (final PropertyNotSupported e) {
System.out.println("FT_DRV_ATC_TEMP not supported");
}
} else if (managerName.equals(IVehicleStatusManager.class.getName())) {
this.mVehicleStatusManager = (IVehicleStatusManager) o;
try {
if (IVehicleStatusManager.version.equalsIgnoreCase(this.mVehicleStatusManager.getManagerVersion())) {
System.out.println("Speedometer " + this.mVehicleStatusManager.getSpeedometer());
this.mVehicleStatusManager.registerSpeedometerChange(this);
} else {
System.out.println("Manager version " + this.mVehicleStatusManager.getManagerVersion()
+ " and interface version " + IVehicleStatusManager.version + " are not the same");
}
} catch (final PropertyNotSupported e) {
System.out.println("Speedometer not supported");
}
}
}
@Override
public void onFT_DRV_ATC_TEMPChange(final int value) {
textViewTemp.setText("FT_DRV_ATC_TEMP: " + value);
}
@Override
public void onSpeedometerChange(final float value) {
textViewSpeed.setText("Speedometer: " + value);
}
/**
* Called when the "START TEST" button is pressed.
* @param view
*/
public void startTest(View view) {
final ManagerLoader managerLoader = new ManagerLoader();
managerLoader.getManager(IVehicleHVACManager.class.getName(), this, getApplicationContext());
managerLoader.getManager(IVehicleStatusManager.class.getName(), this, getApplicationContext());
}
}
test_loader.xml
This is the layout file that contains the Button and 2 TextViews used for testing purposes.
< ?xml version="1.0" encoding="utf-8"?>
< LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
tools:context=".TestLoader">
< Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:onClick="startTest"
android:text="Start Test"
android:textSize="30sp" />
< TextView
android:id="@+id/text_view_temp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:text="FT_DRV_ATC_TEMP: "
android:textSize="30sp" />
< TextView
android:id="@+id/text_view_speed"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:text="Speedometer: "
android:textSize="30sp" />
< /LinearLayout>
App build.gradle
The app's build.gradle file uses the compileOnly dependency configuration for the LoaderManagerStub. This tells Gradle to add the dependency to the compile classpath only (that is, it is not added to the build output). This allows you to compile against the stub but the stub methods will not be called at runtime. This works as long as the LoaderManager libraries have been imported as modules. If the stub and implementation jars have simply been added to and referenced from the libs folder, then Android Studio will encounter a "duplicate class" compile error.
dependencies {
...
compileOnly project(":LoaderManagerStub")
implementation project(":LoaderManagersImpl")
implementation project(":LoaderManagerInterfaces")
}
Updated: 02/05/2020, Android is a trademark of Google LLC