Allow to pass custom bundle in Car Nav Manager

Fix: b/63148533

Test: added API test, verified bundle is received in logging renderer
Change-Id: Ieb230c02a89251520fce058f082bc63cc522aad8
diff --git a/car-cluster-logging-renderer/src/android/car/cluster/loggingrenderer/LoggingClusterRenderingService.java b/car-cluster-logging-renderer/src/android/car/cluster/loggingrenderer/LoggingClusterRenderingService.java
index 527da11..b6ed388 100644
--- a/car-cluster-logging-renderer/src/android/car/cluster/loggingrenderer/LoggingClusterRenderingService.java
+++ b/car-cluster-logging-renderer/src/android/car/cluster/loggingrenderer/LoggingClusterRenderingService.java
@@ -19,7 +19,9 @@
 import android.car.cluster.renderer.NavigationRenderer;
 import android.car.navigation.CarNavigationInstrumentCluster;
 import android.graphics.Bitmap;
+import android.os.Bundle;
 import android.util.Log;
+import com.google.android.collect.Lists;
 
 /**
  * Dummy implementation of {@link LoggingClusterRenderingService} to log all interaction.
@@ -36,6 +38,7 @@
                 Log.i(TAG, "getNavigationProperties");
                 CarNavigationInstrumentCluster config =
                         CarNavigationInstrumentCluster.createCluster(1000);
+                config.getExtra().putIntegerArrayList("dummy", Lists.newArrayList(1, 2, 3, 4));
                 Log.i(TAG, "getNavigationProperties, returns: " + config);
                 return config;
             }
@@ -67,6 +70,11 @@
                         + ", displayDistanceMillis: " + displayDistanceMillis
                         + ", displayDistanceUnit: " + displayDistanceUnit);
             }
+
+            @Override
+            public void onEvent(int eventType, Bundle bundle) {
+                Log.i(TAG, "onEvent, eventType: " + eventType + ", bundle: " + bundle);
+            }
         };
 
         Log.i(TAG, "createNavigationRenderer, returns: " + navigationRenderer);
diff --git a/car-lib/src/android/car/cluster/renderer/IInstrumentClusterNavigation.aidl b/car-lib/src/android/car/cluster/renderer/IInstrumentClusterNavigation.aidl
index 00cc135..b1fb7b1 100644
--- a/car-lib/src/android/car/cluster/renderer/IInstrumentClusterNavigation.aidl
+++ b/car-lib/src/android/car/cluster/renderer/IInstrumentClusterNavigation.aidl
@@ -15,8 +15,9 @@
  */
 package android.car.cluster.renderer;
 
-import android.graphics.Bitmap;
 import android.car.navigation.CarNavigationInstrumentCluster;
+import android.graphics.Bitmap;
+import android.os.Bundle;
 
 /**
  * Binder API for Instrument Cluster Navigation.
@@ -31,5 +32,6 @@
         int turnSide);
     void onNextManeuverDistanceChanged(int distanceMeters, int timeSeconds,
         int displayDistanceMillis, int displayDistanceUnit);
+    void onEvent(int eventType, in Bundle bundle);
     CarNavigationInstrumentCluster getInstrumentClusterInfo();
 }
diff --git a/car-lib/src/android/car/cluster/renderer/InstrumentClusterRenderingService.java b/car-lib/src/android/car/cluster/renderer/InstrumentClusterRenderingService.java
index d110158..068b1e5 100644
--- a/car-lib/src/android/car/cluster/renderer/InstrumentClusterRenderingService.java
+++ b/car-lib/src/android/car/cluster/renderer/InstrumentClusterRenderingService.java
@@ -23,6 +23,7 @@
 import android.car.navigation.CarNavigationInstrumentCluster;
 import android.content.Intent;
 import android.graphics.Bitmap;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
@@ -181,6 +182,12 @@
         }
 
         @Override
+        public void onEvent(int eventType, Bundle bundle) throws RemoteException {
+            assertContextOwnership();
+            mNavigationRenderer.onEvent(eventType, bundle);
+        }
+
+        @Override
         public CarNavigationInstrumentCluster getInstrumentClusterInfo() throws RemoteException {
             return mNavigationRenderer.getNavigationProperties();
         }
diff --git a/car-lib/src/android/car/cluster/renderer/NavigationRenderer.java b/car-lib/src/android/car/cluster/renderer/NavigationRenderer.java
index 1719190..0958548 100644
--- a/car-lib/src/android/car/cluster/renderer/NavigationRenderer.java
+++ b/car-lib/src/android/car/cluster/renderer/NavigationRenderer.java
@@ -19,6 +19,7 @@
 import android.annotation.UiThread;
 import android.car.navigation.CarNavigationInstrumentCluster;
 import android.graphics.Bitmap;
+import android.os.Bundle;
 
 /**
  * Contains methods specified for Navigation App renderer in instrument cluster.
@@ -40,4 +41,7 @@
             int turnNumber, Bitmap image, int turnSide);
     abstract public void onNextTurnDistanceChanged(int distanceMeters, int timeSeconds,
             int displayDistanceMillis, int displayDistanceUnit);
+
+    /** @hide */
+    public void onEvent(int eventType, Bundle bundle) {}
 }
diff --git a/car-lib/src/android/car/cluster/renderer/ThreadSafeNavigationRenderer.java b/car-lib/src/android/car/cluster/renderer/ThreadSafeNavigationRenderer.java
index 37e5d36..047ed90 100644
--- a/car-lib/src/android/car/cluster/renderer/ThreadSafeNavigationRenderer.java
+++ b/car-lib/src/android/car/cluster/renderer/ThreadSafeNavigationRenderer.java
@@ -18,6 +18,7 @@
 import android.annotation.Nullable;
 import android.car.navigation.CarNavigationInstrumentCluster;
 import android.graphics.Bitmap;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
@@ -38,6 +39,7 @@
     private final static int MSG_NAV_STOP = 2;
     private final static int MSG_NAV_NEXT_TURN = 3;
     private final static int MSG_NAV_NEXT_TURN_DISTANCE = 4;
+    private final static int MSG_EVENT = 5;
 
     /** Creates thread-safe {@link NavigationRenderer}. Returns null if renderer == null */
     @Nullable
@@ -90,6 +92,11 @@
         mHandler.sendMessage(mHandler.obtainMessage(MSG_NAV_NEXT_TURN_DISTANCE, distance));
     }
 
+    @Override
+    public void onEvent(int eventType, Bundle bundle) {
+        mHandler.sendMessage(mHandler.obtainMessage(MSG_EVENT, eventType, 0, bundle));
+    }
+
     private static class NavigationRendererHandler extends RendererHandler<NavigationRenderer> {
 
         NavigationRendererHandler(Looper looper, NavigationRenderer renderer) {
@@ -115,6 +122,10 @@
                     renderer.onNextTurnDistanceChanged(
                             d.meters, d.seconds, d.displayDistanceMillis, d.displayDistanceUnit);
                     break;
+                case MSG_EVENT:
+                    Bundle bundle = (Bundle) msg.obj;
+                    renderer.onEvent(msg.arg1, bundle);
+                    break;
                 default:
                     throw new IllegalArgumentException("Msg: " + msg.what);
             }
diff --git a/car-lib/src/android/car/navigation/CarNavigationInstrumentCluster.java b/car-lib/src/android/car/navigation/CarNavigationInstrumentCluster.java
index fe48504..cfa05f8 100644
--- a/car-lib/src/android/car/navigation/CarNavigationInstrumentCluster.java
+++ b/car-lib/src/android/car/navigation/CarNavigationInstrumentCluster.java
@@ -17,9 +17,9 @@
 
 import android.annotation.IntDef;
 import android.annotation.SystemApi;
+import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
-
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
@@ -46,13 +46,15 @@
     private int mMinIntervalMillis;
 
     @ClusterType
-    private int mType;
+    private final int mType;
 
-    private int mImageWidth;
+    private final int mImageWidth;
 
-    private int mImageHeight;
+    private final int mImageHeight;
 
-    private int mImageColorDepthBits;
+    private final int mImageColorDepthBits;
+
+    private final Bundle mExtra;
 
     public static final Parcelable.Creator<CarNavigationInstrumentCluster> CREATOR
             = new Parcelable.Creator<CarNavigationInstrumentCluster>() {
@@ -102,6 +104,12 @@
     }
 
     /**
+     * Contains extra information about instrument cluster.
+     * @hide
+     */
+    public Bundle getExtra() { return mExtra; }
+
+    /**
      * If instrument cluster is image, number of bits of colour depth it supports (8, 16, or 32).
      */
     public int getImageColorDepthBits() {
@@ -130,11 +138,12 @@
             int imageWidth,
             int imageHeight,
             int imageColorDepthBits) {
-        this.mMinIntervalMillis = minIntervalMillis;
-        this.mType = type;
-        this.mImageWidth = imageWidth;
-        this.mImageHeight = imageHeight;
-        this.mImageColorDepthBits = imageColorDepthBits;
+        mMinIntervalMillis = minIntervalMillis;
+        mType = type;
+        mImageWidth = imageWidth;
+        mImageHeight = imageHeight;
+        mImageColorDepthBits = imageColorDepthBits;
+        mExtra = new Bundle();
     }
 
     @Override
@@ -149,6 +158,7 @@
         dest.writeInt(mImageWidth);
         dest.writeInt(mImageHeight);
         dest.writeInt(mImageColorDepthBits);
+        dest.writeBundle(mExtra);
     }
 
     private CarNavigationInstrumentCluster(Parcel in) {
@@ -157,6 +167,7 @@
         mImageWidth = in.readInt();
         mImageHeight = in.readInt();
         mImageColorDepthBits = in.readInt();
+        mExtra = in.readBundle(getClass().getClassLoader());
     }
 
     /** Converts to string for debug purpose */
@@ -167,6 +178,7 @@
                 "type: " + mType + ", " +
                 "imageWidth: " + mImageWidth + ", " +
                 "imageHeight: " + mImageHeight + ", " +
-                "imageColourDepthBits: " + mImageColorDepthBits + " }";
+                "imageColourDepthBits: " + mImageColorDepthBits +
+                "extra: " + mExtra + " }";
     }
 }
diff --git a/car-lib/src/android/car/navigation/CarNavigationStatusManager.java b/car-lib/src/android/car/navigation/CarNavigationStatusManager.java
index f24ff30..9a83219 100644
--- a/car-lib/src/android/car/navigation/CarNavigationStatusManager.java
+++ b/car-lib/src/android/car/navigation/CarNavigationStatusManager.java
@@ -22,10 +22,10 @@
 import android.car.CarNotConnectedException;
 import android.car.cluster.renderer.IInstrumentClusterNavigation;
 import android.graphics.Bitmap;
+import android.os.Bundle;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Log;
-
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
@@ -114,6 +114,13 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface TurnEvent {}
 
+    /** Event type that holds information about next maneuver. */
+    public static final int EVENT_TYPE_NEXT_MANEUVER_INFO = 1;
+    /** Event type that holds information regarding distance/time to the next maneuver. */
+    public static final int EVENT_TYPE_NEXT_MANEUVER_COUNTDOWN = 2;
+    /** All custom (vendor-specific) event types should be equal or greater than this constant. */
+    public static final int EVENT_TYPE_VENDOR_FIRST = 1024;
+
     /* Turn Side */
     /** Turn is on the left side of the vehicle. */
     public static final int TURN_SIDE_LEFT = 1;
@@ -198,7 +205,7 @@
      * drives on the left-hand side of the road, such as Australia; anti-clockwise for roads where
      * the car drives on the right, such as the USA).
      *
-     * @param event event type ({@link #TURN_TURN}, {@link #TURN_U_TURN},
+     * @param turnEvent turn event like ({@link #TURN_TURN}, {@link #TURN_U_TURN},
      *        {@link #TURN_ROUNDABOUT_ENTER_AND_EXIT}, etc).
      * @param eventName Name of the turn event like road name to turn. For example "Charleston road"
      *        in "Turn right to Charleston road"
@@ -212,6 +219,7 @@
      *        {@link #TURN_SIDE_UNSPECIFIED}).
      * @throws CarNotConnectedException if the connection to the car service has been lost.
      *
+     * @deprecated Use {@link #sendEvent(int, Bundle)} instead.
      */
     public void sendNavigationTurnEvent(@TurnEvent int turnEvent, CharSequence eventName,
             int turnAngle, int turnNumber, Bitmap image, @TurnSide int turnSide)
@@ -238,6 +246,8 @@
      * @param displayDistanceUnit units for {@param displayDistanceMillis} param.
      * See {@link DistanceUnit} for acceptable values.
      * @throws CarNotConnectedException if the connection to the car service has been lost.
+     *
+     * @deprecated Use {@link #sendEvent(int, Bundle)} instead.
      */
     public void sendNavigationTurnDistanceEvent(int distanceMeters, int timeSeconds,
             int displayDistanceMillis, @DistanceUnit int displayDistanceUnit)
@@ -252,6 +262,28 @@
         }
     }
 
+    /**
+     * Sends events from navigation app to instrument cluster.
+     *
+     * @param eventType event type
+     * @param bundle object that holds data about the event
+     * @throws CarNotConnectedException if the connection to the car service has been lost.
+     *
+     * @see #EVENT_TYPE_NEXT_MANEUVER_INFO
+     * @see #EVENT_TYPE_NEXT_MANEUVER_COUNTDOWN
+     *
+     * @hide
+     */
+    public void sendEvent(int eventType, Bundle bundle) throws CarNotConnectedException {
+        try {
+            mService.onEvent(eventType, bundle);
+        } catch (IllegalStateException e) {
+            CarApiUtil.checkCarNotConnectedExceptionFromCarService(e);
+        } catch (RemoteException e) {
+            handleCarServiceRemoteExceptionAndThrow(e);
+        }
+    }
+
     @Override
     public void onCarDisconnected() {
         Log.d(TAG, "onCarDisconnected");
diff --git a/tests/android_car_api_test/src/android/car/apitest/CarNavigationManagerTest.java b/tests/android_car_api_test/src/android/car/apitest/CarNavigationManagerTest.java
index 953a4a0..31543bd 100644
--- a/tests/android_car_api_test/src/android/car/apitest/CarNavigationManagerTest.java
+++ b/tests/android_car_api_test/src/android/car/apitest/CarNavigationManagerTest.java
@@ -19,8 +19,10 @@
 import android.car.CarAppFocusManager;
 import android.car.CarAppFocusManager.OnAppFocusOwnershipCallback;
 import android.car.navigation.CarNavigationStatusManager;
+import android.os.Bundle;
 import android.test.suitebuilder.annotation.MediumTest;
 import android.util.Log;
+import com.google.android.collect.Lists;
 
 /**
  * Unit tests for {@link CarNavigationStatusManager}
@@ -79,8 +81,19 @@
         assertTrue(mCarAppFocusManager.isOwningFocus(ownershipCallback,
                 CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION));
 
+        Log.i(TAG, "Instrument cluster: " + mCarNavigationManager.getInstrumentClusterInfo());
+
         // TODO: we should use mocked HAL to be able to verify this, right now just make sure that
         // it is not crashing and logcat has appropriate traces.
         mCarNavigationManager.sendNavigationStatus(1);
+
+        Bundle bundle = new Bundle();
+        bundle.putInt("BUNDLE_INTEGER_VALUE", 1234);
+        bundle.putFloat("BUNDLE_FLOAT_VALUE", 12.3456f);
+        bundle.putStringArrayList("BUNDLE_ARRAY_OF_STRINGS",
+                Lists.newArrayList("Value A", "Value B", "Value Z"));
+
+        mCarNavigationManager.sendEvent
+                (CarNavigationStatusManager.EVENT_TYPE_NEXT_MANEUVER_INFO, bundle);
     }
 }